Options.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. <?php
  2. namespace Widget;
  3. use Typecho\Common;
  4. use Typecho\Config;
  5. use Typecho\Db;
  6. use Typecho\Router;
  7. use Typecho\Router\Parser;
  8. use Typecho\Widget;
  9. use Typecho\Plugin\Exception as PluginException;
  10. use Typecho\Db\Exception as DbException;
  11. use Typecho\Date;
  12. if (!defined('__TYPECHO_ROOT_DIR__')) {
  13. exit;
  14. }
  15. /**
  16. * 全局选项组件
  17. *
  18. * @property string $feedUrl
  19. * @property string $feedRssUrl
  20. * @property string $feedAtomUrl
  21. * @property string $commentsFeedUrl
  22. * @property string $commentsFeedRssUrl
  23. * @property string $commentsFeedAtomUrl
  24. * @property string $themeUrl
  25. * @property string $xmlRpcUrl
  26. * @property string $index
  27. * @property string $siteUrl
  28. * @property array $routingTable
  29. * @property string $rootUrl
  30. * @property string $pluginUrl
  31. * @property string $pluginDir
  32. * @property string $adminUrl
  33. * @property string $loginUrl
  34. * @property string $originalSiteUrl
  35. * @property string $loginAction
  36. * @property string $registerUrl
  37. * @property string $registerAction
  38. * @property string $profileUrl
  39. * @property string $logoutUrl
  40. * @property string $title
  41. * @property string $description
  42. * @property string $keywords
  43. * @property string $lang
  44. * @property string $theme
  45. * @property string|null $missingTheme
  46. * @property int $pageSize
  47. * @property int $serverTimezone
  48. * @property int $timezone
  49. * @property string $charset
  50. * @property string $contentType
  51. * @property string $generator
  52. * @property string $software
  53. * @property string $version
  54. * @property bool $markdown
  55. * @property bool $xmlrpcMarkdown
  56. * @property array $allowedAttachmentTypes
  57. * @property string $attachmentTypes
  58. * @property int $time
  59. * @property string $frontPage
  60. * @property int $commentsListSize
  61. * @property bool $commentsShowCommentOnly
  62. * @property string $actionTable
  63. * @property string $panelTable
  64. * @property bool $commentsThreaded
  65. * @property bool $defaultAllowComment
  66. * @property bool $defaultAllowPing
  67. * @property bool $defaultAllowFeed
  68. * @property string $commentDateFormat
  69. * @property string $commentsAvatarRating
  70. * @property string $commentsPageDisplay
  71. * @property int $commentsPageSize
  72. * @property string $commentsOrder
  73. * @property bool $commentsMarkdown
  74. * @property bool $commentsShowUrl
  75. * @property bool $commentsUrlNofollow
  76. * @property bool $commentsAvatar
  77. * @property bool $commentsPageBreak
  78. * @property bool $commentsRequireModeration
  79. * @property bool $commentsWhitelist
  80. * @property bool $commentsRequireMail
  81. * @property bool $commentsRequireUrl
  82. * @property bool $commentsCheckReferer
  83. * @property bool $commentsAntiSpam
  84. * @property bool $commentsAutoClose
  85. * @property bool $commentsPostIntervalEnable
  86. * @property string $commentsHTMLTagAllowed
  87. * @property bool $allowRegister
  88. * @property bool $allowXmlRpc
  89. * @property int $postsListSize
  90. * @property bool $feedFullText
  91. * @property int $defaultCategory
  92. * @property bool $frontArchive
  93. * @property array $plugins
  94. * @property string $secret
  95. * @property bool $installed
  96. */
  97. class Options extends Base
  98. {
  99. /**
  100. * 缓存的插件配置
  101. *
  102. * @access private
  103. * @var array
  104. */
  105. private $pluginConfig = [];
  106. /**
  107. * 缓存的个人插件配置
  108. *
  109. * @access private
  110. * @var array
  111. */
  112. private $personalPluginConfig = [];
  113. /**
  114. * @param int $components
  115. */
  116. protected function initComponents(int &$components)
  117. {
  118. $components = self::INIT_NONE;
  119. }
  120. /**
  121. * @param Config $parameter
  122. */
  123. protected function initParameter(Config $parameter)
  124. {
  125. if (!$parameter->isEmpty()) {
  126. $this->row = $this->parameter->toArray();
  127. } else {
  128. $this->db = Db::get();
  129. }
  130. }
  131. /**
  132. * 执行函数
  133. *
  134. * @throws DbException
  135. */
  136. public function execute()
  137. {
  138. if (isset($this->db)) {
  139. $values = $this->db->fetchAll($this->db->select()->from('table.options')
  140. ->where('user = 0'), [$this, 'push']);
  141. // finish install
  142. if (empty($values)) {
  143. $this->response->redirect(defined('__TYPECHO_ADMIN__')
  144. ? '../install.php?step=3' : 'install.php?step=3');
  145. }
  146. }
  147. /** 支持皮肤变量重载 */
  148. if (!empty($this->row['theme:' . $this->row['theme']])) {
  149. $themeOptions = null;
  150. /** 解析变量 */
  151. if ($themeOptions = unserialize($this->row['theme:' . $this->row['theme']])) {
  152. /** 覆盖变量 */
  153. $this->row = array_merge($this->row, $themeOptions);
  154. }
  155. }
  156. $this->stack[] = &$this->row;
  157. /** 动态获取根目录 */
  158. $this->rootUrl = defined('__TYPECHO_ROOT_URL__') ? __TYPECHO_ROOT_URL__ : $this->request->getRequestRoot();
  159. if (defined('__TYPECHO_ADMIN__')) {
  160. /** 识别在admin目录中的情况 */
  161. $adminDir = '/' . trim(defined('__TYPECHO_ADMIN_DIR__') ? __TYPECHO_ADMIN_DIR__ : '/admin/', '/');
  162. $this->rootUrl = substr($this->rootUrl, 0, - strlen($adminDir));
  163. }
  164. /** 初始化站点信息 */
  165. if (defined('__TYPECHO_SITE_URL__')) {
  166. $this->siteUrl = __TYPECHO_SITE_URL__;
  167. } elseif (defined('__TYPECHO_DYNAMIC_SITE_URL__') && __TYPECHO_DYNAMIC_SITE_URL__) {
  168. $this->siteUrl = $this->rootUrl;
  169. }
  170. $this->originalSiteUrl = $this->siteUrl;
  171. $this->siteUrl = Common::url(null, $this->siteUrl);
  172. $this->plugins = unserialize($this->plugins);
  173. /** 动态判断皮肤目录 */
  174. $this->missingTheme = null;
  175. if (!is_dir($this->themeFile($this->theme))) {
  176. $this->missingTheme = $this->theme;
  177. $this->theme = 'default';
  178. }
  179. /** 增加对SSL连接的支持 */
  180. if ($this->request->isSecure() && 0 === strpos($this->siteUrl, 'http://')) {
  181. $this->siteUrl = substr_replace($this->siteUrl, 'https', 0, 4);
  182. }
  183. /** 自动初始化路由表 */
  184. $this->routingTable = unserialize($this->routingTable);
  185. if (isset($this->db) && !isset($this->routingTable[0])) {
  186. /** 解析路由并缓存 */
  187. $parser = new Parser($this->routingTable);
  188. $parsedRoutingTable = $parser->parse();
  189. $this->routingTable = array_merge([$parsedRoutingTable], $this->routingTable);
  190. $this->db->query($this->db->update('table.options')->rows(['value' => serialize($this->routingTable)])
  191. ->where('name = ?', 'routingTable'));
  192. }
  193. }
  194. /**
  195. * 获取皮肤文件
  196. *
  197. * @param string $theme
  198. * @param string $file
  199. * @return string
  200. */
  201. public function themeFile(string $theme, string $file = ''): string
  202. {
  203. return __TYPECHO_ROOT_DIR__ . __TYPECHO_THEME_DIR__ . '/' . trim($theme, './') . '/' . trim($file, './');
  204. }
  205. /**
  206. * 重载父类push函数,将所有变量值压入堆栈
  207. *
  208. * @param array $value 每行的值
  209. * @return array
  210. */
  211. public function push(array $value): array
  212. {
  213. //将行数据按顺序置位
  214. $this->row[$value['name']] = $value['value'];
  215. return $value;
  216. }
  217. /**
  218. * 输出网站路径
  219. *
  220. * @param string|null $path 子路径
  221. */
  222. public function siteUrl(?string $path = null)
  223. {
  224. echo Common::url($path, $this->siteUrl);
  225. }
  226. /**
  227. * 输出解析地址
  228. *
  229. * @param string|null $path 子路径
  230. */
  231. public function index(?string $path = null)
  232. {
  233. echo Common::url($path, $this->index);
  234. }
  235. /**
  236. * 输出模板路径
  237. *
  238. * @param string|null $path 子路径
  239. * @param string|null $theme 模版名称
  240. * @return string | void
  241. */
  242. public function themeUrl(?string $path = null, ?string $theme = null)
  243. {
  244. if (!isset($theme)) {
  245. echo Common::url($path, $this->themeUrl);
  246. } else {
  247. $url = defined('__TYPECHO_THEME_URL__') ? __TYPECHO_THEME_URL__ :
  248. Common::url(__TYPECHO_THEME_DIR__ . '/' . $theme, $this->siteUrl);
  249. return isset($path) ? Common::url($path, $url) : $url;
  250. }
  251. }
  252. /**
  253. * 输出插件路径
  254. *
  255. * @param string|null $path 子路径
  256. */
  257. public function pluginUrl(?string $path = null)
  258. {
  259. echo Common::url($path, $this->pluginUrl);
  260. }
  261. /**
  262. * 获取插件目录
  263. *
  264. * @param string|null $plugin
  265. * @return string
  266. */
  267. public function pluginDir(?string $plugin = null): string
  268. {
  269. return Common::url($plugin, $this->pluginDir);
  270. }
  271. /**
  272. * 输出后台路径
  273. *
  274. * @param string|null $path 子路径
  275. * @param bool $return
  276. * @return void|string
  277. */
  278. public function adminUrl(?string $path = null, bool $return = false)
  279. {
  280. $url = Common::url($path, $this->adminUrl);
  281. if ($return) {
  282. return $url;
  283. }
  284. echo $url;
  285. }
  286. /**
  287. * 获取或输出后台静态文件路径
  288. *
  289. * @param string $type
  290. * @param string|null $file
  291. * @param bool $return
  292. * @return void|string
  293. */
  294. public function adminStaticUrl(string $type, ?string $file = null, bool $return = false)
  295. {
  296. $url = Common::url($type, $this->adminUrl);
  297. if (empty($file)) {
  298. return $url;
  299. }
  300. $url = Common::url($file, $url) . '?v=' . $this->version;
  301. if ($return) {
  302. return $url;
  303. }
  304. echo $url;
  305. }
  306. /**
  307. * 编码输出允许出现在评论中的html标签
  308. */
  309. public function commentsHTMLTagAllowed()
  310. {
  311. echo htmlspecialchars($this->commentsHTMLTagAllowed);
  312. }
  313. /**
  314. * 获取插件系统参数
  315. *
  316. * @param mixed $pluginName 插件名称
  317. * @return mixed
  318. * @throws PluginException
  319. */
  320. public function plugin($pluginName)
  321. {
  322. if (!isset($this->pluginConfig[$pluginName])) {
  323. if (
  324. !empty($this->row['plugin:' . $pluginName])
  325. && false !== ($options = unserialize($this->row['plugin:' . $pluginName]))
  326. ) {
  327. $this->pluginConfig[$pluginName] = new Config($options);
  328. } else {
  329. throw new PluginException(_t('插件%s的配置信息没有找到', $pluginName), 500);
  330. }
  331. }
  332. return $this->pluginConfig[$pluginName];
  333. }
  334. /**
  335. * 获取个人插件系统参数
  336. *
  337. * @param mixed $pluginName 插件名称
  338. *
  339. * @return mixed
  340. * @throws PluginException
  341. */
  342. public function personalPlugin($pluginName)
  343. {
  344. if (!isset($this->personalPluginConfig[$pluginName])) {
  345. if (
  346. !empty($this->row['_plugin:' . $pluginName])
  347. && false !== ($options = unserialize($this->row['_plugin:' . $pluginName]))
  348. ) {
  349. $this->personalPluginConfig[$pluginName] = new Config($options);
  350. } else {
  351. throw new PluginException(_t('插件%s的配置信息没有找到', $pluginName), 500);
  352. }
  353. }
  354. return $this->personalPluginConfig[$pluginName];
  355. }
  356. /**
  357. * RSS2.0
  358. *
  359. * @return string
  360. */
  361. protected function ___feedUrl(): string
  362. {
  363. return Router::url('feed', ['feed' => '/'], $this->index);
  364. }
  365. /**
  366. * RSS1.0
  367. *
  368. * @return string
  369. */
  370. protected function ___feedRssUrl(): string
  371. {
  372. return Router::url('feed', ['feed' => '/rss/'], $this->index);
  373. }
  374. /**
  375. * ATOM1.O
  376. *
  377. * @return string
  378. */
  379. protected function ___feedAtomUrl(): string
  380. {
  381. return Router::url('feed', ['feed' => '/atom/'], $this->index);
  382. }
  383. /**
  384. * 评论RSS2.0聚合
  385. *
  386. * @return string
  387. */
  388. protected function ___commentsFeedUrl(): string
  389. {
  390. return Router::url('feed', ['feed' => '/comments/'], $this->index);
  391. }
  392. /**
  393. * 评论RSS1.0聚合
  394. *
  395. * @return string
  396. */
  397. protected function ___commentsFeedRssUrl(): string
  398. {
  399. return Router::url('feed', ['feed' => '/rss/comments/'], $this->index);
  400. }
  401. /**
  402. * 评论ATOM1.0聚合
  403. *
  404. * @return string
  405. */
  406. protected function ___commentsFeedAtomUrl(): string
  407. {
  408. return Router::url('feed', ['feed' => '/atom/comments/'], $this->index);
  409. }
  410. /**
  411. * xmlrpc api地址
  412. *
  413. * @return string
  414. */
  415. protected function ___xmlRpcUrl(): string
  416. {
  417. return Router::url('do', ['action' => 'xmlrpc'], $this->index);
  418. }
  419. /**
  420. * 获取解析路径前缀
  421. *
  422. * @return string
  423. */
  424. protected function ___index(): string
  425. {
  426. return ($this->rewrite || (defined('__TYPECHO_REWRITE__') && __TYPECHO_REWRITE__))
  427. ? $this->rootUrl : Common::url('index.php', $this->rootUrl);
  428. }
  429. /**
  430. * 获取模板路径
  431. *
  432. * @return string
  433. */
  434. protected function ___themeUrl(): string
  435. {
  436. return $this->themeUrl(null, $this->theme);
  437. }
  438. /**
  439. * 获取插件路径
  440. *
  441. * @return string
  442. */
  443. protected function ___pluginUrl(): string
  444. {
  445. return defined('__TYPECHO_PLUGIN_URL__') ? __TYPECHO_PLUGIN_URL__ :
  446. Common::url(__TYPECHO_PLUGIN_DIR__, $this->siteUrl);
  447. }
  448. /**
  449. * @return string
  450. */
  451. protected function ___pluginDir(): string
  452. {
  453. return Common::url(__TYPECHO_PLUGIN_DIR__, __TYPECHO_ROOT_DIR__);
  454. }
  455. /**
  456. * 获取后台路径
  457. *
  458. * @return string
  459. */
  460. protected function ___adminUrl(): string
  461. {
  462. return Common::url(defined('__TYPECHO_ADMIN_DIR__') ?
  463. __TYPECHO_ADMIN_DIR__ : '/admin/', $this->rootUrl);
  464. }
  465. /**
  466. * 获取登录地址
  467. *
  468. * @return string
  469. */
  470. protected function ___loginUrl(): string
  471. {
  472. return Common::url('login.php', $this->adminUrl);
  473. }
  474. /**
  475. * 获取登录提交地址
  476. *
  477. * @return string
  478. */
  479. protected function ___loginAction(): string
  480. {
  481. return Security::alloc()->getTokenUrl(
  482. Router::url(
  483. 'do',
  484. ['action' => 'login', 'widget' => 'Login'],
  485. Common::url('index.php', $this->rootUrl)
  486. )
  487. );
  488. }
  489. /**
  490. * 获取注册地址
  491. *
  492. * @return string
  493. */
  494. protected function ___registerUrl(): string
  495. {
  496. return Common::url('register.php', $this->adminUrl);
  497. }
  498. /**
  499. * 获取登录提交地址
  500. *
  501. * @return string
  502. * @throws Widget\Exception
  503. */
  504. protected function ___registerAction(): string
  505. {
  506. return Security::alloc()->getTokenUrl(
  507. Router::url('do', ['action' => 'register', 'widget' => 'Register'], $this->index)
  508. );
  509. }
  510. /**
  511. * 获取个人档案地址
  512. *
  513. * @return string
  514. */
  515. protected function ___profileUrl(): string
  516. {
  517. return Common::url('profile.php', $this->adminUrl);
  518. }
  519. /**
  520. * 获取登出地址
  521. *
  522. * @return string
  523. */
  524. protected function ___logoutUrl(): string
  525. {
  526. return Security::alloc()->getTokenUrl(
  527. Common::url('/action/logout', $this->index)
  528. );
  529. }
  530. /**
  531. * 获取系统时区
  532. *
  533. * @return integer
  534. */
  535. protected function ___serverTimezone(): int
  536. {
  537. return Date::$serverTimezoneOffset;
  538. }
  539. /**
  540. * 获取GMT标准时间
  541. *
  542. * @return integer
  543. * @deprecated
  544. */
  545. protected function ___gmtTime(): int
  546. {
  547. return Date::gmtTime();
  548. }
  549. /**
  550. * 获取时间
  551. *
  552. * @return integer
  553. * @deprecated
  554. */
  555. protected function ___time(): int
  556. {
  557. return Date::time();
  558. }
  559. /**
  560. * 获取格式
  561. *
  562. * @return string
  563. */
  564. protected function ___contentType(): string
  565. {
  566. return $this->contentType ?? 'text/html';
  567. }
  568. /**
  569. * 软件名称
  570. *
  571. * @return string
  572. */
  573. protected function ___software(): string
  574. {
  575. [$software, $version] = explode(' ', $this->generator);
  576. return $software;
  577. }
  578. /**
  579. * 软件版本
  580. *
  581. * @return string
  582. */
  583. protected function ___version(): string
  584. {
  585. [$software, $version] = explode(' ', $this->generator);
  586. $pos = strpos($version, '/');
  587. // fix for old version
  588. if ($pos !== false) {
  589. $version = substr($version, 0, $pos);
  590. }
  591. return $version;
  592. }
  593. /**
  594. * 允许上传的文件类型
  595. *
  596. * @return array
  597. */
  598. protected function ___allowedAttachmentTypes(): array
  599. {
  600. $attachmentTypesResult = [];
  601. if (null != $this->attachmentTypes) {
  602. $attachmentTypes = str_replace(
  603. ['@image@', '@media@', '@doc@'],
  604. [
  605. 'gif,jpg,jpeg,png,tiff,bmp,webp', 'mp3,mp4,mov,wmv,wma,rmvb,rm,avi,flv,ogg,oga,ogv',
  606. 'txt,doc,docx,xls,xlsx,ppt,pptx,zip,rar,pdf'
  607. ],
  608. $this->attachmentTypes
  609. );
  610. $attachmentTypesResult = array_unique(array_map('trim', preg_split("/(,|\.)/", $attachmentTypes)));
  611. }
  612. return $attachmentTypesResult;
  613. }
  614. }