├── LICENSE
├── README.md
└── duoshuo
├── Abstract.php
├── Client.php
├── Exception.php
├── LocalServer.php
├── WordPress.php
├── api.php
├── comments-seo.php
├── comments.php
├── common-script.html
├── compat-json.php
├── config.php
├── duoshuo.php
├── images
├── head-icon.gif
├── icon.gif
├── menu-icon.png
├── service_icons_32x32.png
└── waiting.gif
├── index.php
├── manage.php
├── nanoSha2.php
├── oauth-proxy.php
├── preferences.php
├── profile.php
├── readme.txt
├── screenshot-1.png
├── screenshot-2.png
├── screenshot-3.png
├── screenshot-4.png
├── screenshot-5.png
├── screenshot-6.jpg
├── settings.php
├── statistics.php
├── styles.css
├── sync.php
├── themes.php
└── widgets.php
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2010 DISQUS
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | duoshuo-wordpress
2 | =================
3 |
4 | 多说评论插件 for WordPress
5 |
6 | 在这里你可以看到多说插件的最新开发进展,如果你想下载稳定版,请访问WordPress的官方网站:
7 | http://wordpress.org/extend/plugins/duoshuo/
8 |
--------------------------------------------------------------------------------
/duoshuo/Abstract.php:
--------------------------------------------------------------------------------
1 | shortName, $this->secret);
26 | }
27 |
28 | public function syncLog(){
29 | $this->updateOption('sync_lock', time());
30 |
31 | $last_log_id = $this->getOption('last_log_id');
32 | if (!$last_log_id)
33 | $last_log_id = 0;
34 |
35 | $limit = 20;
36 |
37 | $params = array(
38 | 'limit' => $limit,
39 | 'order' => 'asc',
40 | );
41 |
42 | $client = $this->getClient();
43 |
44 | $posts = array();
45 | $affectedThreads = array();
46 |
47 | //do{
48 |
49 | $params['since_id'] = $last_log_id;
50 | $response = $client->request('GET', 'log/list', $params);
51 |
52 | if (is_string($response))
53 | throw new Duoshuo_Exception($response, Duoshuo_Exception::INTERNAL_SERVER_ERROR);
54 |
55 | if (!isset($response['response']))
56 | throw new Duoshuo_Exception($response['message'], $response['code']);
57 |
58 | foreach($response['response'] as $log){
59 | switch($log['action']){
60 | case 'create':
61 | $affected = $this->createPost($log['meta']);
62 | break;
63 | case 'approve':
64 | $affected = $this->approvePost($log['meta']);
65 | break;
66 | case 'spam':
67 | $affected = $this->spamPost($log['meta']);
68 | break;
69 | case 'delete':
70 | $affected = $this->deletePost($log['meta']);
71 | break;
72 | case 'delete-forever':
73 | $affected = $this->deleteForeverPost($log['meta']);
74 | break;
75 | case 'update'://现在并没有update操作的逻辑
76 | default:
77 | $affected = array();
78 | }
79 |
80 | //合并
81 | if (is_array($affected))
82 | $affectedThreads = array_merge($affectedThreads, $affected);
83 |
84 | if (strlen($log['log_id']) > strlen($last_log_id) || strcmp($log['log_id'], $last_log_id) > 0)
85 | $last_log_id = $log['log_id'];
86 | }
87 |
88 | $this->updateOption('last_log_id', $last_log_id);
89 |
90 | //} while (count($response['response']) == $limit);//如果返回和最大请求条数一致,则再取一次
91 |
92 | $this->updateOption('sync_lock', 0);
93 |
94 | //更新静态文件
95 | if ($this->getOption('sync_to_local') && $this->plugin->getOption('seo_enabled'))
96 | $this->refreshThreads(array_unique($affectedThreads));
97 |
98 | return count($response['response']);
99 | }
100 |
101 | function rfc3339_to_mysql($string){
102 | if (method_exists('DateTime', 'createFromFormat')){ // php 5.3.0
103 | return DateTime::createFromFormat(DateTime::RFC3339, $string)->format('Y-m-d H:i:s');
104 | }
105 | else{
106 | $timestamp = strtotime($string);
107 | return gmdate('Y-m-d H:i:s', $timestamp + $this->timezone() * 3600);
108 | }
109 | }
110 |
111 | function rfc3339_to_mysql_gmt($string){
112 | if (method_exists('DateTime', 'createFromFormat')){ // php 5.3.0
113 | return DateTime::createFromFormat(DateTime::RFC3339, $string)->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d H:i:s');
114 | }
115 | else{
116 | $timestamp = strtotime($string);
117 | return gmdate('Y-m-d H:i:s', $timestamp);
118 | }
119 | }
120 |
121 | static function encodeJWT($payload, $key){
122 | $header = array('typ' => 'JWT', 'alg' => 'HS256');
123 |
124 | $segments = array(
125 | str_replace('=', '', strtr(base64_encode(json_encode($header)), '+/', '-_')),
126 | str_replace('=', '', strtr(base64_encode(json_encode($payload)), '+/', '-_')),
127 | );
128 | $signing_input = implode('.', $segments);
129 |
130 | $signature = self::hmacsha256($signing_input, $key);
131 |
132 | $segments[] = str_replace('=', '', strtr(base64_encode($signature), '+/', '-_'));
133 |
134 | return implode('.', $segments);
135 | }
136 |
137 | // from: http://www.php.net/manual/en/function.sha1.php#39492
138 | // Calculate HMAC-SHA1 according to RFC2104
139 | // http://www.ietf.org/rfc/rfc2104.txt
140 | static function hmacsha1($data, $key) {
141 | if (function_exists('hash_hmac'))
142 | return hash_hmac('sha1', $data, $key, true);
143 |
144 | $blocksize=64;
145 | if (strlen($key)>$blocksize)
146 | $key=pack('H*', sha1($key));
147 | $key=str_pad($key,$blocksize,chr(0x00));
148 | $ipad=str_repeat(chr(0x36),$blocksize);
149 | $opad=str_repeat(chr(0x5c),$blocksize);
150 | $hmac = pack(
151 | 'H*',sha1(
152 | ($key^$opad).pack(
153 | 'H*',sha1(
154 | ($key^$ipad).$data
155 | )
156 | )
157 | )
158 | );
159 | return $hmac;
160 | }
161 |
162 | /**
163 | * from: http://www.php.net/manual/en/function.sha1.php#39492
164 | * Calculate HMAC-SHA1 according to RFC2104
165 | * http://www.ietf.org/rfc/rfc2104.txt
166 | * Used in OAuth1 and remoteAuth
167 | */
168 | static function hmacsha256($data, $key) {
169 | if (function_exists('hash_hmac'))
170 | return hash_hmac('sha256', $data, $key, true);
171 |
172 | if (!class_exists('nanoSha2', false))
173 | require 'nanoSha2.php';
174 |
175 | $nanoSha2 = new nanoSha2();
176 |
177 | $blocksize=64;
178 | if (strlen($key)>$blocksize)
179 | $key=pack('H*', $nanoSha2->hash($key, true));
180 | $key=str_pad($key,$blocksize,chr(0x00));
181 | $ipad=str_repeat(chr(0x36),$blocksize);
182 | $opad=str_repeat(chr(0x5c),$blocksize);
183 | $hmac = pack(
184 | 'H*',$nanoSha2->hash(
185 | ($key^$opad).pack(
186 | 'H*', $nanoSha2->hash(($key^$ipad).$data, true)
187 | ),
188 | true
189 | )
190 | );
191 | return $hmac;
192 | }
193 |
194 | function exportUsers($users){
195 | if (count($users) === 0)
196 | return 0;
197 |
198 | $params = array('users'=>array());
199 | foreach($users as $user)
200 | $params['users'][] = $this->packageUser($user);
201 |
202 | $remoteResponse = $this->getClient()->request('POST', 'users/import', $params);
203 |
204 | // @deprecated 不再需要记录duoshuo_user_id
205 | if (is_array($remoteResponse) && isset($remoteResponse['response'])){
206 | foreach($remoteResponse['response'] as $userId => $duoshuoUserId)
207 | $this->updateUserMeta($userId, 'duoshuo_user_id', $duoshuoUserId);
208 | }
209 |
210 | return count($users);
211 | }
212 |
213 | function exportPosts($threads){
214 | if (count($threads) === 0)
215 | return 0;
216 |
217 | $params = array(
218 | 'threads' => array(),
219 | );
220 | foreach($threads as $index => $thread){
221 | $params['threads'][] = $this->packageThread($thread);
222 | }
223 |
224 | $remoteResponse = $this->getClient()->request('POST','threads/import', $params);
225 |
226 | if (is_array($remoteResponse) && isset($remoteResponse['response'])){
227 | foreach($remoteResponse['response'] as $threadId => $duoshuoThreadId)
228 | $this->updateThreadMeta($threadId, 'duoshuo_thread_id', $duoshuoThreadId);
229 | }
230 |
231 | return count($threads);
232 | }
233 |
234 | function exportComments($comments){
235 | if (count($comments) === 0)
236 | return 0;
237 |
238 | $params = array(
239 | 'posts' => array()
240 | );
241 |
242 | foreach($comments as $comment)
243 | $params['posts'][] = $this->packageComment($comment);
244 |
245 | $remoteResponse = $this->getClient()->request('POST', 'posts/import', $params);
246 |
247 | return count($comments);
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/duoshuo/Client.php:
--------------------------------------------------------------------------------
1 | shortName = $shortName;
23 | $this->secret = $secret;
24 | $this->jwt = $jwt;
25 | $this->accessToken = $accessToken;
26 | $this->http = new WP_Http();
27 | $this->userAgent = 'WordPress/' . $wp_version . '|Duoshuo/'. Duoshuo_WordPress::VERSION;
28 | }
29 |
30 | /**
31 | *
32 | * @param $method
33 | * @param $path
34 | * @param $params
35 | * @throws Duoshuo_Exception
36 | * @return array
37 | */
38 | public function request($method, $path, $params = array()){
39 | $params['short_name'] = $this->shortName;
40 | $params['secret'] = $this->secret;
41 |
42 | if ($this->jwt)
43 | $params['jwt'] = $this->jwt;
44 |
45 | if ($this->accessToken)
46 | $params['access_token'] = $this->accessToken;
47 |
48 | $url = $this->end_point . $path. '.' . $this->format;
49 |
50 | return $this->httpRequest($url, $method, $params);
51 | }
52 |
53 | public function httpRequest($url, $method, $params){
54 | $args = array(
55 | 'method' => $method,
56 | 'timeout' => 60,
57 | 'redirection' => 5,
58 | 'httpversion' => '1.0',
59 | 'user-agent' => $this->userAgent,
60 | //'blocking' => true,
61 | 'headers' => array('Expect'=>''),
62 | //'cookies' => array(),
63 | //'compress' => false,
64 | //'decompress' => true,
65 | 'sslverify' => false,
66 | //'stream' => false,
67 | //'filename' => null
68 | );
69 |
70 | switch($method){
71 | case 'GET':
72 | $url .= '?' . http_build_query($params, null, '&'); // overwrite arg_separator.output
73 | break;
74 | case 'POST':
75 | $args['body'] = $params; // http类自己会做 http_build_query($params, null, '&') 并指定Content-Type
76 | break;
77 | default:
78 | }
79 |
80 | $response = $this->http->request($url, $args);
81 |
82 | if (isset($response->errors)){
83 | if (isset($response->errors['http_request_failed'])){
84 | $message = $response->errors['http_request_failed'][0];
85 | if ($message == 'name lookup timed out')
86 | $message = 'DNS解析超时,请重试或检查你的主机的域名解析(DNS)设置。';
87 | elseif (stripos($message, 'Could not open handle for fopen') === 0)
88 | $message = '无法打开fopen句柄,请重试或联系多说管理员。http://dev.duoshuo.com/';
89 | elseif (stripos($message, 'Couldn\'t resolve host') === 0)
90 | $message = '无法解析duoshuo.com域名,请重试或检查你的主机的域名解析(DNS)设置。';
91 | elseif (stripos($message, 'Operation timed out after ') === 0)
92 | $message = '操作超时,请重试或联系多说管理员。http://dev.duoshuo.com/';
93 | throw new Duoshuo_Exception($message, Duoshuo_Exception::REQUEST_TIMED_OUT);
94 | }
95 | else
96 | throw new Duoshuo_Exception('连接服务器失败, 详细信息:' . json_encode($response->errors), Duoshuo_Exception::REQUEST_TIMED_OUT);
97 | }
98 |
99 | $json = json_decode($response['body'], true);
100 | return $json === null ? $response['body'] : $json;
101 | }
102 |
103 | /**
104 | *
105 | * @param string $type
106 | * @param array $keys
107 | */
108 | public function getAccessToken( $type, $keys ) {
109 | $params = array(
110 | 'client_id' => $this->shortName,
111 | 'client_secret' => $this->secret,
112 | );
113 |
114 | switch($type){
115 | case 'token':
116 | $params['grant_type'] = 'refresh_token';
117 | $params['refresh_token'] = $keys['refresh_token'];
118 | break;
119 | case 'code':
120 | $params['grant_type'] = 'authorization_code';
121 | $params['code'] = $keys['code'];
122 | $params['redirect_uri'] = $keys['redirect_uri'];
123 | break;
124 | case 'password':
125 | $params['grant_type'] = 'password';
126 | $params['username'] = $keys['username'];
127 | $params['password'] = $keys['password'];
128 | break;
129 | default:
130 | throw new Duoshuo_Exception("wrong auth type");
131 | }
132 |
133 | $accessTokenUrl = $this->end_point . 'oauth2/access_token';
134 | $response = $this->httpRequest($accessTokenUrl, 'POST', $params);
135 |
136 | $token = $response;
137 | if ( is_array($token) && !isset($token['error']) ) {
138 | $this->access_token = $token['access_token'];
139 | if (isset($token['refresh_token'])) // 可能没有refresh_token
140 | $this->refresh_token = $token['refresh_token'];
141 | } else {
142 | throw new Duoshuo_Exception("get access token failed." . $token['error']);
143 | }
144 |
145 | return $token;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/duoshuo/Exception.php:
--------------------------------------------------------------------------------
1 | plugin = $plugin;
15 | }
16 |
17 | /**
18 | * 从服务器pull评论到本地
19 | *
20 | * @param array $input
21 | */
22 | public function sync_log($input = array()){
23 | $syncLock = $this->plugin->getOption('sync_lock');//检查是否正在同步评论 同步完成后该值会置0
24 | if($syncLock && $syncLock > time()- 300){//正在或5分钟内发生过写回但没置0
25 | $this->response = array(
26 | 'code' => Duoshuo_Exception::SUCCESS,
27 | 'response'=> '同步中,请稍候',
28 | );
29 | return;
30 | }
31 |
32 | try{
33 | $this->response['affected'] = $this->plugin->syncLog();
34 | $this->response['last_log_id'] = $this->plugin->getOption('last_log_id');
35 | }
36 | catch(Exception $ex){
37 | //$this->plugin->updateOption('sync_lock', $ex->getLine());
38 | }
39 |
40 | $this->response['code'] = Duoshuo_Exception::SUCCESS;
41 | }
42 |
43 | public function update_option($input = array()){
44 | //duoshuo_short_name
45 | //duoshuo_secret
46 | //duoshuo_notice
47 | foreach($input as $optionName => $optionValue)
48 | if (substr($optionName, 0, 8) === 'duoshuo_'){
49 | update_option($_POST['option'], $_POST['value']);
50 | }
51 | $this->response['code'] = 0;
52 | }
53 |
54 | public function dispatch($input){
55 | if (!isset($input['signature']))
56 | throw new Duoshuo_Exception('Invalid signature.', Duoshuo_Exception::INVALID_SIGNATURE);
57 |
58 | $signature = $input['signature'];
59 | unset($input['signature']);
60 |
61 | ksort($input);
62 | $baseString = http_build_query($input, null, '&');
63 |
64 | $expectSignature = base64_encode(Duoshuo_Abstract::hmacsha1($baseString, $this->plugin->getOption('secret')));
65 | if ($signature !== $expectSignature)
66 | throw new Duoshuo_Exception('Invalid signature, expect: ' . $expectSignature . '. (' . $baseString . ')', Duoshuo_Exception::INVALID_SIGNATURE);
67 |
68 | $method = $input['action'];
69 |
70 | if (!method_exists($this, $method))
71 | throw new Duoshuo_Exception('Unknown action.', Duoshuo_Exception::OPERATION_NOT_SUPPORTED);
72 |
73 | $this->response = array();
74 | $this->$method($input);
75 | $this->sendResponse();
76 | }
77 |
78 | public function sendResponse(){
79 | echo json_encode($this->response);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/duoshuo/WordPress.php:
--------------------------------------------------------------------------------
1 | 0,
30 | 'duoshuo_api_hostname' => 'api.duoshuo.com',
31 | 'duoshuo_cron_sync_enabled' => 1,
32 | 'duoshuo_seo_enabled' => 1,
33 | 'duoshuo_postpone_print_scripts'=> 0,
34 | 'duoshuo_cc_fix' => 1,
35 | 'duoshuo_sync_pingback_and_trackback'=> 0,
36 | 'duoshuo_social_login_enabled' => 1,
37 | 'duoshuo_comments_wrapper_intro'=> '',
38 | 'duoshuo_comments_wrapper_outro'=> '',
39 | 'duoshuo_last_log_id' => 0,
40 | 'duoshuo_theme' => 'default',
41 | 'duoshuo_style_patch' => 1,
42 | );
43 |
44 | protected function __construct(){
45 | $this->shortName = $this->getOption('short_name');
46 | $this->secret = $this->getOption('secret');
47 |
48 | foreach (self::$_defaultOptions as $optionName => $value)
49 | if (get_option($optionName) === false)
50 | update_option($optionName, $value);
51 |
52 | $this->pluginDirUrl = plugin_dir_url(__FILE__);
53 | }
54 |
55 | /**
56 | *
57 | * @return Duoshuo_WordPress
58 | */
59 | public static function getInstance(){
60 | if (self::$_instance === null)
61 | self::$_instance = new self();
62 | return self::$_instance;
63 | }
64 |
65 | public function timezone(){
66 | return get_option('gmt_offset');
67 | }
68 |
69 | public function getOption($key){
70 | return get_option('duoshuo_' . $key);
71 | }
72 |
73 | public function updateOption($key, $value){
74 | return update_option('duoshuo_' . $key, $value);
75 | }
76 |
77 | public function deleteOption($key){
78 | return delete_option('duoshuo_' . $key);
79 | }
80 |
81 | public function updateUserMeta($userId, $metaKey, $metaValue){
82 | return function_exists('update_user_meta')
83 | ? update_user_meta($userId, $metaKey, $metaValue)
84 | : update_usermeta($userId, $metaKey, $metaValue);
85 | }
86 |
87 | public function getUserMeta($userId, $metaKey, $single = false){
88 | //get_user_meta 从3.0开始有效: get_usermeta($user->ID, $blog_prefix.'capabilities', true);
89 | return function_exists('get_user_meta')
90 | ? get_user_meta($userId, $metaKey, true)
91 | : get_usermeta($userId, $metaKey);
92 | }
93 |
94 | public function updateThreadMeta($threadId, $metaKey, $metaValue){
95 | return update_post_meta($threadId, $metaKey, $metaValue);
96 | }
97 |
98 | public function threadKey($post){
99 | return $post->post_status == 'inherit'
100 | ? ($post->post_parent ? $post->post_parent : null)
101 | : $post->ID;
102 | }
103 |
104 | public function topPost($post){
105 | return $post->post_status == 'inherit'
106 | ? ($post->post_parent ? get_post($post->post_parent) : null)
107 | : $post;
108 | }
109 |
110 | public function get_blog_prefix(){
111 | global $wpdb;
112 | return method_exists($wpdb,'get_blog_prefix')
113 | ? $wpdb->get_blog_prefix()
114 | : $wpdb->prefix;
115 | }
116 |
117 | public function connected(){
118 | $connected_failed = get_option('duoshuo_connect_failed');
119 | return $connected_failed
120 | ? (time() - $connected_failed > 1800)
121 | : true;
122 | }
123 |
124 | public function connectFailed(){
125 | update_option('duoshuo_connect_failed', time());
126 | }
127 |
128 | public function normalizeUrl($url){
129 | if (strpos($url, '/') === 0){
130 | $siteurl = get_option('siteurl');
131 |
132 | if (strlen($siteurl) >=8 && (false !== $pos = strpos($siteurl, '/', 8)))
133 | $siteurl = substr($siteurl, 0, $pos);
134 |
135 | return $siteurl . $url;
136 | }
137 | else{
138 | return $url;
139 | }
140 | }
141 |
142 | public function allowedHtml($tags, $context = ''){
143 | if (!isset($tags['img']))
144 | $tags['img'] = array();
145 |
146 | $tags['img']['src'] = true;
147 | return $tags;
148 | }
149 |
150 | public function setJwtCookie($logged_in_cookie, $expire, $expiration, $user_id, $scheme){
151 | $jwt = $this->jwt($user_id);
152 | $secure = $scheme == 'secure_auth';
153 | $secure_logged_in_cookie = apply_filters('secure_logged_in_cookie', false, $user_id, $secure);
154 |
155 | // $httponly = false
156 | //setcookie('duoshuo_token', $jwt, $expire, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure, true);
157 | setcookie('duoshuo_token', $jwt, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure);
158 | setcookie('duoshuo_token', $jwt, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie);
159 | if ( COOKIEPATH != SITECOOKIEPATH )
160 | setcookie('duoshuo_token', $jwt, $expire, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie);
161 |
162 | $this->syncUserToRemote($user_id);
163 | }
164 |
165 | public function clearJwtCookie(){
166 | // 3.5版本之前没有定义 YEAR_IN_SECONDS
167 | //setcookie( 'duoshuo_token', ' ', time() - 31536000, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN );
168 | setcookie( 'duoshuo_token', ' ', time() - 31536000, ADMIN_COOKIE_PATH, COOKIE_DOMAIN );
169 | setcookie( 'duoshuo_token', ' ', time() - 31536000, COOKIEPATH, COOKIE_DOMAIN );
170 |
171 | if ( COOKIEPATH != SITECOOKIEPATH )
172 | setcookie( 'duoshuo_token', ' ', time() - 31536000, SITECOOKIEPATH, COOKIE_DOMAIN );
173 | }
174 |
175 | public function oauthConnect(){
176 | if (!$this->connected())
177 | return ;
178 |
179 | if (!isset($_GET['code']))
180 | return false;
181 |
182 | try{
183 | $keys = array(
184 | 'code' => $_GET['code'],
185 | 'redirect_uri' => (is_ssl()?'https':'http').'://duoshuo.com/login-callback/',
186 | );
187 |
188 | $token = $this->getClient()->getAccessToken('code', $keys);
189 |
190 | if ($token['code'] != 0)
191 | return false;
192 |
193 | $this->userLogin($token);
194 | }
195 | catch(Duoshuo_Exception $e){
196 | $this->connectFailed();
197 | }
198 | }
199 |
200 | public function userLogin($token){
201 | global $wpdb, $error;
202 |
203 | nocache_headers();
204 | if (isset($token['user_key'])){//登陆成功
205 | $user = get_user_by('id', $token['user_key']);
206 |
207 | $this->updateUserMeta($user->ID, 'duoshuo_access_token', $token['access_token']);
208 |
209 | wp_clear_auth_cookie();
210 | wp_set_auth_cookie($user->ID, true, is_ssl());
211 | wp_set_current_user($user->ID);
212 |
213 | if (isset($_GET['redirect_to'])){
214 | // wordpress 采用的是redirect_to字段
215 | wp_redirect($_GET['redirect_to']);
216 | exit;
217 | }
218 | }
219 | else{
220 | if (isset($_GET['redirect_to']) && $_GET['redirect_to'] !== admin_url()){// 如果是在内容页登录,无论如何都把用户带回内容页
221 | wp_redirect($_GET['redirect_to']);
222 | exit;
223 | }
224 | else{
225 | $client = new Duoshuo_Client($this->shortName, $this->secret, null, $token['access_token']);
226 | $response = $client->request('GET', 'users/profile', array('user_id'=> $token['user_id']));
227 |
228 | if (get_option('users_can_register')){// 如果站点开启注册
229 | // 要求用户输入帐号进行绑定
230 | $query = array(
231 | 'action' => 'register',
232 | //'duoshuo_user_id' => $token['user_id'],
233 | 'duoshuo_access_token'=>$token['access_token'],
234 | );
235 |
236 | $this->duoshuoUserId = $token['user_id'];
237 |
238 | $registerUrl = site_url( 'wp-login.php?' . http_build_query($query), 'login' );
239 | $error = ' '
240 | . '' . esc_html($response['response']['name']) . ' ,欢迎! '
241 | . '请绑定已注册的本站帐号; '
242 | . '如果你还没有本站帐号,请注册 ';
243 | }
244 | else{// 如果站点未开启注册
245 | //如果是从wp-login页面发起的请求,就不触发重定向
246 | $error = ' '
247 | . '' . esc_html($response['response']['name']) . ' ,欢迎! '
248 | . '你还没有和本站的用户帐号绑定, '
249 | . '如果你已经注册,请先登录之后绑定社交帐号';
250 | }
251 | }
252 | }
253 | }
254 |
255 | public function bindUser($user_login, $user = null){
256 | // 早期版本wp_login接口只有第一个参数
257 | if (empty($user)){
258 | $user = get_user_by('login', $user_login);
259 | }
260 |
261 | if (isset($_POST['duoshuo_user_id'])){
262 | $query = array(
263 | 'master_user_id'=> $_POST['duoshuo_user_id'],
264 | 'jwt' => $this->jwt($user->ID),
265 | 'redirect_uri' => admin_url(),
266 | );
267 |
268 | wp_redirect((is_ssl()?'https':'http').'://duoshuo.com/merge/?'. http_build_query($query, null, '&'));
269 | exit;
270 | }
271 | }
272 |
273 | public function userRegisterHook($userId){
274 | if (!$this->connected())
275 | return ;
276 |
277 | // WP_User
278 | $user = get_user_by('id', $userId);
279 | try{
280 | if (isset($_POST['user_login']) && isset($_POST['duoshuo_access_token'])){
281 | // 已登录多说帐号,且多说帐号并未与本站user_key绑定
282 | $client = new Duoshuo_Client($this->shortName, $this->secret, null, $_POST['duoshuo_access_token']);
283 |
284 | $params = array(
285 | 'user' => $this->packageUser($user),
286 | );
287 | $remoteResponse = $client->request('POST', 'sites/join', $params);
288 | // 不再需要记录duoshuo_user_id
289 | }
290 | else{
291 | // 未登录多说帐号
292 | $this->exportUsers(array($user));
293 | }
294 | }
295 | catch(Duoshuo_Exception $e){
296 | $this->connectFailed();
297 | }
298 | }
299 |
300 | public function originalCommentsNotice(){
301 | echo '
'
302 | . '
多说正在努力地为您的网站提供强大的社会化评论服务,WordPress原生评论数据现在仅用于备份;
'
303 | . '
多说会将每一条评论实时写回本地数据库,您在多说删除/审核了评论,也同样会同步到本地数据;
'
304 | . '
您在本页做的任何管理评论操作,都不会对多说评论框上的评论起作用,请访问评论管理后台 进行评论管理。
'
305 | . '
';
306 | }
307 |
308 | /**
309 | *
310 | * @return Duoshuo_Client
311 | */
312 | public function getClient($userId = 0){ //如果不输入参数,就是游客
313 | if ($userId !== null){
314 | $accessToken = $this->getUserMeta($userId, 'duoshuo_access_token');
315 |
316 | if (is_string($accessToken))
317 | $client = new Duoshuo_Client($this->shortName, $this->secret, $this->jwt($userId), $accessToken);
318 | }
319 | if (!isset($client))
320 | $client = new Duoshuo_Client($this->shortName, $this->secret, $this->jwt($userId));
321 |
322 | $apiHostname = $this->getOption('api_hostname');
323 | if ($apiHostname)
324 | $client->end_point = 'http://' . $apiHostname . '/';
325 |
326 | return $client;
327 | }
328 |
329 | public function config(){
330 | /*if ($_SERVER['REQUEST_METHOD'] == 'POST' && !($this->shortName && $this->secret)){
331 | self::registerSite();
332 | }*/
333 | include dirname(__FILE__) . '/config.php';
334 | }
335 |
336 | public function sync(){
337 | include dirname(__FILE__) . '/sync.php';
338 | }
339 |
340 | public function manage(){
341 | include dirname(__FILE__) . '/manage.php';
342 | }
343 |
344 | public function themes(){
345 | include dirname(__FILE__) . '/themes.php';
346 | }
347 |
348 | public function preferences(){
349 | include dirname(__FILE__) . '/preferences.php';
350 | }
351 |
352 | public function checkDependency($name){
353 | global $wp_version;
354 |
355 | switch ($name){
356 | case 'php':
357 | return array(PHP_VERSION, version_compare(PHP_VERSION, '5.3.0', '<') ? '建议升级php到5.3或以上' : true);
358 | case 'wordpress':
359 | return array($wp_version, version_compare($wp_version, '3.3.0', '<') ? '建议升级WordPress到3.3或以上' : true);
360 | case 'json':
361 | if (!extension_loaded('json'))
362 | return array('缺少json扩展', '安装并启用JSON扩展');
363 | return array(true, true);
364 | case 'curl':
365 | if (!extension_loaded('curl'))
366 | return array('缺少curl扩展', '安装并启用curl扩展');
367 | if (!function_exists('curl_init'))
368 | return array('curl_init()被禁用', '启用curl_init()函数');
369 | if (!function_exists('curl_exec'))
370 | return array('curl_exec()被禁用', '启用curl_exec()函数');
371 | return array(true, true);
372 | case 'fopen':
373 | if (!function_exists('fopen'))
374 | return array('fopen()被禁用', '启用fopen()函数');
375 | if (!function_exists('ini_get'))
376 | return array('ini_get()被禁用', '启用ini_get()函数');
377 | if (!ini_get('allow_url_fopen'))
378 | return array('allow_url_fopen被关闭', '在php.ini中设置allow_url_fopen = On');
379 | return array(true, true);
380 | case 'fsockopen':
381 | if (!function_exists('fsockopen'))
382 | return array('fsockopen()被禁用', '启用fsockopen()函数');
383 | return array(true, true);
384 | case 'hash_hmac':
385 | if (!function_exists('hash_hmac'))
386 | return array('hash_hmac()不存在', '升级php到5.3或以上');
387 | return array(true, true);
388 | default:
389 | return array(true, true);
390 | }
391 | }
392 |
393 | public function settings(){
394 | include dirname(__FILE__) . '/settings.php';
395 | }
396 |
397 | public function statistics(){
398 | include dirname(__FILE__) . '/statistics.php';
399 | }
400 |
401 | public function profile(){
402 | include dirname(__FILE__) . '/profile.php';
403 | }
404 |
405 | public function reset(){
406 | global $wpdb;
407 | //delete_option('duoshuo_short_name');
408 |
409 | // 删除状态有关的值
410 | delete_option('duoshuo_secret');
411 | delete_option('duoshuo_synchronized');
412 | delete_option('duoshuo_connect_failed');
413 | delete_option('duoshuo_notice');
414 | delete_option('duoshuo_sync_lock');
415 |
416 | // 删除所有选项
417 | foreach (self::$_defaultOptions as $optionName => $value)
418 | delete_option($optionName);
419 |
420 | // WP 2.9 以后支持这个函数
421 | if (function_exists('delete_metadata')){
422 | delete_metadata('user', 0, 'duoshuo_access_token', '', true);
423 | delete_metadata('user', 0, 'duoshuo_user_id', '', true);
424 | delete_metadata('post', 0, 'duoshuo_thread_id', '', true);
425 | delete_metadata('comment', 0, 'duoshuo_parent_id', '', true);
426 | delete_metadata('comment', 0, 'duoshuo_post_id', '', true);
427 | }
428 |
429 | //清空comment_agent字段
430 | $wpdb->get_results($wpdb->prepare("update wp_comments set comment_agent='' where comment_agent LIKE '%%Duoshuo/%%'"));
431 |
432 | $redirect_url = add_query_arg('message', 'reset', admin_url('admin.php?page=duoshuo'));
433 | wp_redirect($redirect_url);
434 | exit;
435 | }
436 |
437 | /**
438 | * 关闭默认的评论,避免spammer
439 | */
440 | public function commentsOpen($open, $post_id = null) {
441 | //if ($this->EMBED || get_post_meta($post_id, 'duoshuo_thread_id', true))
442 | // return false;
443 | $script_name = array_pop( explode( '/', $_SERVER['PHP_SELF'] ) );
444 | if (preg_match('/wp\-comments\-post\.php$/', $script_name) && get_post_meta($post_id, 'duoshuo_status', true) !== 'disabled')
445 | return false;
446 |
447 | return $open;
448 | }
449 |
450 | public function commentsTemplate($value){
451 | global $wpdb, $post, $comments;
452 |
453 | $topPost = $this->topPost($post);
454 |
455 | if ($topPost === null) // 可能是inherit 但post_parent=0
456 | return $value;
457 |
458 | if ( !( is_singular() && ( have_comments() || 'open' == $topPost->comment_status ) ) ) {
459 | return $value;
460 | }
461 |
462 | if (get_post_meta($topPost->ID, 'duoshuo_status', true) == 'disabled')
463 | return $value;
464 |
465 | /*
466 | if ( !dsq_is_installed() || !dsq_can_replace() ) {
467 | return $value;
468 | }*/
469 |
470 | $threadId = get_post_meta($topPost->ID, 'duoshuo_thread_id', true);
471 |
472 | if (empty($threadId) && $this->connected()){
473 | $this->syncUserToRemote($topPost->post_author);
474 | $this->syncPostToRemote($topPost->ID, $topPost);
475 | try{
476 | $comments = $wpdb->get_results($wpdb->prepare("SELECT * FROM $wpdb->comments where comment_post_ID = %d AND comment_agent NOT LIKE '%%Duoshuo/%%' order by comment_ID asc", $topPost->ID));
477 | $this->exportComments($comments);
478 | }
479 | catch(Duoshuo_Exception $e){
480 | $this->connectFailed();
481 | }
482 | }
483 |
484 | $this->EMBED = true;
485 | return dirname(__FILE__) . '/comments.php';
486 | // return $value;
487 | }
488 |
489 | public function commentsPopupLinkAttributes($attribs){
490 | global $post;
491 |
492 | $threadKey = $this->threadKey($post);
493 | if ($threadKey === null) // post_status = inherit, post_parent = 0
494 | return $attribs;
495 |
496 | if (has_filter('comments_number', array($this, 'commentsText')))
497 | remove_filter('comments_number', array($this, 'commentsText'));
498 |
499 | $attribs .= ' class="ds-thread-count" data-thread-key="' . $threadKey .'"';
500 | return $attribs;
501 | }
502 |
503 | public function commentsText($comment_text, $number = null){
504 | global $post;
505 |
506 | $threadKey = $this->threadKey($post);
507 | if ($threadKey === null) // post_status = inherit, post_parent = 0
508 | return $comment_text;
509 |
510 | $attribs = 'class="ds-thread-count" data-thread-key="' . $threadKey .'"';
511 | if (preg_match('/^<([a-z]+)( .*)?>(.*)<\/([a-z]+)>$/i', $comment_text, $matches) && $matches[1] == $matches[4])
512 | return "<$matches[1] $attribs$matches[2]>$matches[3]$matches[4]>";
513 | else
514 | return "$comment_text ";
515 | }
516 |
517 | public function jwt($userId = null){
518 | if ($userId === null)
519 | $user = wp_get_current_user();
520 | elseif($userId != 0)
521 | $user = get_user_by( 'id', $userId);
522 |
523 | if (empty($user) || !$user->ID)
524 | return null;
525 |
526 | $token = array(
527 | 'short_name'=> $this->shortName,
528 | 'user_key' => $user->ID,
529 | 'name' => $user->display_name,
530 | );
531 |
532 | return self::encodeJWT($token, $this->secret);
533 | }
534 |
535 | /**
536 | * @deprecated
537 | * @param string|int $userId
538 | * @return mixed
539 | */
540 | public function userData($userId = null){ // null 代表当前登录用户,0代表游客
541 | if ($userId === null)
542 | $current_user = wp_get_current_user();
543 | elseif($userId != 0)
544 | $current_user = get_user_by( 'id', $userId);
545 |
546 | if (isset($current_user) && $current_user->ID) {
547 | $avatar_tag = get_avatar($current_user->ID);
548 | $avatar_data = array();
549 | preg_match('/(src)=((\'|")[^(\'|")]*(\'|"))/i', $avatar_tag, $avatar_data);
550 | $avatar = htmlspecialchars_decode(str_replace(array('"', "'"), '', $avatar_data[2]), ENT_QUOTES);
551 |
552 | return array(
553 | 'id' => $current_user->ID,
554 | 'name' => $current_user->display_name,
555 | 'avatar' => $avatar,
556 | 'email' => $current_user->user_email,
557 | );
558 | }
559 | else{
560 | return array();
561 | }
562 | }
563 |
564 | public function buildQuery($options = array()){
565 | $query = array(
566 | 'short_name' => $this->shortName,
567 | 'sso' => array(
568 | 'login'=> site_url('wp-login.php', 'login') .'?action=duoshuo_login',
569 | 'logout'=> htmlspecialchars_decode(wp_logout_url(), ENT_QUOTES),
570 | ),
571 | );
572 |
573 | if ($theme = $this->getOption('theme'))
574 | $query['theme'] = $theme;
575 |
576 | if ($this->getOption('style_patch'))
577 | $query['stylePatch'] = 'wordpress/' . str_replace(' ', '_', function_exists('wp_get_theme') ? wp_get_theme()->get('Name') : get_current_theme());
578 |
579 | if (!empty($options))
580 | $query['options'] = $options;
581 |
582 | return $query;
583 | }
584 |
585 | public function appendScripts(){
586 | if ($this->_scriptsPrinted)
587 | return;
588 | $this->_scriptsPrinted = true;
589 | ?>
590 |
595 |
596 |
597 |
598 |
599 | _scriptsPrinted)
607 | return;
608 | $this->_scriptsPrinted = true;
609 | ?>
610 | duoshuoUserId)){ // 登录后发现没有本站帐号,输入帐号进行绑定 ?>
627 |
628 |
631 | 注册后和多说帐号绑定
632 | 'duoshuo_login', 'redirect_to'=>urlencode(admin_url())), site_url('wp-login.php', 'login'));?>
636 |
637 | printScripts();?>
638 |
642 | shortName = $_GET['short_name'];
650 | $this->secret = $_GET['secret'];
651 | ?>
652 |
655 | getOption('debug'));
665 |
666 | $progress = $this->getOption('synchronized');
667 |
668 | if (!$progress || is_numeric($progress))// 之前已经完成了导出流程
669 | $progress = 'user/0';
670 |
671 | list($type, $offset) = explode('/', $progress);
672 |
673 | try{
674 | switch($type){
675 | case 'user':
676 | $limit = 30;
677 | // 不包括user_login, user_pass
678 | $columns = array('ID', 'user_nicename', 'user_email', 'user_url', 'user_registered', 'display_name');
679 | $users = $wpdb->get_results( $wpdb->prepare("SELECT " . implode(',', $columns) . " FROM $wpdb->users order by ID asc limit %d,%d", $offset, $limit));
680 | $count = $this->exportUsers($users);
681 | break;
682 | case 'post':
683 | $limit = 10;
684 | $columns = array('ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_excerpt', 'post_status', 'comment_status', 'ping_status', 'post_name', 'post_modified_gmt', 'guid', 'post_type', 'post_parent');
685 | $posts = $wpdb->get_results( $wpdb->prepare("SELECT " . implode(',', $columns) . " FROM $wpdb->posts where post_type not in ('attachment', 'nav_menu_item', 'revision') and post_status not in ('auto-draft', 'draft', 'trash', 'inherit') order by ID asc limit %d,%d", $offset, $limit) );// 'inherit' 不再进行同步
686 | $count = $this->exportPosts($posts);
687 | break;
688 | case 'comment':
689 | $limit = 50;
690 | $comments = $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments where comment_agent NOT LIKE '%%Duoshuo/%%' order by comment_ID asc limit %d,%d", $offset, $limit));
691 | $count = $this->exportComments($comments);
692 | break;
693 | default:
694 | }
695 |
696 | if ($count == $limit){
697 | $progress = $type . '/' . ($offset + $limit);
698 | }
699 | elseif($type == 'user')
700 | $progress = 'post/0';
701 | elseif($type == 'post')
702 | $progress = 'comment/0';
703 | elseif($type == 'comment')
704 | $progress = time();
705 |
706 | update_option('duoshuo_synchronized', $progress);
707 | $response = array(
708 | 'progress'=>$progress,
709 | 'code' => 0
710 | );
711 | $this->sendJsonResponse($response);
712 | }
713 | catch(Duoshuo_Exception $e){
714 | $this->sendException($e);
715 | }
716 | }
717 |
718 | public function packageOptions(){
719 | global $wp_version;
720 |
721 | $options = array(
722 | 'url' => get_option('home'),
723 | 'siteurl' => get_option('siteurl'),
724 | 'admin_email' => get_option('admin_email'),
725 | 'timezone' => get_option('timezone_string'),
726 | 'use_smilies' => get_option('use_smilies'),
727 | 'name' => html_entity_decode(get_option('blogname'), ENT_QUOTES, 'UTF-8'),
728 | 'description' => html_entity_decode(get_option('blogdescription'), ENT_QUOTES, 'UTF-8'),
729 | 'system_theme' => function_exists('wp_get_theme') ? wp_get_theme()->get('Name') : get_current_theme(),//'current_theme'=>'system_theme',
730 | 'system_version'=> $wp_version,
731 | 'plugin_version'=> self::VERSION,
732 | 'local_api_url' => $this->pluginDirUrl . 'api.php',
733 | 'oauth_proxy_url'=> $this->pluginDirUrl . 'oauth-proxy.php',
734 | );
735 |
736 | $akismet_api_key = get_option('wordpress_api_key');
737 | if ($akismet_api_key)
738 | $options['akismet_api_key'] = $akismet_api_key;
739 |
740 | return $options;
741 | }
742 |
743 | /**
744 | * 通知多说服务器更新站点信息
745 | */
746 | public function updateSite(){
747 | if (!$this->connected())
748 | return;
749 |
750 | $params = $this->packageOptions();
751 | $user = wp_get_current_user();
752 |
753 | try{
754 | $response = $this->getClient($user->ID)->request('POST', 'sites/settings', $params);
755 |
756 | if (is_string($response)){
757 | $this->errorMessages[] = $response;
758 | }
759 | elseif(isset($response['code']) && $response['code'] != 0){
760 | $this->errorMessages[] = $response['errorMessage'];
761 | }
762 | }
763 | catch(Duoshuo_Exception $e){
764 | $this->connectFailed();
765 | }
766 | }
767 | /*
768 | public function updatedOption($option, $oldvalue = null, $newvalue = null){
769 | $options = array('blogname', 'blogdescription', 'home', 'siteurl', 'admin_email', 'timezone_string', 'use_smilies', 'system_theme', 'akismet_api_key');
770 |
771 | if (in_array($option, $options))
772 | $this->needToUpdateSite = true;
773 | }*/
774 |
775 | public function syncUserToRemote($userId){
776 | if (!$this->connected())
777 | return ;
778 |
779 | // WP_User
780 | $userData = get_user_by('id', $userId);
781 |
782 | try{
783 | $this->exportUsers(array($userData));
784 | }
785 | catch(Duoshuo_Exception $e){
786 | $this->connectFailed();
787 | }
788 | }
789 |
790 | /**
791 | * 同步这篇文章到所有社交网站
792 | * @param string $postId
793 | */
794 | public function syncPostToRemote($postId, $post = null){
795 | if (!$this->connected())
796 | return;
797 |
798 | if ($post == null)
799 | $post = get_post($postId);
800 |
801 | if (in_array($post->post_type, array('nav_menu_item', 'revision', 'attachment'))
802 | || in_array($post->post_status, array('inherit', 'auto-draft', 'draft', 'trash'))) //'inherit' 不再进行同步
803 | return ;
804 |
805 | $params = $this->packageThread($post);
806 |
807 | if (isset($_POST['sync_to'])){
808 | if ($_POST['sync_to'][0] == 'placeholder')
809 | unset($_POST['sync_to'][0]);
810 | $params['sync_to'] = implode(',', $_POST['sync_to']);
811 | }
812 |
813 | try{
814 | $response = $this->getClient($post->post_author)->request('POST', 'threads/sync', $params);
815 |
816 | unset($_POST['sync_to']); //避免某些插件多次触发save_post
817 |
818 | if (is_array($response) && isset($response['code']) && $response['code'] == 0 && isset($response['response']))
819 | update_post_meta($post->ID, 'duoshuo_thread_id', $response['response']['thread_id']);
820 | }
821 | catch(Duoshuo_Exception $e){
822 | $this->connectFailed();
823 | }
824 | }
825 |
826 | public function packageUser($user){
827 | static $roleMap = array(
828 | 'administrator' => 'administrator',
829 | 'editor' => 'editor',
830 | 'author' => 'author',
831 | 'contributor' => 'user',
832 | 'subscriber' => 'user',
833 | );
834 |
835 | if ($user instanceof WP_User){ // wordpress 3.3
836 | $userData = $user->data;
837 | unset($userData->user_pass);
838 | unset($userData->user_login);
839 | $capabilities = $user->caps;
840 | }
841 | else{
842 | $userData = $user;
843 | unset($userData->user_pass);
844 | unset($userData->user_login);
845 | $capabilities = $this->getUserMeta($user->ID, $this->get_blog_prefix().'capabilities', true);
846 | }
847 |
848 | $data = array(
849 | 'user_key' => $userData->ID,
850 | 'name' => $userData->display_name,
851 | 'email' => $userData->user_email,
852 | 'url' => $userData->user_url,
853 | 'created_at'=> $userData->user_registered,
854 | 'meta' => json_encode($userData),
855 | );
856 |
857 | $avatar_data = array();
858 | if (preg_match('/(src)=((\'|")[^(\'|")]*(\'|"))/i', get_avatar($userData->ID), $avatar_data))
859 | $data['avatar_url'] = htmlspecialchars_decode(str_replace(array('"', "'"), '', $avatar_data[2]), ENT_QUOTES);
860 |
861 | foreach($roleMap as $wpRole => $role)
862 | if (isset($capabilities[$wpRole]) && $capabilities[$wpRole]){
863 | $data['role'] = $role;
864 | break;
865 | }
866 |
867 | return $data;
868 | }
869 |
870 | public function packageThread($post){
871 | $post->custom = get_post_custom($post->ID);
872 | $meta = clone ($post);
873 | unset($meta->post_title);
874 | unset($meta->post_content);
875 | unset($meta->post_excerpt);
876 | unset($meta->post_date_gmt);
877 | unset($meta->post_modified_gmt);
878 | unset($meta->post_name);
879 | unset($meta->post_status);
880 | unset($meta->comment_status);
881 | unset($meta->ping_status);
882 | unset($meta->guid);
883 | unset($meta->post_type);
884 | unset($meta->post_author);
885 | unset($meta->ID);
886 |
887 | $params = array(
888 | 'thread_key'=> $post->ID,
889 | 'author_key'=> $post->post_author,
890 | 'title' => html_entity_decode($post->post_title, ENT_QUOTES, 'UTF-8'),
891 | 'content' => $post->post_content,
892 | 'excerpt' => $post->post_excerpt,
893 | 'created_at'=> mysql2date('Y-m-d\TH:i:s+00:00', $post->post_date_gmt),
894 | 'updated_at'=> mysql2date('Y-m-d\TH:i:s+00:00', $post->post_modified_gmt),
895 | 'ip' => $_SERVER['REMOTE_ADDR'],
896 | 'url' => get_permalink($post),
897 | 'slug' => $post->post_name,
898 | 'status' => $post->post_status,
899 | 'comment_status'=> $post->comment_status,
900 | 'ping_status'=> $post->ping_status,
901 | 'guid' => $post->guid,
902 | 'type' => $post->post_type,
903 | 'meta' => json_encode($meta),
904 | 'source' => 'wordpress',
905 | );
906 |
907 | if (!class_exists('nggLoader', false) || class_exists('nggRewrite', false))
908 | $params['filtered_content'] = str_replace(']]>', ']]>', apply_filters('the_content', $post->post_content));
909 |
910 | if (function_exists('get_post_thumbnail_id')){ // WordPress 2.9开始支持
911 | $post_thumbnail_id = get_post_thumbnail_id( $post->ID );
912 | if ( $post_thumbnail_id ) {
913 | $params['thumbnail'] = $this->normalizeUrl(wp_get_attachment_url($post_thumbnail_id));
914 | //$image = wp_get_attachment_image_src( $post_thumbnail_id, $size, false);
915 | //list($src, $width, $height) = $image;
916 | //$meta = wp_get_attachment_metadata($id);
917 | //'large-feature'
918 | //'post-thumbnail'
919 | }
920 | }
921 |
922 | $args = array(
923 | 'post_parent' => $post->ID,
924 | 'post_status' => 'inherit',
925 | 'post_type' => 'attachment',
926 | 'post_mime_type' => 'image',
927 | 'order' => 'ASC',
928 | 'orderby' => 'menu_order ID'
929 | );
930 | $images = array();
931 | $children = get_children($args);
932 | if (is_array($children))
933 | foreach($children as $attachment)
934 | $images[] = $this->normalizeUrl(wp_get_attachment_url($attachment->ID));
935 | if (!empty($images))
936 | $params['images'] = json_encode($images);
937 |
938 | /*
939 | $authorId = $this->getUserMeta($post->post_author, 'duoshuo_user_id', true);
940 | if (!empty($authorId))
941 | $params['author_id'] = $authorId;
942 |
943 | $threadId = get_post_meta($post->ID, 'duoshuo_thread_id', true);
944 | if (!empty($threadId))
945 | $params['thread_id'] = $threadId;*/
946 | return $params;
947 | }
948 |
949 | public function packageComment($comment){
950 | static $statusMap = array(
951 | '0' => 'pending',
952 | '1' => 'approved',
953 | 'trash' => 'deleted',
954 | 'spam' => 'spam',
955 | 'post-trashed'=>'thread-deleted',
956 | );
957 | $meta = clone ($comment);
958 | unset($meta->comment_ID);
959 | unset($meta->comment_post_ID);
960 | unset($meta->comment_author);
961 | unset($meta->comment_author_email);
962 | unset($meta->comment_author_url);
963 | unset($meta->comment_author_IP);
964 | unset($meta->comment_date_gmt);
965 | unset($meta->comment_content);
966 | unset($meta->comment_karma);
967 | unset($meta->comment_approved);
968 | unset($meta->comment_agent);
969 | unset($meta->comment_type);
970 | unset($meta->comment_parent);
971 | unset($meta->user_id);
972 |
973 | $data = array(
974 | 'thread_key' => $comment->comment_post_ID,
975 | 'post_key' => $comment->comment_ID,
976 | 'author_key' => $comment->user_id,
977 | 'author_name' => htmlspecialchars_decode($comment->comment_author, ENT_QUOTES),
978 | 'author_email' => $comment->comment_author_email,
979 | 'author_url' => $comment->comment_author_url,
980 | 'created_at' => str_replace(' ', 'T', $comment->comment_date_gmt) . '+00:00',
981 | 'message' => $comment->comment_content,
982 | 'agent' => $comment->comment_agent,
983 | 'type' => $comment->comment_type,
984 | 'ip' => $comment->comment_author_IP,
985 | 'status' => $statusMap[$comment->comment_approved],
986 | 'parent_key' => $comment->comment_parent, // TODO 接收的地方要处理一下
987 | 'meta' => json_encode($meta), // comment_date, comment_karma
988 | );
989 | //'source' => 'import',
990 |
991 | return $data;
992 | }
993 |
994 | public function exportOneComment($comment_ID){
995 | $comment = get_comment($comment_ID);
996 |
997 | return $this->exportComments(array($comment));
998 | }
999 |
1000 | /**
1001 | * 从服务器pull评论到本地
1002 | *
1003 | * @param array $posts
1004 | */
1005 | public function createPost($post){
1006 | global $wpdb;
1007 |
1008 | // 将img加到白名单中,仅对WordPress 3.5.0以后有效
1009 | if (!has_filter('wp_kses_allowed_html', array($this, 'allowedHtml')))
1010 | add_filter('wp_kses_allowed_html', array($this, 'allowedHtml'));
1011 |
1012 | static $approvedMap = array(
1013 | 'pending' => '0',
1014 | 'approved' => '1',
1015 | 'deleted' => 'trash',
1016 | 'spam' => 'spam',
1017 | 'thread-deleted'=>'post-trashed',
1018 | );
1019 |
1020 | $post_id = isset($post['thread_key'])
1021 | ? $post['thread_key']
1022 | : $wpdb->get_var("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'duoshuo_thread_id' AND meta_value = $post[thread_id]");
1023 |
1024 | if (!is_numeric($post_id)) // 找不到对应的文章
1025 | return array();
1026 |
1027 | $data = array(
1028 | 'comment_author' => trim(strip_tags($post['author_name'])),
1029 | 'comment_author_email'=>$post['author_email'],
1030 | 'comment_author_url'=> $post['author_url'],
1031 | 'comment_author_IP' => $post['ip'],
1032 | 'comment_date' => $this->rfc3339_to_mysql($post['created_at']),
1033 | 'comment_date_gmt' => $this->rfc3339_to_mysql_gmt($post['created_at']),
1034 | 'comment_content' => $post['message'],
1035 | 'comment_approved' => $approvedMap[$post['status']],
1036 | 'comment_agent' => 'Duoshuo/' . self::VERSION . ':' . $post['post_id'],
1037 | 'comment_type' => $post['type'],
1038 | 'comment_post_ID' => $post_id,
1039 | //'comment_karma'
1040 | );
1041 |
1042 | if ($post['parent_id']){
1043 | $parent_id = $wpdb->get_var( "SELECT comment_ID FROM $wpdb->commentmeta WHERE meta_key = 'duoshuo_post_id' AND meta_value = '$post[parent_id]'");
1044 |
1045 | if (isset($parent_id))
1046 | $data['comment_parent'] = $parent_id;
1047 | }
1048 |
1049 | $author_id = isset($post['author_key'])
1050 | ? $post['author_key']
1051 | : $wpdb->get_var( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = 'duoshuo_user_id' AND meta_value = $post[author_id]");
1052 |
1053 | if (is_numeric($author_id))
1054 | $data['user_id'] = $author_id;
1055 |
1056 | if (isset($post['post_key'])){
1057 | $data['comment_ID'] = $post['post_key'];
1058 | }
1059 | elseif(isset($post['post_id'])){
1060 | $data['comment_ID'] = $wpdb->get_var( "SELECT comment_ID FROM $wpdb->commentmeta WHERE meta_key = 'duoshuo_post_id' AND meta_value = '$post[post_id]'");
1061 | }
1062 |
1063 | if(isset($data['comment_ID'])){
1064 | // wp_update_comment 中会做 wp_filter_comment
1065 | wp_update_comment($data);
1066 | }
1067 | else{
1068 | $data = wp_filter_comment($data);
1069 | $data['comment_ID'] = wp_insert_comment($data);
1070 | }
1071 |
1072 | if ($post['parent_id'])
1073 | update_comment_meta($data['comment_ID'], 'duoshuo_parent_id', $post['parent_id']);
1074 | else
1075 | delete_comment_meta($data['comment_ID'], 'duoshuo_parent_id');
1076 |
1077 | update_comment_meta($data['comment_ID'], 'duoshuo_post_id', $post['post_id']);
1078 |
1079 | return array($post_id);
1080 | }
1081 |
1082 | public function deleteForeverPost($postIdArray){
1083 | global $wpdb;
1084 |
1085 | $commentIdArray = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->commentmeta WHERE meta_key = 'duoshuo_post_id' AND meta_value IN ('" . implode("', '", $postIdArray) . "')");
1086 |
1087 | if (count($commentIdArray)){
1088 | $commentIdArray = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments WHERE comment_ID IN ('" . implode("', '", $commentIdArray) . "')");
1089 |
1090 | foreach ($commentIdArray as $commentId)
1091 | wp_delete_comment($commentId, true);
1092 | }
1093 |
1094 | return array();
1095 | }
1096 |
1097 | public function deletePost($postIdArray){
1098 | global $wpdb;
1099 |
1100 | $commentIdArray = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->commentmeta WHERE meta_key = 'duoshuo_post_id' AND meta_value IN ('" . implode("', '", $postIdArray) . "')");
1101 |
1102 | if (count($commentIdArray)){
1103 | $commentIdArray = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments WHERE comment_ID IN ('" . implode("', '", $commentIdArray) . "')");
1104 |
1105 | foreach ($commentIdArray as $commentId)
1106 | wp_trash_comment($commentId);
1107 | }
1108 |
1109 | return array();
1110 | }
1111 |
1112 | public function spamPost($postIdArray){
1113 | global $wpdb;
1114 |
1115 | $commentIdArray = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->commentmeta WHERE meta_key = 'duoshuo_post_id' AND meta_value IN ('" . implode("', '", $postIdArray) . "')");
1116 |
1117 | if (count($commentIdArray)){
1118 | $commentIdArray = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments WHERE comment_ID IN ('" . implode("', '", $commentIdArray) . "')");
1119 |
1120 | foreach($commentIdArray as $commentId)
1121 | wp_spam_comment($commentId);
1122 | }
1123 |
1124 | return array();
1125 | }
1126 |
1127 | public function approvePost($postIdArray){
1128 | global $wpdb;
1129 |
1130 | $commentIdArray = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->commentmeta WHERE meta_key = 'duoshuo_post_id' AND meta_value IN ('" . implode("', '", $postIdArray) . "')");
1131 |
1132 | if (count($commentIdArray)){
1133 | $commentIdArray = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments WHERE comment_ID IN ('" . implode("', '", $commentIdArray) . "')");
1134 |
1135 | foreach ($commentIdArray as $commentId)
1136 | wp_set_comment_status($commentId, 'approve');
1137 | }
1138 |
1139 | return array();
1140 | }
1141 |
1142 |
1143 | public function notices(){
1144 | foreach($this->errorMessages as $message)
1145 | echo '';
1146 |
1147 | $duoshuo_notice = $this->getOption('notice');
1148 | if (!empty($duoshuo_notice)){//系统推送的通知
1149 | echo ''.$duoshuo_notice.'
';
1150 | }
1151 | elseif ($duoshuo_notice === false){
1152 | update_option('duoshuo_notice', '');
1153 | }
1154 |
1155 | $messages = array(
1156 | 'registered'=>'注册成功,请同步数据 ',
1157 | 'reset' =>'已重置 ',
1158 | );
1159 | if (isset($_GET['message']) && isset($messages[$_GET['message']]))
1160 | echo ''.$messages[$_GET['message']].'
';
1161 | }
1162 |
1163 | public function showException($e){
1164 | echo '';
1165 | }
1166 |
1167 | public function sendException($e){
1168 | $response = array(
1169 | 'code' => $e->getCode(),
1170 | 'errorMessage'=>$e->getMessage(),
1171 | );
1172 | echo json_encode($response);
1173 | exit;
1174 | }
1175 |
1176 | public function sendJsonResponse($response){
1177 | if (!headers_sent()) {
1178 | nocache_headers();
1179 | header('Content-type: application/json; charset=UTF-8');
1180 | }
1181 |
1182 | echo json_encode($response);
1183 | exit;
1184 | }
1185 |
1186 | //发布文章时候的同步设置
1187 | public function syncOptions(){
1188 | global $post;
1189 |
1190 | switch($post->post_status){
1191 | case 'auto-draft':
1192 | case 'inherit':
1193 | case 'draft':
1194 | case 'trash':
1195 | break;
1196 | case 'publish':
1197 | break;
1198 | default:
1199 | }
1200 |
1201 | $query = array(
1202 | 'callback' => 'getSyncOptionsCallback',
1203 | //'require' => 'site,visitor,serverTime',
1204 | 'jwt' => $this->jwt(),
1205 | );
1206 |
1207 | if ($post->ID)
1208 | $query['thread_key'] = $post->ID;
1209 |
1210 | $jsonpUrl = (is_ssl()?'https':'http').'://' . $this->shortName . '.duoshuo.com/api/users/syncOptions.jsonp?' . http_build_query($query, null, '&');
1211 | ?>
1212 |
1266 |
1270 |
1271 | ID, 'duoshuo_status', true), 'disabled'); ?> />
1272 | getOption('debug'));
1292 |
1293 | try{
1294 | $response = array(
1295 | 'count' => $this->syncLog(),
1296 | 'code' => 0
1297 | );
1298 | $this->sendJsonResponse($response);
1299 | }
1300 | catch(Duoshuo_Exception $e){
1301 | if ($e->getCode() == Duoshuo_Exception::REQUEST_TIMED_OUT){
1302 | $this->connectFailed();
1303 | $this->updateOption('sync_lock', 0);
1304 | }
1305 |
1306 | $this->sendException($e);
1307 | }
1308 | }
1309 |
1310 | public function actionsFilter($actions){
1311 | /**
1312 | * TODO
1313 | $actions['ds-comments'] = '管理评论 ';
1314 | */
1315 | return $actions;
1316 | }
1317 |
1318 | public function pluginActionLinks($links, $file) {
1319 | if (empty($this->shortName) || empty($this->secret)) // 如果已经设置好了站点信息,就不显示安装 || !is_numeric($this->getOption('synchronized'))
1320 | array_unshift($links, ''.__('Install').' ');
1321 | else
1322 | array_unshift($links, ''.__('Settings').' ');
1323 | return $links;
1324 | }
1325 |
1326 | public function dashboardWidget(){
1327 | if ( !$widget_options = get_option( 'dashboard_widget_options' ) )
1328 | $widget_options = array();
1329 |
1330 | if ( !isset($widget_options['dashboard_duoshuo']) )
1331 | $widget_options['dashboard_duoshuo'] = array();
1332 |
1333 | $widgets = get_option( 'dashboard_widget_options' );
1334 | $total_items = isset( $widgets['dashboard_duoshuo'] ) && isset( $widgets['dashboard_duoshuo']['items'] )
1335 | ? absint( $widgets['dashboard_duoshuo']['items'] ) : 5;
1336 |
1337 | echo '';
1338 |
1339 | $this->printScripts();
1340 | }
1341 |
1342 | /**
1343 | * The recent comments dashboard widget control.
1344 | *
1345 | * @since 3.0.0
1346 | */
1347 | public function dashboardWidgetControl() {
1348 | if ( !$widget_options = get_option( 'dashboard_widget_options' ) )
1349 | $widget_options = array();
1350 |
1351 | if ( !isset($widget_options['dashboard_duoshuo']) )
1352 | $widget_options['dashboard_duoshuo'] = array();
1353 |
1354 | if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset($_POST['widget-duoshuo']) ) {
1355 | $number = absint( $_POST['widget-duoshuo']['items'] );
1356 | $widget_options['dashboard_duoshuo']['items'] = $number;
1357 | update_option( 'dashboard_widget_options', $widget_options );
1358 | }
1359 |
1360 | $number = isset( $widget_options['dashboard_duoshuo']['items'] ) ? (int) $widget_options['dashboard_duoshuo']['items'] : '';
1361 |
1362 | echo '' . __('Number of comments to show:') . ' ';
1363 | echo '
';
1364 | }
1365 |
1366 | public function registerSettings(){
1367 | register_setting('duoshuo', 'duoshuo_short_name');
1368 | register_setting('duoshuo', 'duoshuo_secret');
1369 |
1370 | foreach (self::$_defaultOptions as $optionName => $value)
1371 | register_setting('duoshuo', $optionName);
1372 | }
1373 |
1374 | public function updateLocalOptions(){
1375 | foreach (self::$_defaultOptions as $optionName => $value)
1376 | if (isset($_POST[$optionName]))
1377 | update_option($optionName, stripslashes($_POST[$optionName]));
1378 |
1379 | //stripslashes($_POST['duoshuo_comments_wrapper_intro'])
1380 | //stripslashes($_POST['duoshuo_comments_wrapper_outro'])
1381 | }
1382 | }
1383 |
--------------------------------------------------------------------------------
/duoshuo/api.php:
--------------------------------------------------------------------------------
1 | 30,
23 | 'errorMessage' => 'Duoshuo plugin hasn\'t been activated.'
24 | );
25 | echo json_encode($response);
26 | exit;
27 | }
28 |
29 | require DUOSHUO_PLUGIN_PATH . '/LocalServer.php';
30 |
31 | $plugin = Duoshuo_WordPress::getInstance();
32 |
33 | try{
34 | if ($_SERVER['REQUEST_METHOD'] == 'POST'){
35 | $input = $_POST;
36 | if (isset($input['spam_confirmed'])) //D-Z Theme 会给POST设置这个参数
37 | unset($input['spam_confirmed']);
38 |
39 | $server = new Duoshuo_LocalServer($plugin);
40 | $server->dispatch($input);
41 | }
42 | }
43 | catch (Exception $e){
44 | $plugin->sendException($e);
45 | }
46 |
--------------------------------------------------------------------------------
/duoshuo/comments-seo.php:
--------------------------------------------------------------------------------
1 | comment_type ) :
6 | case 'pingback' :
7 | case 'trackback' :
8 | ?>
9 |
10 | ', '' ); ?>
11 |
13 | break;
14 | default :
15 | ?>
16 | id="li-comment-">
17 |
38 |
40 | break;
41 | endswitch;
42 | }
43 | }
44 | ?>
45 |
46 |
47 | 1 && get_option('page_comments')): ?>
48 |
53 |
54 |
55 |
63 |
64 | 1 && get_option( 'page_comments' ) ) :?>
65 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/duoshuo/comments.php:
--------------------------------------------------------------------------------
1 | printScripts();
7 |
8 | $topPost = $duoshuoPlugin->topPost($post);
9 |
10 | if ($intro = get_option('duoshuo_comments_wrapper_intro'))
11 | echo $intro;
12 | ?>
13 |
14 |
15 | ID, 'duoshuo_thread_id', true);
18 | if (empty($threadId)):?>
19 | 这篇文章的评论尚未同步到多说,点此同步
20 | $topPost->ID,
25 | 'author-key'=> $topPost->post_author,
26 | 'title' => $topPost->post_title,
27 | 'url' => get_permalink($topPost->ID),
28 | //'order' => 'desc',
29 | //'limit' => 20,
30 | );
31 |
32 | $attribs = '';
33 | foreach ($data as $key => $value)
34 | $attribs .= ' data-' . $key . '="' . esc_attr($value) . '"';
35 | ?>
36 | >
37 |
38 |
42 |
46 |
2 | #ds-export{
3 | margin-bottom:3em;
4 | }
5 | .message-complete, .ds-exported .message-start, .ds-exporting .message-start, .status{
6 | display:none;
7 | }
8 | .ds-exported .message-complete, .message-start, .ds-exporting .status{
9 | display:block;
10 | }
11 |
12 |
95 |
--------------------------------------------------------------------------------
/duoshuo/compat-json.php:
--------------------------------------------------------------------------------
1 |
51 | * @author Matt Knapp
52 | * @author Brett Stimmerman
53 | * @copyright 2005 Michal Migurski
54 | * @version CVS: $Id: JSON.php 288200 2009-09-09 15:41:29Z alan_k $
55 | * @license http://www.opensource.org/licenses/bsd-license.php
56 | * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
57 | */
58 |
59 | /**
60 | * Marker constant for Services_JSON::decode(), used to flag stack state
61 | */
62 | define('SERVICES_JSON_SLICE', 1);
63 |
64 | /**
65 | * Marker constant for Services_JSON::decode(), used to flag stack state
66 | */
67 | define('SERVICES_JSON_IN_STR', 2);
68 |
69 | /**
70 | * Marker constant for Services_JSON::decode(), used to flag stack state
71 | */
72 | define('SERVICES_JSON_IN_ARR', 3);
73 |
74 | /**
75 | * Marker constant for Services_JSON::decode(), used to flag stack state
76 | */
77 | define('SERVICES_JSON_IN_OBJ', 4);
78 |
79 | /**
80 | * Marker constant for Services_JSON::decode(), used to flag stack state
81 | */
82 | define('SERVICES_JSON_IN_CMT', 5);
83 |
84 | /**
85 | * Behavior switch for Services_JSON::decode()
86 | */
87 | define('SERVICES_JSON_LOOSE_TYPE', 16);
88 |
89 | /**
90 | * Behavior switch for Services_JSON::decode()
91 | */
92 | define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
93 |
94 | /**
95 | * Converts to and from JSON format.
96 | *
97 | * Brief example of use:
98 | *
99 | *
100 | * // create a new instance of Services_JSON
101 | * $json = new Services_JSON();
102 | *
103 | * // convert a complexe value to JSON notation, and send it to the browser
104 | * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
105 | * $output = $json->encode($value);
106 | *
107 | * print($output);
108 | * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
109 | *
110 | * // accept incoming POST data, assumed to be in JSON notation
111 | * $input = file_get_contents('php://input', 1000000);
112 | * $value = $json->decode($input);
113 | *
114 | */
115 | class Services_JSON
116 | {
117 | /**
118 | * constructs a new JSON instance
119 | *
120 | * @param int $use object behavior flags; combine with boolean-OR
121 | *
122 | * possible values:
123 | * - SERVICES_JSON_LOOSE_TYPE: loose typing.
124 | * "{...}" syntax creates associative arrays
125 | * instead of objects in decode().
126 | * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
127 | * Values which can't be encoded (e.g. resources)
128 | * appear as NULL instead of throwing errors.
129 | * By default, a deeply-nested resource will
130 | * bubble up with an error, so all return values
131 | * from encode() should be checked with isError()
132 | */
133 | function Services_JSON($use = 0)
134 | {
135 | $this->use = $use;
136 | }
137 |
138 | /**
139 | * convert a string from one UTF-16 char to one UTF-8 char
140 | *
141 | * Normally should be handled by mb_convert_encoding, but
142 | * provides a slower PHP-only method for installations
143 | * that lack the multibye string extension.
144 | *
145 | * @param string $utf16 UTF-16 character
146 | * @return string UTF-8 character
147 | * @access private
148 | */
149 | function utf162utf8($utf16)
150 | {
151 | // oh please oh please oh please oh please oh please
152 | if(function_exists('mb_convert_encoding')) {
153 | return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
154 | }
155 |
156 | $bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);
157 |
158 | switch(true) {
159 | case ((0x7F & $bytes) == $bytes):
160 | // this case should never be reached, because we are in ASCII range
161 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
162 | return chr(0x7F & $bytes);
163 |
164 | case (0x07FF & $bytes) == $bytes:
165 | // return a 2-byte UTF-8 character
166 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
167 | return chr(0xC0 | (($bytes >> 6) & 0x1F))
168 | . chr(0x80 | ($bytes & 0x3F));
169 |
170 | case (0xFFFF & $bytes) == $bytes:
171 | // return a 3-byte UTF-8 character
172 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
173 | return chr(0xE0 | (($bytes >> 12) & 0x0F))
174 | . chr(0x80 | (($bytes >> 6) & 0x3F))
175 | . chr(0x80 | ($bytes & 0x3F));
176 | }
177 |
178 | // ignoring UTF-32 for now, sorry
179 | return '';
180 | }
181 |
182 | /**
183 | * convert a string from one UTF-8 char to one UTF-16 char
184 | *
185 | * Normally should be handled by mb_convert_encoding, but
186 | * provides a slower PHP-only method for installations
187 | * that lack the multibye string extension.
188 | *
189 | * @param string $utf8 UTF-8 character
190 | * @return string UTF-16 character
191 | * @access private
192 | */
193 | function utf82utf16($utf8)
194 | {
195 | // oh please oh please oh please oh please oh please
196 | if(function_exists('mb_convert_encoding')) {
197 | return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
198 | }
199 |
200 | switch(strlen($utf8)) {
201 | case 1:
202 | // this case should never be reached, because we are in ASCII range
203 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
204 | return $utf8;
205 |
206 | case 2:
207 | // return a UTF-16 character from a 2-byte UTF-8 char
208 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
209 | return chr(0x07 & (ord($utf8[0]) >> 2))
210 | . chr((0xC0 & (ord($utf8[0]) << 6))
211 | | (0x3F & ord($utf8[1])));
212 |
213 | case 3:
214 | // return a UTF-16 character from a 3-byte UTF-8 char
215 | // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
216 | return chr((0xF0 & (ord($utf8[0]) << 4))
217 | | (0x0F & (ord($utf8[1]) >> 2)))
218 | . chr((0xC0 & (ord($utf8[1]) << 6))
219 | | (0x7F & ord($utf8[2])));
220 | }
221 |
222 | // ignoring UTF-32 for now, sorry
223 | return '';
224 | }
225 |
226 | /**
227 | * encodes an arbitrary variable into JSON format (and sends JSON Header)
228 | *
229 | * @param mixed $var any number, boolean, string, array, or object to be encoded.
230 | * see argument 1 to Services_JSON() above for array-parsing behavior.
231 | * if var is a strng, note that encode() always expects it
232 | * to be in ASCII or UTF-8 format!
233 | *
234 | * @return mixed JSON string representation of input var or an error if a problem occurs
235 | * @access public
236 | */
237 | function encode($var)
238 | {
239 | header('Content-type: application/json');
240 | return $this->_encode($var);
241 | }
242 | /**
243 | * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow CSS!!!!)
244 | *
245 | * @param mixed $var any number, boolean, string, array, or object to be encoded.
246 | * see argument 1 to Services_JSON() above for array-parsing behavior.
247 | * if var is a strng, note that encode() always expects it
248 | * to be in ASCII or UTF-8 format!
249 | *
250 | * @return mixed JSON string representation of input var or an error if a problem occurs
251 | * @access public
252 | */
253 | function encodeUnsafe($var)
254 | {
255 | return $this->_encode($var);
256 | }
257 | /**
258 | * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format
259 | *
260 | * @param mixed $var any number, boolean, string, array, or object to be encoded.
261 | * see argument 1 to Services_JSON() above for array-parsing behavior.
262 | * if var is a strng, note that encode() always expects it
263 | * to be in ASCII or UTF-8 format!
264 | *
265 | * @return mixed JSON string representation of input var or an error if a problem occurs
266 | * @access public
267 | */
268 | function _encode($var)
269 | {
270 |
271 | switch (gettype($var)) {
272 | case 'boolean':
273 | return $var ? 'true' : 'false';
274 |
275 | case 'NULL':
276 | return 'null';
277 |
278 | case 'integer':
279 | return (int) $var;
280 |
281 | case 'double':
282 | case 'float':
283 | return (float) $var;
284 |
285 | case 'string':
286 | // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
287 | $ascii = '';
288 | $strlen_var = strlen($var);
289 |
290 | /*
291 | * Iterate over every character in the string,
292 | * escaping with a slash or encoding to UTF-8 where necessary
293 | */
294 | for ($c = 0; $c < $strlen_var; ++$c) {
295 |
296 | $ord_var_c = ord($var[$c]);
297 |
298 | switch (true) {
299 | case $ord_var_c == 0x08:
300 | $ascii .= '\b';
301 | break;
302 | case $ord_var_c == 0x09:
303 | $ascii .= '\t';
304 | break;
305 | case $ord_var_c == 0x0A:
306 | $ascii .= '\n';
307 | break;
308 | case $ord_var_c == 0x0C:
309 | $ascii .= '\f';
310 | break;
311 | case $ord_var_c == 0x0D:
312 | $ascii .= '\r';
313 | break;
314 |
315 | case $ord_var_c == 0x22:
316 | case $ord_var_c == 0x2F:
317 | case $ord_var_c == 0x5C:
318 | // double quote, slash, slosh
319 | $ascii .= '\\'.$var[$c];
320 | break;
321 |
322 | case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
323 | // characters U-00000000 - U-0000007F (same as ASCII)
324 | $ascii .= $var[$c];
325 | break;
326 |
327 | case (($ord_var_c & 0xE0) == 0xC0):
328 | // characters U-00000080 - U-000007FF, mask 110XXXXX
329 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
330 | if ($c+1 >= $strlen_var) {
331 | $c += 1;
332 | $ascii .= '?';
333 | break;
334 | }
335 |
336 | $char = pack('C*', $ord_var_c, ord($var[$c + 1]));
337 | $c += 1;
338 | $utf16 = $this->utf82utf16($char);
339 | $ascii .= sprintf('\u%04s', bin2hex($utf16));
340 | break;
341 |
342 | case (($ord_var_c & 0xF0) == 0xE0):
343 | if ($c+2 >= $strlen_var) {
344 | $c += 2;
345 | $ascii .= '?';
346 | break;
347 | }
348 | // characters U-00000800 - U-0000FFFF, mask 1110XXXX
349 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
350 | $char = pack('C*', $ord_var_c,
351 | @ord($var[$c + 1]),
352 | @ord($var[$c + 2]));
353 | $c += 2;
354 | $utf16 = $this->utf82utf16($char);
355 | $ascii .= sprintf('\u%04s', bin2hex($utf16));
356 | break;
357 |
358 | case (($ord_var_c & 0xF8) == 0xF0):
359 | if ($c+3 >= $strlen_var) {
360 | $c += 3;
361 | $ascii .= '?';
362 | break;
363 | }
364 | // characters U-00010000 - U-001FFFFF, mask 11110XXX
365 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
366 | $char = pack('C*', $ord_var_c,
367 | ord($var[$c + 1]),
368 | ord($var[$c + 2]),
369 | ord($var[$c + 3]));
370 | $c += 3;
371 | $utf16 = $this->utf82utf16($char);
372 | $ascii .= sprintf('\u%04s', bin2hex($utf16));
373 | break;
374 |
375 | case (($ord_var_c & 0xFC) == 0xF8):
376 | // characters U-00200000 - U-03FFFFFF, mask 111110XX
377 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
378 | if ($c+4 >= $strlen_var) {
379 | $c += 4;
380 | $ascii .= '?';
381 | break;
382 | }
383 | $char = pack('C*', $ord_var_c,
384 | ord($var[$c + 1]),
385 | ord($var[$c + 2]),
386 | ord($var[$c + 3]),
387 | ord($var[$c + 4]));
388 | $c += 4;
389 | $utf16 = $this->utf82utf16($char);
390 | $ascii .= sprintf('\u%04s', bin2hex($utf16));
391 | break;
392 |
393 | case (($ord_var_c & 0xFE) == 0xFC):
394 | if ($c+5 >= $strlen_var) {
395 | $c += 5;
396 | $ascii .= '?';
397 | break;
398 | }
399 | // characters U-04000000 - U-7FFFFFFF, mask 1111110X
400 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
401 | $char = pack('C*', $ord_var_c,
402 | ord($var[$c + 1]),
403 | ord($var[$c + 2]),
404 | ord($var[$c + 3]),
405 | ord($var[$c + 4]),
406 | ord($var[$c + 5]));
407 | $c += 5;
408 | $utf16 = $this->utf82utf16($char);
409 | $ascii .= sprintf('\u%04s', bin2hex($utf16));
410 | break;
411 | }
412 | }
413 | return '"'.$ascii.'"';
414 |
415 | case 'array':
416 | /*
417 | * As per JSON spec if any array key is not an integer
418 | * we must treat the the whole array as an object. We
419 | * also try to catch a sparsely populated associative
420 | * array with numeric keys here because some JS engines
421 | * will create an array with empty indexes up to
422 | * max_index which can cause memory issues and because
423 | * the keys, which may be relevant, will be remapped
424 | * otherwise.
425 | *
426 | * As per the ECMA and JSON specification an object may
427 | * have any string as a property. Unfortunately due to
428 | * a hole in the ECMA specification if the key is a
429 | * ECMA reserved word or starts with a digit the
430 | * parameter is only accessible using ECMAScript's
431 | * bracket notation.
432 | */
433 |
434 | // treat as a JSON object
435 | if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
436 | $properties = array_map(array($this, 'name_value'),
437 | array_keys($var),
438 | array_values($var));
439 |
440 | foreach($properties as $property) {
441 | if(Services_JSON::isError($property)) {
442 | return $property;
443 | }
444 | }
445 |
446 | return '{' . join(',', $properties) . '}';
447 | }
448 |
449 | // treat it like a regular array
450 | $elements = array_map(array($this, '_encode'), $var);
451 |
452 | foreach($elements as $element) {
453 | if(Services_JSON::isError($element)) {
454 | return $element;
455 | }
456 | }
457 |
458 | return '[' . join(',', $elements) . ']';
459 |
460 | case 'object':
461 | $vars = get_object_vars($var);
462 |
463 | $properties = array_map(array($this, 'name_value'),
464 | array_keys($vars),
465 | array_values($vars));
466 |
467 | foreach($properties as $property) {
468 | if(Services_JSON::isError($property)) {
469 | return $property;
470 | }
471 | }
472 |
473 | return '{' . join(',', $properties) . '}';
474 |
475 | default:
476 | return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
477 | ? 'null'
478 | : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
479 | }
480 | }
481 |
482 | /**
483 | * array-walking function for use in generating JSON-formatted name-value pairs
484 | *
485 | * @param string $name name of key to use
486 | * @param mixed $value reference to an array element to be encoded
487 | *
488 | * @return string JSON-formatted name-value pair, like '"name":value'
489 | * @access private
490 | */
491 | function name_value($name, $value)
492 | {
493 | $encoded_value = $this->_encode($value);
494 |
495 | if(Services_JSON::isError($encoded_value)) {
496 | return $encoded_value;
497 | }
498 |
499 | return $this->_encode(strval($name)) . ':' . $encoded_value;
500 | }
501 |
502 | /**
503 | * reduce a string by removing leading and trailing comments and whitespace
504 | *
505 | * @param $str string string value to strip of comments and whitespace
506 | *
507 | * @return string string value stripped of comments and whitespace
508 | * @access private
509 | */
510 | function reduce_string($str)
511 | {
512 | $str = preg_replace(array(
513 |
514 | // eliminate single line comments in '// ...' form
515 | '#^\s*//(.+)$#m',
516 |
517 | // eliminate multi-line comments in '/* ... */' form, at start of string
518 | '#^\s*/\*(.+)\*/#Us',
519 |
520 | // eliminate multi-line comments in '/* ... */' form, at end of string
521 | '#/\*(.+)\*/\s*$#Us'
522 |
523 | ), '', $str);
524 |
525 | // eliminate extraneous space
526 | return trim($str);
527 | }
528 |
529 | /**
530 | * decodes a JSON string into appropriate variable
531 | *
532 | * @param string $str JSON-formatted string
533 | *
534 | * @return mixed number, boolean, string, array, or object
535 | * corresponding to given JSON input string.
536 | * See argument 1 to Services_JSON() above for object-output behavior.
537 | * Note that decode() always returns strings
538 | * in ASCII or UTF-8 format!
539 | * @access public
540 | */
541 | function decode($str)
542 | {
543 | $str = $this->reduce_string($str);
544 |
545 | switch (strtolower($str)) {
546 | case 'true':
547 | return true;
548 |
549 | case 'false':
550 | return false;
551 |
552 | case 'null':
553 | return null;
554 |
555 | default:
556 | $m = array();
557 |
558 | if (is_numeric($str)) {
559 | // Lookie-loo, it's a number
560 |
561 | // This would work on its own, but I'm trying to be
562 | // good about returning integers where appropriate:
563 | // return (float)$str;
564 |
565 | // Return float or int, as appropriate
566 | return ((float)$str == (integer)$str)
567 | ? (integer)$str
568 | : (float)$str;
569 |
570 | } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
571 | // STRINGS RETURNED IN UTF-8 FORMAT
572 | $delim = substr($str, 0, 1);
573 | $chrs = substr($str, 1, -1);
574 | $utf8 = '';
575 | $strlen_chrs = strlen($chrs);
576 |
577 | for ($c = 0; $c < $strlen_chrs; ++$c) {
578 |
579 | $substr_chrs_c_2 = substr($chrs, $c, 2);
580 | $ord_chrs_c = ord($chrs[$c]);
581 |
582 | switch (true) {
583 | case $substr_chrs_c_2 == '\b':
584 | $utf8 .= chr(0x08);
585 | ++$c;
586 | break;
587 | case $substr_chrs_c_2 == '\t':
588 | $utf8 .= chr(0x09);
589 | ++$c;
590 | break;
591 | case $substr_chrs_c_2 == '\n':
592 | $utf8 .= chr(0x0A);
593 | ++$c;
594 | break;
595 | case $substr_chrs_c_2 == '\f':
596 | $utf8 .= chr(0x0C);
597 | ++$c;
598 | break;
599 | case $substr_chrs_c_2 == '\r':
600 | $utf8 .= chr(0x0D);
601 | ++$c;
602 | break;
603 |
604 | case $substr_chrs_c_2 == '\\"':
605 | case $substr_chrs_c_2 == '\\\'':
606 | case $substr_chrs_c_2 == '\\\\':
607 | case $substr_chrs_c_2 == '\\/':
608 | if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
609 | ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
610 | $utf8 .= $chrs[++$c];
611 | }
612 | break;
613 |
614 | case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
615 | // single, escaped unicode character
616 | $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
617 | . chr(hexdec(substr($chrs, ($c + 4), 2)));
618 | $utf8 .= $this->utf162utf8($utf16);
619 | $c += 5;
620 | break;
621 |
622 | case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
623 | $utf8 .= $chrs[$c];
624 | break;
625 |
626 | case ($ord_chrs_c & 0xE0) == 0xC0:
627 | // characters U-00000080 - U-000007FF, mask 110XXXXX
628 | //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
629 | $utf8 .= substr($chrs, $c, 2);
630 | ++$c;
631 | break;
632 |
633 | case ($ord_chrs_c & 0xF0) == 0xE0:
634 | // characters U-00000800 - U-0000FFFF, mask 1110XXXX
635 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
636 | $utf8 .= substr($chrs, $c, 3);
637 | $c += 2;
638 | break;
639 |
640 | case ($ord_chrs_c & 0xF8) == 0xF0:
641 | // characters U-00010000 - U-001FFFFF, mask 11110XXX
642 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
643 | $utf8 .= substr($chrs, $c, 4);
644 | $c += 3;
645 | break;
646 |
647 | case ($ord_chrs_c & 0xFC) == 0xF8:
648 | // characters U-00200000 - U-03FFFFFF, mask 111110XX
649 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
650 | $utf8 .= substr($chrs, $c, 5);
651 | $c += 4;
652 | break;
653 |
654 | case ($ord_chrs_c & 0xFE) == 0xFC:
655 | // characters U-04000000 - U-7FFFFFFF, mask 1111110X
656 | // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
657 | $utf8 .= substr($chrs, $c, 6);
658 | $c += 5;
659 | break;
660 |
661 | }
662 |
663 | }
664 |
665 | return $utf8;
666 |
667 | } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
668 | // array, or object notation
669 |
670 | if ($str[0] == '[') {
671 | $stk = array(SERVICES_JSON_IN_ARR);
672 | $arr = array();
673 | } else {
674 | if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
675 | $stk = array(SERVICES_JSON_IN_OBJ);
676 | $obj = array();
677 | } else {
678 | $stk = array(SERVICES_JSON_IN_OBJ);
679 | $obj = new stdClass();
680 | }
681 | }
682 |
683 | array_push($stk, array('what' => SERVICES_JSON_SLICE,
684 | 'where' => 0,
685 | 'delim' => false));
686 |
687 | $chrs = substr($str, 1, -1);
688 | $chrs = $this->reduce_string($chrs);
689 |
690 | if ($chrs == '') {
691 | if (reset($stk) == SERVICES_JSON_IN_ARR) {
692 | return $arr;
693 |
694 | } else {
695 | return $obj;
696 |
697 | }
698 | }
699 |
700 | //print("\nparsing {$chrs}\n");
701 |
702 | $strlen_chrs = strlen($chrs);
703 |
704 | for ($c = 0; $c <= $strlen_chrs; ++$c) {
705 |
706 | $top = end($stk);
707 | $substr_chrs_c_2 = substr($chrs, $c, 2);
708 |
709 | if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
710 | // found a comma that is not inside a string, array, etc.,
711 | // OR we've reached the end of the character list
712 | $slice = substr($chrs, $top['where'], ($c - $top['where']));
713 | array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
714 | //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
715 |
716 | if (reset($stk) == SERVICES_JSON_IN_ARR) {
717 | // we are in an array, so just push an element onto the stack
718 | array_push($arr, $this->decode($slice));
719 |
720 | } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
721 | // we are in an object, so figure
722 | // out the property name and set an
723 | // element in an associative array,
724 | // for now
725 | $parts = array();
726 |
727 | if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
728 | // "name":value pair
729 | $key = $this->decode($parts[1]);
730 | $val = $this->decode($parts[2]);
731 |
732 | if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
733 | $obj[$key] = $val;
734 | } else {
735 | $obj->$key = $val;
736 | }
737 | } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
738 | // name:value pair, where name is unquoted
739 | $key = $parts[1];
740 | $val = $this->decode($parts[2]);
741 |
742 | if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
743 | $obj[$key] = $val;
744 | } else {
745 | $obj->$key = $val;
746 | }
747 | }
748 |
749 | }
750 |
751 | } elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
752 | // found a quote, and we are not inside a string
753 | array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c]));
754 | //print("Found start of string at {$c}\n");
755 |
756 | } elseif (($chrs[$c] == $top['delim']) &&
757 | ($top['what'] == SERVICES_JSON_IN_STR) &&
758 | ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
759 | // found a quote, we're in a string, and it's not escaped
760 | // we know that it's not escaped becase there is _not_ an
761 | // odd number of backslashes at the end of the string so far
762 | array_pop($stk);
763 | //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
764 |
765 | } elseif (($chrs[$c] == '[') &&
766 | in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
767 | // found a left-bracket, and we are in an array, object, or slice
768 | array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
769 | //print("Found start of array at {$c}\n");
770 |
771 | } elseif (($chrs[$c] == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
772 | // found a right-bracket, and we're in an array
773 | array_pop($stk);
774 | //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
775 |
776 | } elseif (($chrs[$c] == '{') &&
777 | in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
778 | // found a left-brace, and we are in an array, object, or slice
779 | array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
780 | //print("Found start of object at {$c}\n");
781 |
782 | } elseif (($chrs[$c] == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
783 | // found a right-brace, and we're in an object
784 | array_pop($stk);
785 | //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
786 |
787 | } elseif (($substr_chrs_c_2 == '/*') &&
788 | in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
789 | // found a comment start, and we are in an array, object, or slice
790 | array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
791 | $c++;
792 | //print("Found start of comment at {$c}\n");
793 |
794 | } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
795 | // found a comment end, and we're in one now
796 | array_pop($stk);
797 | $c++;
798 |
799 | for ($i = $top['where']; $i <= $c; ++$i)
800 | $chrs = substr_replace($chrs, ' ', $i, 1);
801 |
802 | //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
803 |
804 | }
805 |
806 | }
807 |
808 | if (reset($stk) == SERVICES_JSON_IN_ARR) {
809 | return $arr;
810 |
811 | } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
812 | return $obj;
813 |
814 | }
815 |
816 | }
817 | }
818 | }
819 |
820 | /**
821 | * @todo Ultimately, this should just call PEAR::isError()
822 | */
823 | function isError($data, $code = null)
824 | {
825 | if (class_exists('pear')) {
826 | return PEAR::isError($data, $code);
827 | } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
828 | is_subclass_of($data, 'services_json_error'))) {
829 | return true;
830 | }
831 |
832 | return false;
833 | }
834 | }
835 |
836 | if (class_exists('PEAR_Error')) {
837 |
838 | class Services_JSON_Error extends PEAR_Error
839 | {
840 | function Services_JSON_Error($message = 'unknown error', $code = null,
841 | $mode = null, $options = null, $userinfo = null)
842 | {
843 | parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
844 | }
845 | }
846 |
847 | } else {
848 |
849 | /**
850 | * @todo Ultimately, this class shall be descended from PEAR_Error
851 | */
852 | class Services_JSON_Error
853 | {
854 | function Services_JSON_Error($message = 'unknown error', $code = null,
855 | $mode = null, $options = null, $userinfo = null)
856 | {
857 |
858 | }
859 | }
860 |
861 | }
862 | endif;
863 |
864 |
865 | /**
866 | * 为了兼容低版本的php而增加的函数集
867 | * json从5.2.0开始支持,wordpress 2.9开始提供json函数的兼容性代码
868 | */
869 | if ( !function_exists('json_encode') ) {
870 | function json_encode( $string ) {
871 | global $wp_json;
872 |
873 | if ( !is_a($wp_json, 'Services_JSON') ) {
874 | $wp_json = new Services_JSON();
875 | }
876 |
877 | return $wp_json->encodeUnsafe( $string );
878 | }
879 | }
880 |
881 | if ( !function_exists('json_decode') ) {
882 | function json_decode( $string, $assoc_array = false ) {
883 | global $wp_json;
884 |
885 | if ( !is_a($wp_json, 'Services_JSON') ) {
886 | $wp_json = new Services_JSON();
887 | }
888 |
889 | $res = $wp_json->decode( $string );
890 | if ( $assoc_array )
891 | $res = _json_decode_object_helper( $res );
892 | return $res;
893 | }
894 | function _json_decode_object_helper($data) {
895 | if ( is_object($data) )
896 | $data = get_object_vars($data);
897 | return is_array($data) ? array_map(__FUNCTION__, $data) : $data;
898 | }
899 | }
900 |
--------------------------------------------------------------------------------
/duoshuo/config.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
注册站点
4 | packageOptions() + array(
7 | 'system' => 'wordpress',
8 | 'callback' => admin_url('admin.php?page=duoshuo'),
9 | 'user_key' => $user->ID,
10 | 'user_name' => $user->display_name,
11 | 'sync_log' => 1,
12 | );
13 |
14 | $adminUrl = is_ssl() ? 'https://' : 'http://';
15 | $adminUrl .= self::DOMAIN . '/connect-site/?'. http_build_query($params, null, '&')
16 | ?>
17 |
18 |
19 |
--------------------------------------------------------------------------------
/duoshuo/duoshuo.php:
--------------------------------------------------------------------------------
1 | 您的php版本低于5.0,请升级php到最新版,多说就能为您服务了。
';
17 | }
18 | add_action('admin_notices', 'duoshuo_php_version_warning');
19 | }
20 | return;
21 | }
22 |
23 | if (version_compare( $wp_version, '2.8', '<' )){
24 | if(is_admin()){
25 | function duoshuo_wp_version_warning(){
26 | echo '您的WordPress版本低于2.8,请升级WordPress到最新版,多说就能为您服务了。
';
27 | }
28 | add_action('admin_notices', 'duoshuo_wp_version_warning');
29 | }
30 | return;
31 | }
32 |
33 | function duoshuo_get_available_transport(){
34 | if (extension_loaded('curl') && function_exists('curl_init') && function_exists('curl_exec'))
35 | return 'curl';
36 |
37 | if (function_exists('fopen') && function_exists('ini_get') && ini_get('allow_url_fopen'))
38 | return 'streams';
39 |
40 | if (function_exists('fsockopen') && (false === ($option = get_option( 'disable_fsockopen' )) || time() - $option >= 43200))
41 | return 'fsockopen';
42 |
43 | return false;
44 | }
45 |
46 | $transport = duoshuo_get_available_transport();
47 | if ($transport === false){
48 | if(is_admin()){
49 | function duoshuo_transport_warning(){
50 | echo '没有可用的 HTTP 传输器 ,请联系你的主机商,安装或开启curl
';
51 | }
52 | add_action('admin_notices', 'duoshuo_transport_warning');
53 | }
54 | return;
55 | }
56 |
57 | if (!extension_loaded('json'))
58 | include DUOSHUO_PLUGIN_PATH . '/compat-json.php';
59 |
60 | require DUOSHUO_PLUGIN_PATH . '/Exception.php';
61 | require DUOSHUO_PLUGIN_PATH . '/Client.php';
62 | require DUOSHUO_PLUGIN_PATH . '/Abstract.php';
63 | require DUOSHUO_PLUGIN_PATH . '/WordPress.php';
64 |
65 | function duoshuo_admin_initialize(){
66 | global $wp_version, $duoshuoPlugin, $plugin_page;
67 |
68 | //在admin界面内执行的action
69 | // wordpress2.8 以后都支持这个过滤器
70 | add_filter('plugin_action_links_duoshuo/duoshuo.php', array($duoshuoPlugin, 'pluginActionLinks'), 10, 2);
71 |
72 | if (empty($duoshuoPlugin->shortName) || empty($duoshuoPlugin->secret)){//你尚未安装这个插件。
73 | function duoshuo_config_warning(){
74 | echo '';
75 | }
76 |
77 | if ($plugin_page !== 'duoshuo')
78 | add_action('admin_notices', 'duoshuo_config_warning');
79 | return ;
80 | }
81 |
82 | add_action('admin_notices', array($duoshuoPlugin, 'notices'));
83 |
84 | add_action('switch_theme', array($duoshuoPlugin, 'updateSite'));
85 | // support from WP 2.9
86 | //add_action('updated_option', array($duoshuoPlugin, 'updatedOption'));
87 |
88 | add_filter('post_row_actions', array($duoshuoPlugin, 'actionsFilter'));
89 |
90 | if (function_exists('get_post_types')){// support from WP 2.9
91 | $post_types = get_post_types( array('public' => true, 'show_in_nav_menus' => true), 'objects');
92 |
93 | foreach($post_types as $type => $object)
94 | add_meta_box('duoshuo-sidebox', '同时发布到', array($duoshuoPlugin,'syncOptions'), $type, 'side', 'high');
95 | }
96 | else{
97 | add_meta_box('duoshuo-sidebox', '同时发布到', array($duoshuoPlugin,'syncOptions'), 'post', 'side', 'high');
98 | add_meta_box('duoshuo-sidebox', '同时发布到', array($duoshuoPlugin,'syncOptions'), 'page', 'side', 'high');
99 | }
100 | //wp 3.0以下不支持此项功能
101 | /**
102 | * TODO
103 | if ($post !== null && 'publish' == $post->post_status || 'private' == $post->post_status)
104 | add_meta_box('duoshuo-comments', '来自社交网站的评论(多说)', array($duoshuoPlugin,'managePostComments'), 'post', 'normal', 'low');
105 | */
106 |
107 | add_action('post_comment_status_meta_box-options', array($duoshuoPlugin, 'commentStatusMetaBoxOptions'));
108 |
109 | add_action('wp_dashboard_setup', 'duoshuo_add_dashboard_widget');
110 |
111 | //// backwards compatible (before WP 3.0)
112 | if (version_compare( $wp_version, '3.0', '<' ) && current_user_can('administrator')){
113 | function duoshuo_wp_version_notice(){
114 | echo '您的WordPress版本低于3.0,如果您能升级WordPress,多说就能更好地为您服务。
';
115 | }
116 | add_action(get_plugin_page_hook('duoshuo', 'duoshuo'), 'duoshuo_wp_version_notice');
117 | add_action(get_plugin_page_hook('duoshuo-preferences', 'duoshuo'), 'duoshuo_wp_version_notice');
118 | add_action(get_plugin_page_hook('duoshuo-settings', 'duoshuo'), 'duoshuo_wp_version_notice');
119 | }
120 |
121 | if (!is_numeric($duoshuoPlugin->getOption('synchronized')) && current_user_can('administrator')){
122 | function duoshuo_unsynchronized_notice(){
123 | echo '';
124 | }
125 |
126 | add_action(get_plugin_page_hook('duoshuo', 'duoshuo'), 'duoshuo_unsynchronized_notice');
127 | add_action(get_plugin_page_hook('duoshuo-preferences', 'duoshuo'), 'duoshuo_unsynchronized_notice');
128 | add_action(get_plugin_page_hook('duoshuo-settings', 'duoshuo'), 'duoshuo_unsynchronized_notice');
129 | }
130 |
131 | add_action('admin_head-edit-comments.php', array($duoshuoPlugin, 'originalCommentsNotice'));
132 |
133 | if (defined('DOING_AJAX')){
134 | add_action('wp_ajax_duoshuo_export', array($duoshuoPlugin, 'export'));
135 | add_action('wp_ajax_duoshuo_sync_log', array($duoshuoPlugin, 'syncLogAction'));
136 | }
137 |
138 | duoshuo_common_initialize();
139 | }
140 |
141 | function duoshuo_initialize(){
142 | global $duoshuoPlugin;
143 |
144 | if (empty($duoshuoPlugin->shortName) || empty($duoshuoPlugin->secret)){
145 | return;
146 | }
147 |
148 | if ($duoshuoPlugin->getOption('social_login_enabled')){
149 | add_action('login_form', array($duoshuoPlugin, 'loginForm'));
150 | add_action('register_form', array($duoshuoPlugin, 'loginForm'));
151 | }
152 |
153 | // wp2.8 以后支持这个事件
154 | add_action(get_option('duoshuo_postpone_print_scripts') ? 'wp_print_footer_scripts' : 'wp_print_scripts', array($duoshuoPlugin, 'appendScripts'));
155 |
156 | //以下应该根据是否设置,选择是否启用
157 | add_filter('comments_template', array($duoshuoPlugin,'commentsTemplate'));
158 |
159 | if (get_option('duoshuo_cc_fix')){ //直接输出HTML评论
160 | add_filter('comments_popup_link_attributes', array($duoshuoPlugin, 'commentsPopupLinkAttributes'));
161 | add_filter('comments_number', array($duoshuoPlugin, 'commentsText'));
162 | }
163 |
164 | if (get_option('duoshuo_sync_pingback_and_trackback')){
165 | add_action('trackback_post', array($duoshuoPlugin, 'exportOneComment'));
166 | add_action('pingback_post', array($duoshuoPlugin, 'exportOneComment'));
167 | }
168 |
169 | duoshuo_common_initialize();
170 | }
171 |
172 | function duoshuo_common_initialize(){
173 | global $duoshuoPlugin;
174 | // 没有用cookie方式保持身份,所以不需要重定向
175 | //add_action('wp_logout', array($duoshuoPlugin, 'logout'));
176 | add_filter('comments_open', array($duoshuoPlugin, 'commentsOpen'), 10, 2);
177 | add_action('set_auth_cookie', array($duoshuoPlugin, 'setJwtCookie'), 10, 5);
178 | add_action('clear_auth_cookie', array($duoshuoPlugin, 'clearJwtCookie'));
179 |
180 | add_action('profile_update', array($duoshuoPlugin, 'syncUserToRemote'));
181 | add_action('user_register', array($duoshuoPlugin, 'userRegisterHook'));
182 | add_action('wp_login', array($duoshuoPlugin, 'bindUser'), 10, 2);
183 |
184 | if ($duoshuoPlugin->getOption('cron_sync_enabled')){
185 | add_action('duoshuo_sync_log_cron', array($duoshuoPlugin, 'syncLog'));
186 | if (!wp_next_scheduled('duoshuo_sync_log_cron')){
187 | wp_schedule_event(time(), 'hourly', 'duoshuo_sync_log_cron');
188 | }
189 | }
190 | }
191 |
192 | // Register widgets.
193 | function duoshuo_register_widgets(){
194 | require_once dirname(__FILE__) . '/widgets.php';
195 |
196 | register_widget('Duoshuo_Widget_Recent_Visitors');
197 | //register_widget('Duoshuo_Widget_Top_Commenters');
198 |
199 | register_widget('Duoshuo_Widget_Recent_Comments');
200 | register_widget('Duoshuo_Widget_Top_Threads');
201 |
202 | register_widget('Duoshuo_Widget_Qqt_Follow');
203 | }
204 |
205 | function duoshuo_add_pages() {
206 | global $duoshuoPlugin;
207 |
208 | if (empty($duoshuoPlugin->shortName) || empty($duoshuoPlugin->secret)){ // 尚未安装
209 | add_object_page(
210 | '安装',
211 | '多说评论',
212 | 'moderate_comments', // 权限
213 | 'duoshuo',
214 | array($duoshuoPlugin, 'config'),
215 | $duoshuoPlugin->pluginDirUrl . 'images/menu-icon.png'
216 | );
217 | }
218 | else{ // 已经安装成功
219 | if (current_user_can('moderate_comments')){
220 | if(get_option('duoshuo_synchronized') === false){
221 | add_object_page(
222 | '数据同步',
223 | '多说评论',
224 | 'moderate_comments',
225 | 'duoshuo',
226 | array($duoshuoPlugin, 'sync'),
227 | $duoshuoPlugin->pluginDirUrl . 'images/menu-icon.png'
228 | );
229 | add_submenu_page(
230 | 'duoshuo',
231 | '多说评论管理',
232 | '评论管理',
233 | 'moderate_comments',
234 | 'duoshuo-manage',
235 | array($duoshuoPlugin,'manage')
236 | );
237 | }
238 | else{
239 | add_object_page(
240 | '多说评论管理',
241 | '多说评论',
242 | 'moderate_comments',
243 | 'duoshuo',
244 | array($duoshuoPlugin,'manage'),
245 | $duoshuoPlugin->pluginDirUrl . 'images/menu-icon.png'
246 | );
247 | }
248 | add_submenu_page(
249 | 'duoshuo',//$parent_slug
250 | '个性化设置',//page_title
251 | '个性化设置',//menu_title
252 | 'manage_options',//权限
253 | 'duoshuo-preferences',//menu_slug
254 | array($duoshuoPlugin, 'preferences')//function
255 | );
256 | add_submenu_page(
257 | 'duoshuo',//$parent_slug
258 | '主题设置',//page_title
259 | '主题设置',//menu_title
260 | 'manage_options',//权限
261 | 'duoshuo-themes',//menu_slug
262 | array($duoshuoPlugin, 'themes')//function
263 | );
264 | add_submenu_page(
265 | 'duoshuo',//$parent_slug
266 | '高级选项',//page_title
267 | '高级选项',//menu_title
268 | 'manage_options',//权限
269 | 'duoshuo-settings',//menu_slug
270 | array($duoshuoPlugin, 'settings')//function
271 | );
272 | add_submenu_page(
273 | 'duoshuo',//$parent_slug
274 | '数据统计',//page_title
275 | '数据统计',//menu_title
276 | 'manage_options',//权限
277 | 'duoshuo-statistics',//menu_slug
278 | array($duoshuoPlugin, 'statistics')//function
279 | );
280 | add_submenu_page(
281 | 'duoshuo',//$parent_slug
282 | '我的多说帐号',//page_title
283 | '我的多说帐号',//menu_title
284 | 'level_0',//权限
285 | 'duoshuo-profile',//menu_slug
286 | array($duoshuoPlugin, 'profile')//function
287 | );
288 | }
289 | elseif(current_user_can('level_0')){
290 | add_submenu_page(
291 | 'profile.php',//$parent_slug
292 | '我的多说帐号',//page_title
293 | '我的多说帐号',//menu_title
294 | 'level_0',//权限
295 | 'duoshuo-profile',//menu_slug
296 | array($duoshuoPlugin, 'profile')//function
297 | );
298 | }
299 | }
300 | }
301 |
302 | function duoshuo_add_dashboard_widget(){
303 | global $duoshuoPlugin;
304 |
305 | wp_add_dashboard_widget('dashboard_duoshuo', '多说最新评论', array($duoshuoPlugin, 'dashboardWidget'), array($duoshuoPlugin, 'dashboardWidgetControl'));
306 | }
307 |
308 | function duoshuo_request_handler(){
309 | global $duoshuoPlugin, $parent_file;
310 |
311 | if ($_SERVER['REQUEST_METHOD'] == 'POST'){
312 | switch ($parent_file){
313 | case 'duoshuo':
314 | if (isset($_POST['duoshuo_reset']))
315 | $duoshuoPlugin->reset();
316 | if (isset($_POST['duoshuo_local_options']))
317 | $duoshuoPlugin->updateLocalOptions();
318 | break;
319 | default:
320 | }
321 | }
322 | elseif ($_SERVER['REQUEST_METHOD'] == 'GET'){
323 | switch ($parent_file){
324 | case 'options-general.php':
325 | if (isset($_GET['settings-updated']))
326 | $duoshuoPlugin->updateSite();
327 | break;
328 | case 'duoshuo':
329 | if (isset($_GET['duoshuo_connect_site']))
330 | $duoshuoPlugin->connectSite();
331 | if (isset($_GET['duoshuo_theme'])){
332 | update_option('duoshuo_theme', $_GET['duoshuo_theme']);
333 | }
334 | break;
335 | default:
336 | }
337 | }
338 | }
339 |
340 | function duoshuo_deactivate($network_wide = false){
341 | // 升级插件的时候也会停用插件
342 | //delete_option('duoshuo_synchronized');
343 | }
344 |
345 |
346 | $duoshuoPlugin = Duoshuo_WordPress::getInstance();
347 |
348 | if(is_admin()){//在admin界面内执行的action
349 | register_deactivation_hook(__FILE__, 'duoshuo_deactivate');
350 | add_action('admin_menu', 'duoshuo_add_pages', 10);
351 | add_action('admin_init', 'duoshuo_request_handler');
352 | add_action('admin_init', array($duoshuoPlugin, 'registerSettings'));
353 | add_action('admin_init', 'duoshuo_admin_initialize');
354 | }
355 | else{
356 | add_action('init', 'duoshuo_initialize');
357 | add_action('login_form_duoshuo_login', array($duoshuoPlugin, 'oauthConnect'));
358 | //add_action('login_form_duoshuo_logout', array($duoshuoPlugin,'oauthDisconnect'));
359 | }
360 |
361 | add_action('widgets_init', 'duoshuo_register_widgets');
362 |
363 | add_action('save_post', array($duoshuoPlugin, 'savePostDuoshuoStatus'));
364 | add_action('save_post', array($duoshuoPlugin, 'syncPostToRemote'), 10, 2);
365 |
366 | /*
367 | if (function_exists('get_post_types')){ // cron jobs runs in common mode, sometimes
368 | foreach(get_post_types() as $type)
369 | if ($type !== 'nav_menu_item' && $type !== 'revision')
370 | add_action('publish_' . $type, array($duoshuoPlugin,'syncPostToRemote'));
371 | }
372 | else{
373 | add_action('publish_post', array($duoshuoPlugin,'syncPostToRemote'));
374 | add_action('publish_page', array($duoshuoPlugin,'syncPostToRemote'));
375 | }
376 | */
377 |
--------------------------------------------------------------------------------
/duoshuo/images/head-icon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/images/head-icon.gif
--------------------------------------------------------------------------------
/duoshuo/images/icon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/images/icon.gif
--------------------------------------------------------------------------------
/duoshuo/images/menu-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/images/menu-icon.png
--------------------------------------------------------------------------------
/duoshuo/images/service_icons_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/images/service_icons_32x32.png
--------------------------------------------------------------------------------
/duoshuo/images/waiting.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/images/waiting.gif
--------------------------------------------------------------------------------
/duoshuo/index.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/duoshuo/manage.php:
--------------------------------------------------------------------------------
1 |
2 | $this->jwt(),
5 | );
6 |
7 | $adminUrl= is_ssl() ? 'https://' : 'http://';
8 | $adminUrl .= $this->shortName . '.' . self::DOMAIN.'/admin/?' . http_build_query($params, null, '&');
9 |
10 | ?>
11 |
12 |
13 |
多说评论管理
14 | 在新窗口中打开
15 |
16 |
17 |
18 |
19 |
30 |
--------------------------------------------------------------------------------
/duoshuo/nanoSha2.php:
--------------------------------------------------------------------------------
1 | .
26 | *
27 | * Include:
28 | *
29 | * require_once("[path/]sha256.inc.php");
30 | *
31 | * Usage Options:
32 | *
33 | * 1) $shaStr = hash('sha256', $string_to_hash);
34 | *
35 | * 2) $shaStr = sha256($string_to_hash[, bool ignore_php5_hash = false]);
36 | *
37 | * 3) $obj = new nanoSha2([bool $upper_case_output = false]);
38 | * $shaStr = $obj->hash($string_to_hash[, bool $ignore_php5_hash = false]);
39 | *
40 | * Reference: http://csrc.nist.gov/groups/ST/toolkit/secure_hashing.html
41 | *
42 | * 2007-12-13: Cleaned up for initial public release
43 | * 2008-05-10: Moved all helper functions into a class. API access unchanged.
44 | * 2009-06-23: Created abstraction of hash() routine
45 | * 2009-07-23: Added detection of 32 vs 64bit platform, and patches.
46 | * Ability to define "_NANO_SHA2_UPPER" to yeild upper case hashes.
47 | * 2009-08-01: Added ability to attempt to use mhash() prior to running pure
48 | * php code.
49 | *
50 | * NOTE: Some sporadic versions of PHP do not handle integer overflows the
51 | * same as the majority of builds. If you get hash results of:
52 | * 7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff7fffffff
53 | *
54 | * If you do not have permissions to change PHP versions (if you did
55 | * you'd probably upgrade to PHP 5 anyway) it is advised you install a
56 | * module that will allow you to use their hashing routines, examples are:
57 | * - mhash module : http://ca3.php.net/mhash
58 | * - Suhosin : http://www.hardened-php.net/suhosin/
59 | *
60 | * If you install the Suhosin module, this script will transparently
61 | * use their routine and define the PHP routine as _nano_sha256().
62 | *
63 | * If the mhash module is present, and $ignore_php5_hash = false the
64 | * script will attempt to use the output from mhash prior to running
65 | * the PHP code.
66 | */
67 | class nanoSha2
68 | {
69 | // php 4 - 5 compatable class properties
70 | var $toUpper;
71 | var $platform;
72 |
73 | // Php 4 - 6 compatable constructor
74 | function nanoSha2($toUpper = false) {
75 | // Determine if the caller wants upper case or not.
76 | $this->toUpper = is_bool($toUpper)
77 | ? $toUpper
78 | : ((defined('_NANO_SHA2_UPPER')) ? true : false);
79 |
80 | // Deteremine if the system is 32 or 64 bit.
81 | $tmpInt = (int)4294967295;
82 | $this->platform = ($tmpInt > 0) ? 64 : 32;
83 | }
84 |
85 | // Do the SHA-256 Padding routine (make input a multiple of 512 bits)
86 | function char_pad($str)
87 | {
88 | $tmpStr = $str;
89 |
90 | $l = strlen($tmpStr)*8; // # of bits from input string
91 |
92 | $tmpStr .= "\x80"; // append the "1" bit followed by 7 0's
93 |
94 | $k = (512 - (($l + 8 + 64) % 512)) / 8; // # of 0 bytes to append
95 | $k += 4; // PHP Strings will never exceed (2^31)-1, 1st 32bits of
96 | // the 64-bit value representing $l can be all 0's
97 |
98 | for ($x = 0; $x < $k; $x++) {
99 | $tmpStr .= "\0";
100 | }
101 |
102 | // append the 32-bits representing # of bits from input string ($l)
103 | $tmpStr .= chr((($l>>24) & 0xFF));
104 | $tmpStr .= chr((($l>>16) & 0xFF));
105 | $tmpStr .= chr((($l>>8) & 0xFF));
106 | $tmpStr .= chr(($l & 0xFF));
107 |
108 | return $tmpStr;
109 | }
110 |
111 | // Here are the bitwise and functions as defined in FIPS180-2 Standard
112 | function addmod2n($x, $y, $n = 4294967296) // Z = (X + Y) mod 2^32
113 | {
114 | $mask = 0x80000000;
115 |
116 | if ($x < 0) {
117 | $x &= 0x7FFFFFFF;
118 | $x = (float)$x + $mask;
119 | }
120 |
121 | if ($y < 0) {
122 | $y &= 0x7FFFFFFF;
123 | $y = (float)$y + $mask;
124 | }
125 |
126 | $r = $x + $y;
127 |
128 | if ($r >= $n) {
129 | while ($r >= $n) {
130 | $r -= $n;
131 | }
132 | }
133 |
134 | return (int)$r;
135 | }
136 |
137 | // Logical bitwise right shift (PHP default is arithmetic shift)
138 | function SHR($x, $n) // x >> n
139 | {
140 | if ($n >= 32) { // impose some limits to keep it 32-bit
141 | return (int)0;
142 | }
143 |
144 | if ($n <= 0) {
145 | return (int)$x;
146 | }
147 |
148 | $mask = 0x40000000;
149 |
150 | if ($x < 0) {
151 | $x &= 0x7FFFFFFF;
152 | $mask = $mask >> ($n-1);
153 | return ($x >> $n) | $mask;
154 | }
155 |
156 | return (int)$x >> (int)$n;
157 | }
158 |
159 | function ROTR($x, $n) { return (int)(($this->SHR($x, $n) | ($x << (32-$n)) & 0xFFFFFFFF)); }
160 | function Ch($x, $y, $z) { return ($x & $y) ^ ((~$x) & $z); }
161 | function Maj($x, $y, $z) { return ($x & $y) ^ ($x & $z) ^ ($y & $z); }
162 | function Sigma0($x) { return (int) ($this->ROTR($x, 2)^$this->ROTR($x, 13)^$this->ROTR($x, 22)); }
163 | function Sigma1($x) { return (int) ($this->ROTR($x, 6)^$this->ROTR($x, 11)^$this->ROTR($x, 25)); }
164 | function sigma_0($x) { return (int) ($this->ROTR($x, 7)^$this->ROTR($x, 18)^$this->SHR($x, 3)); }
165 | function sigma_1($x) { return (int) ($this->ROTR($x, 17)^$this->ROTR($x, 19)^$this->SHR($x, 10)); }
166 |
167 | /*
168 | * Custom functions to provide PHP support
169 | */
170 | // split a byte-string into integer array values
171 | function int_split($input)
172 | {
173 | $l = strlen($input);
174 |
175 | if ($l <= 0) {
176 | return (int)0;
177 | }
178 |
179 | if (($l % 4) != 0) { // invalid input
180 | return false;
181 | }
182 |
183 | for ($i = 0; $i < $l; $i += 4)
184 | {
185 | $int_build = (ord($input[$i]) << 24);
186 | $int_build += (ord($input[$i+1]) << 16);
187 | $int_build += (ord($input[$i+2]) << 8);
188 | $int_build += (ord($input[$i+3]));
189 |
190 | $result[] = $int_build;
191 | }
192 |
193 | return $result;
194 | }
195 |
196 | /**
197 | * Process and return the hash.
198 | *
199 | * @param $str Input string to hash
200 | * @param $ig_func Option param to ignore checking for php > 5.1.2
201 | * @return string Hexadecimal representation of the message digest
202 | */
203 | function hash($str, $ig_func = false)
204 | {
205 | unset($binStr); // binary representation of input string
206 | unset($hexStr); // 256-bit message digest in readable hex format
207 |
208 | // check for php's internal sha256 function, ignore if ig_func==true
209 | if ($ig_func == false) {
210 | if (version_compare(PHP_VERSION,'5.1.2','>=')) {
211 | return hash("sha256", $str, false);
212 | } else if (function_exists('mhash') && defined('MHASH_SHA256')) {
213 | return base64_encode(bin2hex(mhash(MHASH_SHA256, $str)));
214 | }
215 | }
216 |
217 | /*
218 | * SHA-256 Constants
219 | * Sequence of sixty-four constant 32-bit words representing the
220 | * first thirty-two bits of the fractional parts of the cube roots
221 | * of the first sixtyfour prime numbers.
222 | */
223 | $K = array((int)0x428a2f98, (int)0x71374491, (int)0xb5c0fbcf,
224 | (int)0xe9b5dba5, (int)0x3956c25b, (int)0x59f111f1,
225 | (int)0x923f82a4, (int)0xab1c5ed5, (int)0xd807aa98,
226 | (int)0x12835b01, (int)0x243185be, (int)0x550c7dc3,
227 | (int)0x72be5d74, (int)0x80deb1fe, (int)0x9bdc06a7,
228 | (int)0xc19bf174, (int)0xe49b69c1, (int)0xefbe4786,
229 | (int)0x0fc19dc6, (int)0x240ca1cc, (int)0x2de92c6f,
230 | (int)0x4a7484aa, (int)0x5cb0a9dc, (int)0x76f988da,
231 | (int)0x983e5152, (int)0xa831c66d, (int)0xb00327c8,
232 | (int)0xbf597fc7, (int)0xc6e00bf3, (int)0xd5a79147,
233 | (int)0x06ca6351, (int)0x14292967, (int)0x27b70a85,
234 | (int)0x2e1b2138, (int)0x4d2c6dfc, (int)0x53380d13,
235 | (int)0x650a7354, (int)0x766a0abb, (int)0x81c2c92e,
236 | (int)0x92722c85, (int)0xa2bfe8a1, (int)0xa81a664b,
237 | (int)0xc24b8b70, (int)0xc76c51a3, (int)0xd192e819,
238 | (int)0xd6990624, (int)0xf40e3585, (int)0x106aa070,
239 | (int)0x19a4c116, (int)0x1e376c08, (int)0x2748774c,
240 | (int)0x34b0bcb5, (int)0x391c0cb3, (int)0x4ed8aa4a,
241 | (int)0x5b9cca4f, (int)0x682e6ff3, (int)0x748f82ee,
242 | (int)0x78a5636f, (int)0x84c87814, (int)0x8cc70208,
243 | (int)0x90befffa, (int)0xa4506ceb, (int)0xbef9a3f7,
244 | (int)0xc67178f2);
245 |
246 | // Pre-processing: Padding the string
247 | $binStr = $this->char_pad($str);
248 |
249 | // Parsing the Padded Message (Break into N 512-bit blocks)
250 | $M = str_split($binStr, 64);
251 |
252 | // Set the initial hash values
253 | $h[0] = (int)0x6a09e667;
254 | $h[1] = (int)0xbb67ae85;
255 | $h[2] = (int)0x3c6ef372;
256 | $h[3] = (int)0xa54ff53a;
257 | $h[4] = (int)0x510e527f;
258 | $h[5] = (int)0x9b05688c;
259 | $h[6] = (int)0x1f83d9ab;
260 | $h[7] = (int)0x5be0cd19;
261 |
262 | // loop through message blocks and compute hash. ( For i=1 to N : )
263 | $N = count($M);
264 | for ($i = 0; $i < $N; $i++)
265 | {
266 | // Break input block into 16 32bit words (message schedule prep)
267 | $MI = $this->int_split($M[$i]);
268 |
269 | // Initialize working variables
270 | $_a = (int)$h[0];
271 | $_b = (int)$h[1];
272 | $_c = (int)$h[2];
273 | $_d = (int)$h[3];
274 | $_e = (int)$h[4];
275 | $_f = (int)$h[5];
276 | $_g = (int)$h[6];
277 | $_h = (int)$h[7];
278 | unset($_s0);
279 | unset($_s1);
280 | unset($_T1);
281 | unset($_T2);
282 | $W = array();
283 |
284 | // Compute the hash and update
285 | for ($t = 0; $t < 16; $t++)
286 | {
287 | // Prepare the first 16 message schedule values as we loop
288 | $W[$t] = $MI[$t];
289 |
290 | // Compute hash
291 | $_T1 = $this->addmod2n($this->addmod2n($this->addmod2n($this->addmod2n($_h, $this->Sigma1($_e)), $this->Ch($_e, $_f, $_g)), $K[$t]), $W[$t]);
292 | $_T2 = $this->addmod2n($this->Sigma0($_a), $this->Maj($_a, $_b, $_c));
293 |
294 | // Update working variables
295 | $_h = $_g; $_g = $_f; $_f = $_e; $_e = $this->addmod2n($_d, $_T1);
296 | $_d = $_c; $_c = $_b; $_b = $_a; $_a = $this->addmod2n($_T1, $_T2);
297 | }
298 |
299 | for (; $t < 64; $t++)
300 | {
301 | // Continue building the message schedule as we loop
302 | $_s0 = $W[($t+1)&0x0F];
303 | $_s0 = $this->sigma_0($_s0);
304 | $_s1 = $W[($t+14)&0x0F];
305 | $_s1 = $this->sigma_1($_s1);
306 |
307 | $W[$t&0xF] = $this->addmod2n($this->addmod2n($this->addmod2n($W[$t&0xF], $_s0), $_s1), $W[($t+9)&0x0F]);
308 |
309 | // Compute hash
310 | $_T1 = $this->addmod2n($this->addmod2n($this->addmod2n($this->addmod2n($_h, $this->Sigma1($_e)), $this->Ch($_e, $_f, $_g)), $K[$t]), $W[$t&0xF]);
311 | $_T2 = $this->addmod2n($this->Sigma0($_a), $this->Maj($_a, $_b, $_c));
312 |
313 | // Update working variables
314 | $_h = $_g; $_g = $_f; $_f = $_e; $_e = $this->addmod2n($_d, $_T1);
315 | $_d = $_c; $_c = $_b; $_b = $_a; $_a = $this->addmod2n($_T1, $_T2);
316 | }
317 |
318 | $h[0] = $this->addmod2n($h[0], $_a);
319 | $h[1] = $this->addmod2n($h[1], $_b);
320 | $h[2] = $this->addmod2n($h[2], $_c);
321 | $h[3] = $this->addmod2n($h[3], $_d);
322 | $h[4] = $this->addmod2n($h[4], $_e);
323 | $h[5] = $this->addmod2n($h[5], $_f);
324 | $h[6] = $this->addmod2n($h[6], $_g);
325 | $h[7] = $this->addmod2n($h[7], $_h);
326 | }
327 |
328 | // Convert the 32-bit words into human readable hexadecimal format.
329 | $hexStr = sprintf("%08x%08x%08x%08x%08x%08x%08x%08x", $h[0], $h[1], $h[2], $h[3], $h[4], $h[5], $h[6], $h[7]);
330 |
331 | return ($this->toUpper) ? strtoupper($hexStr) : $hexStr;
332 | }
333 |
334 | }
335 |
--------------------------------------------------------------------------------
/duoshuo/oauth-proxy.php:
--------------------------------------------------------------------------------
1 | pluginDirUrl; ?>styles.css" type="text/css" />
2 | $this->jwt(),
5 | );
6 |
7 | $adminUrl = is_ssl() ? 'https://' : 'http://';
8 | $adminUrl .= $this->shortName . '.' . self::DOMAIN . '/admin/settings/?' . http_build_query($params, null, '&');
9 |
10 | ?>
11 |
17 |
18 |
29 |
--------------------------------------------------------------------------------
/duoshuo/profile.php:
--------------------------------------------------------------------------------
1 | $this->jwt(),
4 | );
5 | $settingsUrl = is_ssl()? 'https://' : 'http://';
6 | $settingsUrl .= self::DOMAIN . '/settings/?' . http_build_query($params, null, '&');
7 | ?>
8 |
9 |
10 |
11 |
12 |
我的多说帐号
13 | 在新窗口中打开
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/duoshuo/readme.txt:
--------------------------------------------------------------------------------
1 | === 多说社会化评论框 ===
2 | Contributors: shen2
3 | Donate link: http://duoshuo.com/
4 | Tags: comments, social, share, spam, weibo, qzone, youyan, pinglunla, widget, 评论, 社会化, 分享, 微博, QQ, 腾讯, 新浪微博, 垃圾评论
5 | Requires at least: 2.8
6 | Tested up to: 3.6
7 | Stable tag: 1.0
8 |
9 | 追求最佳用户体验的社会化评论框,为中小网站提供新浪微博、QQ、人人、开心、豆瓣等多帐号登录并评论功能。
10 |
11 | == Description ==
12 |
13 | 追求最佳用户体验的社会化评论框,为中小网站提供新浪微博、QQ(腾讯微博和QQ空间)、人人、开心、豆瓣、网易微博、搜狐微博、百度等多帐号登录并评论功能,显示网页相关的新浪微博和腾讯微博。
14 | “多说”帮你搭建更活跃,互动性更强的评论平台,提高用户黏性和流量。它还有众多实用特性,功能强大且永久免费。
15 | 官方网站:[duoshuo.com](http://duoshuo.com/ "多说网")
16 |
17 | = 特色 =
18 |
19 | 1. 多账号登录:可用新浪微博、QQ(腾讯微博和QQ空间)、人人、开心、豆瓣、网易微博、搜狐微博、百度账号登录发表评论,不必输入邮箱
20 | 1. 单点登录功能支持:已经登陆WordPress的用户,评论框身份和WordPress身份保持一致
21 | 1. 轻松安装:安装WordPress插件或插入一段代码,安装从未如此简单
22 | 1. 优质的速度和稳定性:保证300毫秒或更短加载时间,99.9%正常服务时间
23 | 1. 与社交网站紧密结合:评论同时能分享到各大社交网站,评论框中能自动显示网页相关新浪微博和腾讯微博
24 | 1. 数据实时本地保存:评论内容实时保存到您的WordPress本地服务器,并可将多说评论数据导出,数据永远归你所有
25 | 1. 手机界面深度优化适配:自动识别访问者使用的移动设备,自动适应各种宽度的手机浏览器
26 | 1. 主题样式随意切换:CSS代码全面开源,你可以深度定制属于自己的主题样式
27 |
28 | = 评论框原来可以如此精彩 =
29 | * 多账号登录:让网站的用户轻松加入你的社区参与讨论,支持新浪微博(Sina Weibo)、QQ(Tencent Weibo和Qzone)、人人(Renren)、豆瓣(Douban)、开心网(Kaixin001)、网易微博(Netease)、搜狐微博(Sohu)、百度(Baidu)账号和邮箱登录,更多登录方式陆续添加中。
30 | * 回复提醒:如果评论收到回复,评论者在任何安装了多说系统的网页都可以收到提示,回访你的网站
31 | * 评论标记喜欢:鼓励用户留下言之有物的评论
32 | * 评论界面自定制:自定义CSS,评论框位置,评论排列顺序,多级回复…多说的自定制选项让评论框自然融入你的网站
33 |
34 | = 高效强大的管理后台服务帮你过滤垃圾评论 =
35 |
36 | * 数据本地保存:评论数据实时保存到WordPress本地服务器,并可随时将多说评论导出,不用担心数据丢失
37 | * 智能识别:利用Akismat和其他安装了多说评论系统网站的数据库,智能识别垃圾评论
38 | * 易用的管理后台:一站式后台帮你轻松、高效的处理海量评论。你可以在后台方便的将评论进行分类和删除,只有正常评论才会显示在网站的页面上。
39 | * 多账号管理:多个管理员可登陆管理同一个网站的评论,并可设定管理员、编辑等不同管理权限
40 | * 优质的速度和稳定性:300毫秒或更短加载时间,99.9%正常服务时间,加载评论时访问多说服务器,降低网站自身服务器压力
41 | * 特别为评论内容设计的SEO优化
42 |
43 | = 让多说成为纽带,将你的网站与外界相连 =
44 |
45 | * 分享评论或文章:只需简单勾选,评论或文章即可分享到各大社交网站。带有评论的分享会让评论者的好友更有兴趣访问你的网站
46 | * 发文章同步到各大社交网站:WordPress网站可以在发布文章的同时将文章同步到各大社交网站,轻松更新多个微博和博客
47 | * 微博评论同步:无需设置,自动显示带有文章url的新浪微博和腾讯微博,及其评论和转发
48 | * 跨网站登录:访客在别的网站上登录多说后,再访问你的网站时,可以不用再登录,直接评论
49 |
50 | = 细节特色 =
51 |
52 | * 显示文章相关新浪和腾讯微博
53 | * 回复提醒:页面浮框提醒和邮件提醒
54 | * 最新评论挂件
55 | * 采用Akismet过滤垃圾评论
56 | * 插入表情
57 | * 喜欢文章并分享到社交网络功能
58 | * 支持HTML解析
59 | * 支持Gravatar头像
60 | * 自定义官方微博账号
61 | * 写文章同步到微博时第一张图片作为微博配图
62 | * 支持自定义CSS追加修改多说样式
63 | * 支持首页最新评论列表更新
64 | * 支持文章标题旁边的评论计数更新
65 | * 启用多说时拒绝垃圾广告机器人利用WP接口发布评论
66 | * 定时发微博功能
67 |
68 | = 很容易就能找到我们 =
69 |
70 | > 官方网站:[duoshuo.com](http://duoshuo.com/ "多说网")
71 | > 新浪微博:[@多说网](http://weibo.com/duoshuo)
72 | > 腾讯微博:[多说网](http://t.qq.com/duo-shuo)
73 | > 人人主页:[多说网](http://page.renren.com/699168408)
74 | > 豆瓣小站:[多说网](http://site.douban.com/duoshuo)
75 | > 网易微博:[@多说网](http://t.163.com/duoshuo)
76 | > 搜狐微博:[@多说网](http://duoshuo.t.sohu.com/)
77 | > 电话:010-82827537
78 | > QQ:1175762238
79 | > QQ:2310391001
80 |
81 | = 同类产品 =
82 | * Disqus
83 | * IntenseDebate
84 | * livefyre
85 | * uyan (youyan-social-comment-system)
86 | * pinglunla
87 | * denglu
88 | * wp-connect
89 | * Social Medias Connect
90 |
91 | == Installation ==
92 |
93 | 1. 在WordPress 插件库中搜索"duoshuo", 下载并启用
94 | 2. 在WordPress “设置 -> 多说”中,绑定社交账号,设置二级域名并一键注册
95 | 3. 同步评论到多说,并进行其他设置
96 |
97 | == Frequently Asked Questions ==
98 |
99 | 1. 我的网站想使用多说,请问对网站系统有什么基本要求吗?
100 |
101 | 对于WordPress网站,建议WordPress版本在3.0或以上,这样能完整的实现所有功能。当然,如果版本在WordPress 3.0以下,多说也会尽力支持。多说也支持WordPress Mu,不同博客可以分别启用。
102 | 建议Php程序5.0以上,推荐5.3.0以上,安装curl扩展。
103 | 我们鼓励大家抛弃老掉牙的IE6,所以多说管理页面不支持IE6,强烈建议各位网站主大人使用IE 8,9,Chrome或Firefox浏览器。
104 |
105 | 1. 网站的评论数相当多,同步到多说的过程中断了怎么办?
106 |
107 | 在客户服务中,我们发现部分香港和美国主机的用户,以及数据量比较大的用户,在同步中,出现中断的情况。多说WordPress插件支持同步过程中的断点续传,如果您发现同步数据不动了,或者中断报错,一般情况下再刷新一次页面,重新开始同步过程就好。我们保证数据不会出现重复和冗余。如果多次出现这种情况,请和多说的管理员联系(QQ 2310391001,新浪微博:@多说网)。
108 |
109 | 对于已有大量文章和评论需要同步的网站,建议您在安装前与我们联系,以便我们为您提供更好更及时地支持。
110 |
111 | 1. 发文章时同时发布微博,这里面有什么玄机?
112 |
113 | 发文章时,如果文章写了摘要,那么发出来的微博是“文章摘要+文章链接”的格式。(如果您找不到哪里写摘要,请在编辑文章页面上方找到“显示选项”,在其中勾选上“摘要”)
114 | 如果没有写摘要,那么发出来的微博是“【文章题目】+文章正文+文章链接”的格式。其中文章题目长度为40个中文字或80个英文字。
115 |
116 | 文章中的第一张图片也会发到微博里,优先选择文章的特色图像作为微博配图(如果您找不到哪里设置特色图像,请在编辑文章页面上方找到“显示选项”,在其中勾选上“特色图像”)。只有上传到WordPress中的图片会被发到微博。对于外链图片,目前只支持jpg格式的图片。
117 |
118 | 1. 文章评论能保存到WordPress本地数据库吗?
119 |
120 | 多说WordPress插件支持评论实时保存到网站本地数据库,但这种保存只起到备份作用,在“多说评论”里删除一条评论并不会在本地做同步删除。
121 |
122 | 当您在localhost测试时,由于我们无法链接到你的服务器,保存到本地不是实时的,而是半个小时写回一次。
123 |
124 | == Screenshots ==
125 | 1. 文章/页面下的评论框,支持用新浪微博、QQ、人人、豆瓣、网易微博、搜狐微博、开心网、百度账号帐号登录
126 | 2. 针对手机界面深度优化,响应式的设计自动适配各种移动终端
127 | 3. 评论列表单级回复样式
128 | 4. 你的评论被回复之后,实时提醒
129 | 5. 多款主题供您选择,深色背景的网站毫无违和感。CSS样式代码全面开源,供您深度定制属于自己的评论框
130 | 6. 集成在WordPress后台的多说评论管理界面,智能过滤垃圾评论
131 |
132 | == Changelog ==
133 | = 1.2 (2015-11-23) =
134 | * [修正]修复多说在https下无法使用的问题
135 | * [改进]更彻底的清空站点配置,重置所有关联数据
136 |
137 | = 1.1 (2013-8-28) =
138 | * [新增]评论框主题设定功能
139 | * [新增]环境依赖检查
140 | * [新增]多说CSS样式开源啦
141 | * [改进]针对WordPress主题的自适应主题补丁功能
142 | * [改进]用社交帐号登录之后的提示信息
143 | * [改进]多说评论数据同步回本地时,保留表情图片
144 | * [修正]用社交帐号登录之后跳转到登录页的问题
145 | * [修正]兼容其他插件设置了__autoload()的情况
146 |
147 | = 1.0 (2013-3-14) =
148 | * [新增]支持核心代码后置功能
149 | * [新增]允许单篇文章或页面启用或禁用多说评论框
150 | * [新增]社交登录的新用户允许绑定和注册
151 | * [新增]查看统计数据功能
152 | * [新增]新评论实时桌面提醒
153 | * [新增]反向评论回流功能检测
154 | * [新增]顶起来的评论功能
155 | * [新增]文本自定义功能
156 | * [改进]单点登录的实现方式,全面兼容 W3 Total Cache 和 WP super Cache
157 | * [改进]在非标准WP主题下依然能够正常渲染评论框、最新评论挂件、最新访客挂件等
158 | * [改进]最新访客挂件(小工具)增加头像尺寸选项
159 | * [改进]文章评论计数和原主题结合更好
160 | * [改进]调整卸载功能的说明文字,补充文档
161 | * [改进]管理后台代码重构,速度更快更流畅
162 | * [改进]网页中的相对路径图片,依然能够发微博 (感谢雷锋网的反馈)
163 | * [修正]删除站点之后无法重新设置帐号的问题
164 | * [修正]代码后置在某些版本WP中的问题 (感谢水煮鱼的反馈)
165 | * [修正]网络环境不佳的情况下登录出错的问题
166 |
167 | = 0.9 (2012-10-31) =
168 | * [新增]自定义APIKey功能(暂时仅支持日均访问量大于10000的站点,暂时仅支持新浪微博)
169 | * [新增]评论框支持直接转发/评论微博
170 | * [新增]分布式服务器架构,支持更多更大站点,速度更快
171 | * [改进]允许用户选择不同的API服务器域名
172 | * [改进]在多说服务器无法连接的情况下仍然不影响正常服务
173 | * [改进]分享到社交网络功能改成了ajax模式,编辑文章页面打开更快
174 | * [改进]增强兼容性,避免和个别插件冲突导致微博多次发布
175 | * [改进]从多说服务器同步数据到WordPress数据库,一键点击之后同步所有记录
176 | * [修正]多说分布式架构升级之后,32位php服务器同步评论数据到本地出错的问题
177 | * [修正]同步数据到多说时出现的错误
178 | * [修正]SAE用户同步数据时出现重复同步的问题
179 | * [修正]同步未完成的用户也能进入管理界面
180 | * [修正]部分插件冲突导致的redeclare class问题
181 |
182 | = 0.8 (2012-08-04) =
183 | * [新增]ajax加载文章的评论数
184 | * [新增]热评文章挂件(小工具)
185 | * [新增]首页(仪表盘)新增多说最新评论挂件
186 | * [新增]评论管理界面增加彻底删除评论功能
187 | * [新增]用户管理界面可以直接搜索用户添加管理员
188 | * [新增]管理界面支持导入评论数据文件功能
189 | * [新增]手动同步多说服务器上的数据到本地数据库功能
190 | * [新增]调试功能开关
191 | * [新增]卸载功能(慎用)
192 | * [新增]支持Google帐号登录
193 | * [改进]大规模重构了代码,提高复用度,删除了无用的函数
194 | * [改进]完美支持WordPress 3.4
195 | * [改进]在多说管理界面中做的删除、通过、标记为垃圾评论等操作,都会同步到本地数据库(感谢众多忠实用户的反馈)
196 | * [改进]个人设置页面和站点功能设置页面直接集成iframe管理界面
197 | * [改进]网络不通畅情况下,不影响其他WordPress功能(感谢众多忠实用户的反馈)
198 | * [改进]评论管理界面的导航,更大的操作空间
199 | * [改进]文章的附件不再采用单独的评论框,而是和文章共享同一个评论框
200 | * [改进]不仅通过Akismet同时还启用多说自己的垃圾评论过滤系统处理社交用户评论(感谢众多忠实用户的反馈)
201 | * [修正]反向同步评论数据到本地时,过滤XSS代码(感谢科学松鼠会的反馈)
202 | * [修正]用户合并帐号之后无法用社交帐号登录多说的问题
203 | * [修正]特殊场景下绑定不上社交帐号的问题
204 | * [修正]一个网页中插入多个多说评论框时的bug
205 |
206 | = 0.7.2 (2012-05) =
207 | * [新增]最活跃的用户挂件(小工具)
208 | * [改进]将标题中的特殊字符进行转义
209 |
210 | = 0.7.1 (2012-04-29) =
211 | * [新增]查看对话功能(感谢夜不如新的建议)
212 | * [新增]等待审核的评论计数器
213 | * [新增]自定义文章类型开始支持同步发布到微博(感谢我爱水煮鱼和陆召的建议)
214 | * [新增]发布到搜狐微博的功能支持
215 | * [新增]最近访客挂件(小工具)
216 | * [新增]自带腾讯微博-收听组件(小工具)
217 | * [改进]评论转发到微博后实时更新文章相关微博
218 | * [改进]用社交帐号登录功能可以开启关闭(感谢myqa的反馈)
219 | * [改进]管理后台菜单和标题显示多说图标(感谢我爱水煮鱼的建议)
220 | * [改进]启用多说评论时,关闭WP后台的评论接口,避免垃圾评论利用WP后台评论接口
221 | * [修正]附件图片无法进行评论的问题
222 | * [修正]pingback和trackback无法审核的问题
223 | * [修正]绑定社交帐号无效的问题(感谢静水流深/vatcom_dong的反馈)
224 |
225 | = 0.7 (2012-04-15) =
226 | * [新增]单点登录功能支持,已经登陆WordPress的用户,评论框身份和WordPress身份保持一致
227 | * [新增]更适合深度讨论交流的平铺模式(嵌套层级设置为1即可启用)
228 | * [新增]显示文章相关的腾讯微博
229 | * [新增]用社交网站帐号登录WordPress的功能
230 | * [新增]完美支持即将发布的WordPress 3.4
231 | * [改进]站点首页的新留言及回复提醒功能
232 | * [改进]登录WordPress后台后,显示权限不足的问题
233 | * [修正]文章同步时间相差8小时的问题
234 | * [修正]最新评论小工具(widget)修改设置无效的bug
235 | * [修正]相对时间无效的问题
236 |
237 | = 0.6.1 (2012-03-07) =
238 | * [新增]定时发微博功能
239 | * [新增]最新评论挂件隐藏管理员评论功能
240 | * [新增]显示文章转载记录功能
241 | * [改进]允许一个网页内出现多个评论框
242 | * [改进]提升文章同步速度
243 | * [修正]NextGen Gallery和多说不兼容,导致同步文章时出错的bug(感谢爱发现的反馈)
244 | * [修正]同步文章出错的bug
245 | * [修正]SEO的HTML代码未闭合的bug(感谢ourctc.com的反馈)
246 | * [修正]最新评论挂件显示头像开关无效的bug
247 |
248 | = 0.6 (2012-02-21) =
249 | * [新增]最新评论侧栏挂件(widget)
250 | * [新增]自定义新浪微博/QQ开放平台API Key
251 | * [新增]自定义微博官方帐号
252 | * [新增]自定义版权信息
253 | * [新增]对于文章中ShortCode的支持
254 | * [新增]帐号合并功能
255 | * [新增]解除绑定功能
256 | * [新增]优先采用文章摘要来发布微博
257 | * [新增]意见反馈提示
258 | * [新增]个人评论汇总页面
259 | * [改进]同步数据时更人性化的提示信息
260 | * [改进]akismet垃圾评论过滤可以关闭(感谢天涯海阁的反馈)
261 | * [改进]提升同步数据的速度和稳定性(成功一次导入了10万条评论)(感谢万戈、邻居的耳朵、科学松鼠会的支持)
262 | * [改进]优化了在常见的主题模版下的样式
263 | * [改进]同步文章到QQ空间和人人时的格式混乱问题
264 | * [改进]进一步提升加载速度
265 | * [修正]IE下不能插入表情的bug
266 | * [修正]保存评论到本地时丢失层级关系的问题
267 | * [修正]升级插件时要求重新同步数据的问题
268 | * [修正]连接出错时候的提示信息错误(感谢leyi的反馈)
269 |
270 | = 0.5 (2012-02-07) =
271 | * [新增]显示文章相关微博
272 | * [新增]插入表情功能
273 | * [新增]喜欢文章功能
274 | * [新增]回复提醒功能
275 | * [新增]自定义评论框CSS功能(感谢“DNSpod团队博客”的反馈)
276 | * [新增]采用Akismet过滤垃圾评论
277 | * [改进]安装流程,避免出现多个帐号的现象
278 | * [改进]启用多说时拒绝垃圾广告机器人利用WP接口发布评论(感谢“绿色精品软件”的反馈)
279 | * [改进]同步文章到微博的模版
280 | * [改进]站点信息更新时,同时更新多说的站点设置
281 | * [改进]关闭文章评论功能(感谢“鸸鹋动物园”的反馈)
282 | * [修正]同步数据慢的问题(感谢“懂得”、“十万个为什么”、“UbuntuSoft”、“前端集合”的反馈)
283 | * [修正]重复同步文章导致部分评论看不到的问题(感谢“UbuntuSoft”的反馈)
284 | * [修正]网易微博头像显示不正常的问题(感谢“动漫X档案”的反馈)
285 | * [修正]和Prototype不兼容的问题(感谢“小权”的反馈)
286 | * [修正]管理后台IE6识别错误的问题(感谢“朋朋”的反馈)
287 | * [修正]WordPress2.8-2.9版本中数据同步不正常的问题(感谢“豆果美食”的反馈)
288 | * [修正]几个安全性漏洞
289 |
290 | = 0.4.3 (2012-01-17) =
291 | * [新增]全面支持开心网、搜狐微博、网易微博、百度帐号登陆
292 | * [新增]焕然一新的评论管理后台
293 | * [改进]链接增加nofollow
294 | * [修正]WordPress 3.2以前版本的同步数据兼容性问题
295 | * [修正]部分特殊主题的评论框加载
296 | * [修正]部分用户gravatar头像不显示的问题
297 |
298 | = 0.4.2 (2012-01-10) =
299 | * [新增]同步数据时支持断点续传
300 | * [改进]SEO优化,避免和SuperCache类冲突
301 | * [修正]ipad上日期显示不正确的问题
302 |
303 | = 0.4.1 (2011-12-26) =
304 | * [新增]高级自定义,可以设置评论框嵌套标签
305 | * [新增]SEO功能/本地评论导入功能的开关
306 | * [修正]同步评论到多说的bug
307 | * [修正]低版本php的兼容性问题
308 | * [修正]网速不快的情况下同步数据超时的问题
309 |
310 | = 0.4 (2011-12-25) =
311 | * [新增]支持多说评论反向写回本地数据库(实时的哟)
312 | * [新增]SEO支持,搜索引擎能够爬到评论内容
313 | * [新增]支持HTML解析(白名单制)
314 | * [新增]支持插入视频、图片、表情
315 | * [改进]即使没有curl支持,也可以使用其他方式连接多说服务器
316 | * [改进]安装流程
317 | * [改进]管理界面菜单位置
318 | * [改进]个人帐号管理界面
319 | * [修正]IE6/7下加载评论框报错
320 | * [修正]IE6/7下时间显示错误
321 | * [修正]腾讯微博头像显示问题
322 |
323 | = 0.3.3 (2011-12-14) =
324 | * [修正]一个安全漏洞
325 | * [修正]在WordPress3.3下注册站点和注册用户时的bug
326 | * [新增]账号共享功能,允许多人用同一个微博账号发布微博
327 |
328 | = 0.3.2 (2011-12-13) =
329 | * [改进]在发布文章同步到微博时带上图片
330 | * [改进]文章没有同步时自动同步
331 |
332 | = 0.3.1 (2011-12-12) =
333 | * [修正]在WordPress 3.3下面同步用户不正常的bug
334 | * [修正]新发布的文章不显示评论框的bug
335 |
336 | = 0.3 (2011-12-12) =
337 | * [新增]评论管理界面上线
338 | * [改进]全面支持WordPress 3.3
339 |
340 | = 0.2 (2011-12-6) =
341 | * [改进]发文章同步到微博、QQ空间和人人的功能
342 | * [新增]登录WordPress后台时,可以同时登录多说
343 | * [新增]WordPress管理界面内就可以绑定社交帐号
344 | * [新增]支持Gravatar头像
345 |
346 | = 0.1 (2011-12-2) =
347 | * [新增]基本的功能
348 |
349 | == Upgrade Notice ==
350 |
351 | = 0.3 =
352 | 如果系统提示你“不是管理员或编辑”,请登出WordPress再重新登录
353 |
354 | = 0.2 =
355 | 之前发布的文章,如果没有出现多说评论框,请点击“多说评论框设置 > 同步评论到多说”。
356 |
357 | = 0.1 =
358 | 发布文章功能可能工作不正常。
359 |
360 | == DEMO ==
361 |
362 | 已经有数千家网站开始使用多说,其中包括:
363 |
364 | 1. [站长之家](http://www.chinaz.com/ "站长之家")
365 | 1. [DoNews](http://www.donews.com/ "DoNews")
366 | 1. [泡泡网](http://www.pcpop.com/ "泡泡网")
367 | 1. [IT168](http://www.it168.com/ "IT168")
368 | 1. [下厨房](http://blog.xiachufang.com/ "下厨房")
369 | 1. [邻居的耳朵](http://kxt.fm/ "邻居的耳朵")
370 | 1. [DNSpod](http://blog.dnspod.cn/ "DNSpod")
371 | 1. [Web2.0Share](http://http://www.web20share.com/ "Web2.0Share")
372 | 1. [Ubuntusoft](http://www.ubuntusoft.com "Ubuntusoft")
373 | 1. [分享网络2.0](www.showeb20.com/ "分享网络2.0")
374 | 1. [懒人图库](http://www.lanrentuku.com/ "学会偷懒 懒出境界")
375 | 1. [天堂图片网](http://www.ivsky.com/ "天堂图片网")
376 | 1. [豆果美食](http://blog.douguo.com/ "豆果美食")
377 | 1. [精品绿色便携软件](http://www.portablesoft.org/ "精品绿色便携软件")
378 | 1. [鸸鹋动物园](http://www.ermiao.com/ "鸸鹋动物园")
379 | 1. [Aladd设计量贩铺](http://aladd.net/ "高品质设计分享平台")
380 | 1. [MacGG](http://www.macgg.com/ "MacGG")
381 | 1. [蘑菇爱家居](http://mooogu.cn/blog "蘑菇爱家居")
382 | 1. [微奇生活](http://www.vikilife.com/ "创意玩意集散地")
383 |
--------------------------------------------------------------------------------
/duoshuo/screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/screenshot-1.png
--------------------------------------------------------------------------------
/duoshuo/screenshot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/screenshot-2.png
--------------------------------------------------------------------------------
/duoshuo/screenshot-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/screenshot-3.png
--------------------------------------------------------------------------------
/duoshuo/screenshot-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/screenshot-4.png
--------------------------------------------------------------------------------
/duoshuo/screenshot-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/screenshot-5.png
--------------------------------------------------------------------------------
/duoshuo/screenshot-6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/duoshuo/duoshuo-wordpress/c7aa8e44b87fffc483d3ed62046a3f4bd8ca41cb/duoshuo/screenshot-6.jpg
--------------------------------------------------------------------------------
/duoshuo/settings.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
多说评论框设置
5 |
6 |
高级设定
7 |
93 |
94 |
数据同步
95 |
100 |
104 |
105 |
106 |
107 |
清空多说站点配置
108 |
112 |
113 |
114 |
115 |
环境依赖检查
116 |
117 |
118 |
119 | 依赖
120 | 状态
121 | 结果
122 |
123 |
124 |
125 | 'php版本',
128 | 'wordpress' => 'WordPress版本',
129 | 'json' => 'json扩展',
130 | 'curl' => 'curl扩展',
131 | 'fopen' => 'fopen()',
132 | 'fsockopen' => 'fsockopen()',
133 | 'hash_hmac' => 'hash_hmac()',
134 | );
135 | foreach($dependencies as $key => $name):
136 | list($status, $result) = $this->checkDependency($key);?>
137 |
138 |
139 |
140 | OK' : $result;?>
141 |
142 |
143 |
144 |
145 |
curl扩展、fopen()、fsockopen()只需支持一个即可,推荐使用curl扩展
146 |
147 |
148 |
常见问题和参考链接
149 |
153 |
154 |
意见反馈
155 |
你的意见是多说成长的原动力,欢迎给我们留言 ,或许你想要的功能下一个版本就会实现哦!
156 |
多说正在招人!如果你相信改变世界不是资本而是技术;如果你不只是想完成任务,还希望你的巧妙构思实现意想不到的好处;如果你希望和跟你一样聪明的人一起工作。那么你不妨加入我们!
157 |
158 |
159 |
160 |
161 | 'QQ空间',
164 | 'weibo' => '新浪微博',
165 | 'qqt' => '腾讯微博',
166 | 'renren'=> '人人网',
167 | 'kaixin'=> '开心网',
168 | 'douban'=> '豆瓣网',
169 | 'netease'=> '网易微博',
170 | 'sohu' => '搜狐微博',
171 | );
172 |
173 | ?>
174 |
我们永远相信,分享是一种美德
175 |
把多说分享给你的朋友:
176 |
177 | $serviceName):?>
178 |
179 |
180 |
181 |
210 |
211 |
212 |
--------------------------------------------------------------------------------
/duoshuo/statistics.php:
--------------------------------------------------------------------------------
1 |
2 | $this->jwt(),
5 | );
6 | $adminUrl = is_ssl()? 'https://' : 'http://';
7 | $adminUrl .= $this->shortName . '.' . self::DOMAIN . '/admin/statistics/?' . http_build_query($params, null, '&');
8 | ?>
9 |
15 |
--------------------------------------------------------------------------------
/duoshuo/styles.css:
--------------------------------------------------------------------------------
1 | .ds-service-icon li{
2 | float: left;
3 | margin-right:6px;
4 | }
5 | .ds-service-icon a {
6 | display: block;
7 | cursor:pointer;
8 | width: 32px !important;
9 | height: 32px !important;
10 | background: url(images/service_icons_32x32.png) no-repeat;
11 | overflow: hidden;
12 | text-indent:-9999px;
13 | }
14 | .ds-service-icon a.ds-weibo {background-position: 0 0;}
15 | .ds-service-icon a.ds-qzone {background-position: 0 -32px;}
16 | .ds-service-icon a.ds-qqt {background-position: 0 -1760px;}
17 | .ds-service-icon a.ds-renren {background-position: 0 -64px;}
18 | .ds-service-icon a.ds-kaixin {background-position: 0 -192px;}
19 | .ds-service-icon a.ds-netease {background-position: 0 -320px;}
20 | .ds-service-icon a.ds-sohu {background-position: 0 -1248px;}
21 | .ds-service-icon a.ds-qq {background-position: 0 -2208px;}
22 | .ds-service-icon a.ds-douban {background-position: 0 -224px;}
23 | .ds-service-icon a.ds-baidu {background-position: 0 -352px;}
24 | .ds-service-icon a.ds-taobao {background-position: 0 -2016px;}
25 | .ds-service-icon a.ds-msn {background-position: 0 -864px;}
26 |
27 | #icon-duoshuo{
28 | background: url("images/head-icon.gif") no-repeat scroll 0 4px transparent;
29 | }
30 |
31 | .ds-icon-yes{
32 | display:inline-block;
33 | background: url("images/icon.gif") no-repeat 0 -10px transparent;
34 | width: 15px;
35 | height: 12px;
36 | text-indent:-9999px;
37 | }
38 | .ds-icon-no{
39 | display:inline-block;
40 | background: url("images/icon.gif") no-repeat 0 -22px transparent;
41 | width: 15px;
42 | height: 12px;
43 | text-indent:-9999px;
44 | }
45 |
46 | .ds-dependencies{
47 | width:500px;
48 | border-collapse: collapse;
49 | border: 1px solid #ebebeb;
50 | }
51 | .ds-dependencies td, .ds-dependencies th {padding: 8px; text-align:left;}
52 | .ds-dependencies thead tr{background-color : #ebebeb;}
53 | .ds-dependencies tbody tr:nth-child(even){background-color : #ebebeb;}
54 | .ds-dependencies tbody tr:nth-child(odd){background-color : #FFF;}
55 |
--------------------------------------------------------------------------------
/duoshuo/sync.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
数据同步
4 |
5 |
安装成功了!只要一键将您的用户、文章和评论信息同步到多说,多说就可以开始为您服务了!开始同步
6 |
7 |
同步完成,现在你可以设置 或管理
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/duoshuo/themes.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
多说主题设置
6 |
7 |
13 |
23 |
45 |
46 | shortName .".duoshuo.com/api/sites/themes.jsonp?callback=loadDuoshuoThemes";
49 |
50 | ?>
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/duoshuo/widgets.php:
--------------------------------------------------------------------------------
1 | 'ds-widget-recent-comments', 'description' => '最新评论(由多说提供)' );
9 | parent::__construct('ds-recent-comments', '最新评论(多说)', $widget_ops);
10 |
11 | $this->alt_option_name = 'duoshuo_widget_recent_comments';
12 |
13 | if ( is_active_widget(false, false, $this->id_base) )
14 | add_action( 'wp_head', array(&$this, 'recent_comments_style') );
15 |
16 | //add_action( 'comment_post', array(&$this, 'flush_widget_cache') );
17 | //add_action( 'transition_comment_status', array(&$this, 'flush_widget_cache') );
18 |
19 | $this->duoshuoPlugin = Duoshuo_WordPress::getInstance();
20 | }
21 |
22 | function recent_comments_style() {
23 | if ( ! current_theme_supports( 'widgets' ) )// Temp hack #14876
24 | return;
25 | }
26 |
27 | function widget( $args, $instance ) {
28 | global $comments, $comment;
29 |
30 | if ( ! isset( $args['widget_id'] ) )
31 | $args['widget_id'] = $this->id;
32 |
33 | extract($args, EXTR_SKIP);
34 |
35 | $output = '';
36 | $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? __( 'Recent Comments' ) : $instance['title'], $instance, $this->id_base );
37 |
38 | if ( empty( $instance['number'] ) || ! $number = absint( $instance['number'] ) )
39 | $number = 10;
40 |
41 | $output .= $before_widget;
42 | if ( $title )
43 | $output .= $before_title . $title . $after_title;
44 |
45 | $data = array(
46 | 'num_items' => $number,
47 | 'show_avatars'=>isset($instance['show_avatars']) ? $instance['show_avatars'] : 1,
48 | 'show_time'=> isset($instance['show_time']) ? $instance['show_time'] : 1,
49 | 'show_title'=> isset($instance['show_title']) ? $instance['show_title'] : 1,
50 | 'show_admin'=> isset($instance['show_admin']) ? $instance['show_admin'] : 1,
51 | 'avatar_size'=> 30,
52 | 'excerpt_length'=> isset($instance['excerpt_length']) ? $instance['excerpt_length'] : 70,
53 | );
54 | $attribs = '';
55 | foreach ($data as $key => $value)
56 | $attribs .= ' data-' . str_replace('_','-',$key) . '="' . esc_attr($value) . '"';
57 | $output .= ''
58 | . $after_widget;
59 | echo $output;?>
60 | duoshuoPlugin->printScripts();
65 | }
66 |
67 |
68 | function update( $new_instance, $old_instance ) {
69 | $instance = $old_instance;
70 | $instance['title'] = strip_tags($new_instance['title']);
71 | $instance['number'] = absint( $new_instance['number'] );
72 | $instance['excerpt_length'] = absint( $new_instance['excerpt_length'] );
73 | $instance['show_avatars'] = absint( $new_instance['show_avatars'] );
74 | $instance['show_time'] = absint( $new_instance['show_time'] );
75 | $instance['show_title'] = absint( $new_instance['show_title'] );
76 | $instance['show_admin'] = absint( $new_instance['show_admin'] );
77 |
78 | $alloptions = wp_cache_get( 'alloptions', 'options' );
79 | if ( isset($alloptions['duoshuo_widget_recent_comments']) )
80 | delete_option('duoshuo_widget_recent_comments');
81 |
82 | return $instance;
83 | }
84 |
85 | function form( $instance ) {
86 | $title = isset($instance['title']) ? esc_attr($instance['title']) : '';
87 | $number = isset($instance['number']) ? absint($instance['number']) : 5;
88 | $show_avatars = isset($instance['show_avatars']) ? absint( $instance['show_avatars']) : 1;
89 | $show_title = isset($instance['show_title']) ? absint($instance['show_title']) : 1;
90 | $show_time = isset($instance['show_time']) ? absint($instance['show_time']) : 1;
91 | $show_admin = isset($instance['show_admin']) ? absint($instance['show_admin']) : 1;
92 | $excerpt_length = isset($instance['excerpt_length']) ? absint($instance['excerpt_length']) : 70;
93 | ?>
94 |
95 |
96 |
97 |
98 |
99 | />
100 | 显示头像
101 |
102 |
103 |
104 |
105 | />
106 | 显示评论时间
107 |
108 |
109 |
110 |
111 | />
112 | 显示文章标题
113 |
114 |
115 |
116 |
117 | />
118 | 显示管理员评论
119 |
120 |
121 |
122 |
123 |
124 |
125 | 引文字数(中文):
126 |
127 | 'ds-widget-top-threads', 'description' => '热评文章(由多说提供)');
137 | parent::__construct('ds-top-threads', '热评文章(多说)', $widget_ops);
138 |
139 | $this->alt_option_name = 'duoshuo_widget_top_threads';
140 |
141 | $this->duoshuoPlugin = Duoshuo_WordPress::getInstance();
142 | }
143 |
144 | function widget( $args, $instance ) {
145 | global $comments, $comment;
146 |
147 | if ( ! isset( $args['widget_id'] ) )
148 | $args['widget_id'] = $this->id;
149 |
150 | extract($args, EXTR_SKIP);
151 |
152 | $output = '';
153 | $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '热评文章' : $instance['title'], $instance, $this->id_base );
154 |
155 | if ( empty( $instance['number'] ) || ! $number = absint( $instance['number'] ) )
156 | $number = 5;
157 |
158 | $output .= $before_widget;
159 | if ( $title )
160 | $output .= $before_title . $title . $after_title;
161 |
162 | $data = array(
163 | 'num_items' => $number,
164 | 'range' => isset($instance['range']) ? $instance['range'] : 'weekly',
165 | //'show_avatars'=>isset($instance['show_avatars']) ? $instance['show_avatars'] : 1,
166 | //'avatar_size'=> 30,
167 | );
168 | $attribs = '';
169 | foreach ($data as $key => $value)
170 | $attribs .= ' data-' . str_replace('_','-',$key) . '="' . esc_attr($value) . '"';
171 | $output .= ''
172 | . $after_widget;
173 | echo $output;?>
174 | duoshuoPlugin->printScripts();
179 | }
180 |
181 | function update( $new_instance, $old_instance ) {
182 | $instance = $old_instance;
183 | $instance['range'] = $new_instance['range'];
184 | $instance['title'] = strip_tags($new_instance['title']);
185 | $instance['number'] = absint( $new_instance['number'] );
186 | //$instance['show_avatars'] = absint( $new_instance['show_avatars'] );
187 |
188 | $alloptions = wp_cache_get( 'alloptions', 'options' );
189 | if ( isset($alloptions['duoshuo_widget_top_threads']) )
190 | delete_option('duoshuo_widget_top_threads');
191 |
192 | return $instance;
193 | }
194 |
195 | function form( $instance ) {
196 | $title = isset($instance['title']) ? esc_attr($instance['title']) : '';
197 | $range = isset($instance['range']) ? esc_attr($instance['range']) : 'weekly';
198 | $number = isset($instance['number']) ? absint($instance['number']) : 5;
199 | //$show_avatars = isset($instance['show_avatars']) ? absint( $instance['show_avatars']) : 1;
200 | ?>
201 |
202 |
203 |
204 |
205 | />24小时内
206 | />7天内
207 | />30天内
208 |
209 |
216 |
217 |
218 | 'ds-widget-recent-visitors', 'description' => '最近访客(由多说提供)' );
226 | parent::__construct('ds-recent-visitors', '最近访客(多说)', $widget_ops);
227 |
228 | $this->alt_option_name = 'duoshuo_widget_recent_visitors';
229 |
230 | if ( is_active_widget(false, false, $this->id_base) )
231 | add_action( 'wp_head', array(&$this, 'printScripts') );
232 |
233 | //add_action( 'comment_post', array(&$this, 'flush_widget_cache') );
234 | //add_action( 'transition_comment_status', array(&$this, 'flush_widget_cache') );
235 |
236 | $this->duoshuoPlugin = Duoshuo_WordPress::getInstance();
237 | }
238 |
239 | function printScripts() {
240 | if ( ! current_theme_supports( 'widgets' ) )// Temp hack #14876
241 | return;
242 | }
243 |
244 | function widget( $args, $instance ) {
245 | global $comments, $comment;
246 |
247 | if ( ! isset( $args['widget_id'] ) )
248 | $args['widget_id'] = $this->id;
249 |
250 | extract($args, EXTR_SKIP);
251 |
252 | $output = '';
253 | $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '最近访客' : $instance['title'], $instance, $this->id_base );
254 |
255 | if ( empty( $instance['number'] ) || ! $number = absint( $instance['number'] ) )
256 | $number = 12;
257 |
258 | $output .= $before_widget;
259 | if ( $title )
260 | $output .= $before_title . $title . $after_title;
261 |
262 | $data = array(
263 | 'num_items' => $number,
264 | 'show_time'=> isset($instance['show_time']) ? $instance['show_time'] : 1,
265 | 'avatar_size'=> isset($instance['avatar_size']) ? $instance['avatar_size'] : 50,
266 | );
267 | $attribs = '';
268 | foreach ($data as $key => $value)
269 | $attribs .= ' data-' . str_replace('_','-',$key) . '="' . esc_attr($value) . '"';
270 | $output .= ''
271 | . $after_widget;
272 | echo $output;?>
273 | duoshuoPlugin->printScripts();
278 | }
279 |
280 |
281 | function update( $new_instance, $old_instance ) {
282 | $instance = $old_instance;
283 | $instance['title'] = strip_tags($new_instance['title']);
284 | $instance['number'] = absint( $new_instance['number'] );
285 | $instance['show_time'] = absint( $new_instance['show_time'] );
286 | $instance['avatar_size'] = absint( $new_instance['avatar_size'] );
287 |
288 | $alloptions = wp_cache_get( 'alloptions', 'options' );
289 | if ( isset($alloptions['duoshuo_widget_recent_visitors']) )
290 | delete_option('duoshuo_widget_recent_visitors');
291 |
292 | return $instance;
293 | }
294 |
295 | function form( $instance ) {
296 | $title = isset($instance['title']) ? esc_attr($instance['title']) : '';
297 | $number = isset($instance['number']) ? absint($instance['number']) : 15;
298 | $show_time = isset($instance['show_time']) ? absint($instance['show_time']) : 1;
299 | $avatar_size = isset($instance['avatar_size']) ? absint($instance['avatar_size']) : 50;
300 | ?>
301 |
302 |
303 |
304 |
309 |
310 | 显示访客的数量:
311 |
312 | 头像尺寸:
313 | px
314 | 'ds-widget-qqt-follow', 'description' => '腾讯微博-收听组件(由多说提供)' );
323 | parent::__construct('ds-qqt-follow', '腾讯微博-收听(多说)', $widget_ops);
324 |
325 | $this->alt_option_name = 'duoshuo_widget_qqt_follow';
326 |
327 | }
328 |
329 | function widget( $args, $instance ) {
330 |
331 | if ( ! isset( $args['widget_id'] ) )
332 | $args['widget_id'] = $this->id;
333 |
334 | extract($args, EXTR_SKIP);
335 |
336 | $output = $before_widget;
337 |
338 | $title = apply_filters( 'widget_title', isset( $instance['title'] ) ? $instance['title'] : '', $instance, $this->id_base );
339 |
340 | if ( $title )
341 | $output .= $before_title . $title . $after_title;
342 |
343 | $params = array(
344 | 'c' => 'follow',
345 | 'a' => 'quick',
346 | 'name'=>isset($instance['qqt_name']) ? $instance['qqt_name'] : 'duo-shuo',
347 | 'style'=>isset($instance['qqt_style']) ? $instance['qqt_style'] : 1,
348 | 't' => time() . sprintf("%03d", microtime() * 1000),
349 | 'f' => isset($instance['qqt_followers']) ? $instance['qqt_followers'] : 1,
350 | );
351 |
352 | switch($params['style']){
353 | case 1:
354 | $width = $params['f'] ? 227 : 167;
355 | $height = 75;
356 | break;
357 | case 2:
358 | $width = $params['f'] ? 191 : 136;
359 | $height = 38;
360 | break;
361 | case 3:
362 | $width = $params['f'] ? 168 : 125;
363 | $height = 20;
364 | break;
365 | case 4:
366 | $width = $params['f'] ? 182 : 125;
367 | $height = 27;
368 | break;
369 | case 5:
370 | $width = $params['f'] ? 178 : 125;
371 | $height = 24;
372 | break;
373 | default:
374 | }
375 |
376 | $attribs = array(
377 | 'scrolling' => 'no',
378 | 'width' => $width,
379 | 'height' => $height,
380 | 'frameborder'=> 0,
381 | 'allowtransparency'=>'true',
382 | 'marginheight'=>0,
383 | 'marginwidth'=> 0,
384 | 'src' => (is_ssl()?'https':'http').'://follow.v.t.qq.com/index.php?' . http_build_query($params, null, '&'),
385 | );
386 |
387 | $output .= '' . $after_widget;
391 | echo $output;
392 | }
393 |
394 |
395 | function update( $new_instance, $old_instance ) {
396 | $instance = $old_instance;
397 | $instance['title'] = strip_tags($new_instance['title']);
398 | $instance['qqt_name'] = strip_tags($new_instance['qqt_name']);
399 | $instance['qqt_style'] = absint( $new_instance['qqt_style'] );
400 | $instance['qqt_followers'] = absint( $new_instance['qqt_followers'] );
401 |
402 | $alloptions = wp_cache_get( 'alloptions', 'options' );
403 | if ( isset($alloptions['duoshuo_widget_qqt_follow']) )
404 | delete_option('duoshuo_widget_qqt_follow');
405 |
406 | return $instance;
407 | }
408 |
409 | function form( $instance ) {
410 | $title = isset($instance['title']) ? $instance['title'] : '';
411 | $qqt_name = isset($instance['qqt_name']) ? $instance['qqt_name'] : '';
412 | $qqt_style = isset($instance['qqt_style']) ? absint( $instance['qqt_style']) : 1;
413 | $qqt_followers = isset($instance['qqt_followers']) ? absint( $instance['qqt_followers']) : 1;
414 | ?>
415 | 标题:
416 |
417 | 腾讯微博帐号 (不含@,如:duo-shuo):
418 |
419 |
420 | />
421 | 显示已收听人数
422 |
429 |