Archive.php 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233
  1. <?php
  2. namespace Widget;
  3. use Typecho\Common;
  4. use Typecho\Config;
  5. use Typecho\Cookie;
  6. use Typecho\Db;
  7. use Typecho\Db\Query;
  8. use Typecho\Feed;
  9. use Typecho\Router;
  10. use Typecho\Widget\Exception as WidgetException;
  11. use Typecho\Widget\Helper\PageNavigator;
  12. use Typecho\Widget\Helper\PageNavigator\Classic;
  13. use Typecho\Widget\Helper\PageNavigator\Box;
  14. use Widget\Base\Contents;
  15. use Widget\Base\Metas;
  16. use Widget\Comments\Ping;
  17. use Widget\Comments\Recent;
  18. use Widget\Contents\Attachment\Related;
  19. use Widget\Contents\Related\Author;
  20. use Widget\Metas\Category\Rows;
  21. if (!defined('__TYPECHO_ROOT_DIR__')) {
  22. exit;
  23. }
  24. /**
  25. * 内容的文章基类
  26. * 定义的css类
  27. * p.more:阅读全文链接所属段落
  28. *
  29. * @package Widget
  30. */
  31. class Archive extends Contents
  32. {
  33. /**
  34. * 调用的风格文件
  35. *
  36. * @var string
  37. */
  38. private $themeFile;
  39. /**
  40. * 风格目录
  41. *
  42. * @var string
  43. */
  44. private $themeDir;
  45. /**
  46. * 分页计算对象
  47. *
  48. * @var Query
  49. */
  50. private $countSql;
  51. /**
  52. * 所有文章个数
  53. *
  54. * @var integer
  55. */
  56. private $total = false;
  57. /**
  58. * 标记是否为从外部调用
  59. *
  60. * @var boolean
  61. */
  62. private $invokeFromOutside = false;
  63. /**
  64. * 是否由聚合调用
  65. *
  66. * @var boolean
  67. */
  68. private $invokeByFeed = false;
  69. /**
  70. * 当前页
  71. *
  72. * @var integer
  73. */
  74. private $currentPage;
  75. /**
  76. * 生成分页的内容
  77. *
  78. * @var array
  79. */
  80. private $pageRow = [];
  81. /**
  82. * 聚合器对象
  83. *
  84. * @var Feed
  85. */
  86. private $feed;
  87. /**
  88. * RSS 2.0聚合地址
  89. *
  90. * @var string
  91. */
  92. private $feedUrl;
  93. /**
  94. * RSS 1.0聚合地址
  95. *
  96. * @var string
  97. */
  98. private $feedRssUrl;
  99. /**
  100. * ATOM 聚合地址
  101. *
  102. * @var string
  103. */
  104. private $feedAtomUrl;
  105. /**
  106. * 本页关键字
  107. *
  108. * @var string
  109. */
  110. private $keywords;
  111. /**
  112. * 本页描述
  113. *
  114. * @var string
  115. */
  116. private $description;
  117. /**
  118. * 聚合类型
  119. *
  120. * @var string
  121. */
  122. private $feedType;
  123. /**
  124. * 聚合类型
  125. *
  126. * @var string
  127. */
  128. private $feedContentType;
  129. /**
  130. * 当前feed地址
  131. *
  132. * @var string
  133. */
  134. private $currentFeedUrl;
  135. /**
  136. * 归档标题
  137. *
  138. * @var string
  139. */
  140. private $archiveTitle = null;
  141. /**
  142. * 归档地址
  143. *
  144. * @var string|null
  145. */
  146. private $archiveUrl = null;
  147. /**
  148. * 归档类型
  149. *
  150. * @var string
  151. */
  152. private $archiveType = 'index';
  153. /**
  154. * 是否为单一归档
  155. *
  156. * @var string
  157. */
  158. private $archiveSingle = false;
  159. /**
  160. * 是否为自定义首页, 主要为了标记自定义首页的情况
  161. *
  162. * (default value: false)
  163. *
  164. * @var boolean
  165. * @access private
  166. */
  167. private $makeSinglePageAsFrontPage = false;
  168. /**
  169. * 归档缩略名
  170. *
  171. * @access private
  172. * @var string
  173. */
  174. private $archiveSlug;
  175. /**
  176. * 设置分页对象
  177. *
  178. * @access private
  179. * @var PageNavigator
  180. */
  181. private $pageNav;
  182. /**
  183. * @param Config $parameter
  184. * @throws \Exception
  185. */
  186. protected function initParameter(Config $parameter)
  187. {
  188. $parameter->setDefault([
  189. 'pageSize' => $this->options->pageSize,
  190. 'type' => null,
  191. 'checkPermalink' => true,
  192. 'preview' => false
  193. ]);
  194. /** 用于判断是路由调用还是外部调用 */
  195. if (null == $parameter->type) {
  196. $parameter->type = Router::$current;
  197. } else {
  198. $this->invokeFromOutside = true;
  199. }
  200. /** 用于判断是否为feed调用 */
  201. if ($parameter->isFeed) {
  202. $this->invokeByFeed = true;
  203. }
  204. /** 初始化皮肤路径 */
  205. $this->themeDir = rtrim($this->options->themeFile($this->options->theme), '/') . '/';
  206. /** 处理feed模式 **/
  207. if ('feed' == $parameter->type) {
  208. $this->currentFeedUrl = '';
  209. /** 判断聚合类型 */
  210. switch (true) {
  211. case 0 === strpos($this->request->feed, '/rss/') || '/rss' == $this->request->feed:
  212. /** 如果是RSS1标准 */
  213. $this->request->feed = substr($this->request->feed, 4);
  214. $this->feedType = Feed::RSS1;
  215. $this->currentFeedUrl = $this->options->feedRssUrl;
  216. $this->feedContentType = 'application/rdf+xml';
  217. break;
  218. case 0 === strpos($this->request->feed, '/atom/') || '/atom' == $this->request->feed:
  219. /** 如果是ATOM标准 */
  220. $this->request->feed = substr($this->request->feed, 5);
  221. $this->feedType = Feed::ATOM1;
  222. $this->currentFeedUrl = $this->options->feedAtomUrl;
  223. $this->feedContentType = 'application/atom+xml';
  224. break;
  225. default:
  226. $this->feedType = Feed::RSS2;
  227. $this->currentFeedUrl = $this->options->feedUrl;
  228. $this->feedContentType = 'application/rss+xml';
  229. break;
  230. }
  231. $feedQuery = $this->request->feed;
  232. //$parameter->type = Router::$current;
  233. //$this->request->setParams($params);
  234. if ('/comments/' == $feedQuery || '/comments' == $feedQuery) {
  235. /** 专为feed使用的hack */
  236. $parameter->type = 'comments';
  237. $this->options->feedUrl = $this->options->commentsFeedUrl;
  238. $this->options->feedRssUrl = $this->options->commentsFeedRssUrl;
  239. $this->options->feedAtomUrl = $this->options->commentsFeedAtomUrl;
  240. } else {
  241. $matched = Router::match($this->request->feed, 'pageSize=10&isFeed=1');
  242. if ($matched instanceof Archive) {
  243. $this->import($matched);
  244. } else {
  245. throw new WidgetException(_t('聚合页不存在'), 404);
  246. }
  247. }
  248. /** 初始化聚合器 */
  249. $this->setFeed(new Feed(Common::VERSION, $this->feedType, $this->options->charset, _t('zh-CN')));
  250. /** 默认输出10则文章 **/
  251. $parameter->pageSize = 10;
  252. }
  253. }
  254. /**
  255. * 增加标题
  256. * @param string $archiveTitle 标题
  257. */
  258. public function addArchiveTitle(string $archiveTitle)
  259. {
  260. $current = $this->getArchiveTitle();
  261. $current[] = $archiveTitle;
  262. $this->setArchiveTitle($current);
  263. }
  264. /**
  265. * @return string
  266. */
  267. public function getArchiveTitle(): ?string
  268. {
  269. return $this->archiveTitle;
  270. }
  271. /**
  272. * @param string $archiveTitle the $archiveTitle to set
  273. */
  274. public function setArchiveTitle(string $archiveTitle)
  275. {
  276. $this->archiveTitle = $archiveTitle;
  277. }
  278. /**
  279. * 获取分页对象
  280. * @return array
  281. */
  282. public function getPageRow(): array
  283. {
  284. return $this->pageRow;
  285. }
  286. /**
  287. * 设置分页对象
  288. * @param array $pageRow
  289. */
  290. public function setPageRow(array $pageRow)
  291. {
  292. $this->pageRow = $pageRow;
  293. }
  294. /**
  295. * @return string|null
  296. */
  297. public function getArchiveSlug(): ?string
  298. {
  299. return $this->archiveSlug;
  300. }
  301. /**
  302. * @param string $archiveSlug the $archiveSlug to set
  303. */
  304. public function setArchiveSlug(string $archiveSlug)
  305. {
  306. $this->archiveSlug = $archiveSlug;
  307. }
  308. /**
  309. * @return string|null
  310. */
  311. public function getArchiveSingle(): ?string
  312. {
  313. return $this->archiveSingle;
  314. }
  315. /**
  316. * @param string $archiveSingle the $archiveSingle to set
  317. */
  318. public function setArchiveSingle(string $archiveSingle)
  319. {
  320. $this->archiveSingle = $archiveSingle;
  321. }
  322. /**
  323. * @return string|null
  324. */
  325. public function getArchiveType(): ?string
  326. {
  327. return $this->archiveType;
  328. }
  329. /**
  330. * @param string $archiveType the $archiveType to set
  331. */
  332. public function setArchiveType(string $archiveType)
  333. {
  334. $this->archiveType = $archiveType;
  335. }
  336. /**
  337. * @return string|null
  338. */
  339. public function getArchiveUrl(): ?string
  340. {
  341. return $this->archiveUrl;
  342. }
  343. /**
  344. * @param string|null $archiveUrl
  345. */
  346. public function setArchiveUrl(?string $archiveUrl): void
  347. {
  348. $this->archiveUrl = $archiveUrl;
  349. }
  350. /**
  351. * @return string|null
  352. */
  353. public function getFeedType(): ?string
  354. {
  355. return $this->feedType;
  356. }
  357. /**
  358. * @param string $feedType the $feedType to set
  359. */
  360. public function setFeedType(string $feedType)
  361. {
  362. $this->feedType = $feedType;
  363. }
  364. /**
  365. * @return string|null
  366. */
  367. public function getDescription(): ?string
  368. {
  369. return $this->description;
  370. }
  371. /**
  372. * @param string $description the $description to set
  373. */
  374. public function setDescription(string $description)
  375. {
  376. $this->description = $description;
  377. }
  378. /**
  379. * @return string|null
  380. */
  381. public function getKeywords(): ?string
  382. {
  383. return $this->keywords;
  384. }
  385. /**
  386. * @param string $keywords the $keywords to set
  387. */
  388. public function setKeywords(string $keywords)
  389. {
  390. $this->keywords = $keywords;
  391. }
  392. /**
  393. * @return string
  394. */
  395. public function getFeedAtomUrl(): string
  396. {
  397. return $this->feedAtomUrl;
  398. }
  399. /**
  400. * @param string $feedAtomUrl the $feedAtomUrl to set
  401. */
  402. public function setFeedAtomUrl(string $feedAtomUrl)
  403. {
  404. $this->feedAtomUrl = $feedAtomUrl;
  405. }
  406. /**
  407. * @return string
  408. */
  409. public function getFeedRssUrl(): string
  410. {
  411. return $this->feedRssUrl;
  412. }
  413. /**
  414. * @param string $feedRssUrl the $feedRssUrl to set
  415. */
  416. public function setFeedRssUrl(string $feedRssUrl)
  417. {
  418. $this->feedRssUrl = $feedRssUrl;
  419. }
  420. /**
  421. * @return string
  422. */
  423. public function getFeedUrl(): string
  424. {
  425. return $this->feedUrl;
  426. }
  427. /**
  428. * @param string $feedUrl the $feedUrl to set
  429. */
  430. public function setFeedUrl(string $feedUrl)
  431. {
  432. $this->feedUrl = $feedUrl;
  433. }
  434. /**
  435. * @return Feed
  436. */
  437. public function getFeed(): Feed
  438. {
  439. return $this->feed;
  440. }
  441. /**
  442. * @param Feed $feed the $feed to set
  443. */
  444. public function setFeed(Feed $feed)
  445. {
  446. $this->feed = $feed;
  447. }
  448. /**
  449. * @return Query|null
  450. */
  451. public function getCountSql(): ?Query
  452. {
  453. return $this->countSql;
  454. }
  455. /**
  456. * @param Query $countSql the $countSql to set
  457. */
  458. public function setCountSql($countSql)
  459. {
  460. $this->countSql = $countSql;
  461. }
  462. /**
  463. * @return int
  464. */
  465. public function getCurrentPage(): int
  466. {
  467. return $this->currentPage;
  468. }
  469. /**
  470. * _currentPage
  471. *
  472. * @return int
  473. */
  474. public function ____currentPage(): int
  475. {
  476. return $this->getCurrentPage();
  477. }
  478. /**
  479. * 获取页数
  480. *
  481. * @return integer
  482. */
  483. public function getTotalPage(): int
  484. {
  485. return ceil($this->getTotal() / $this->parameter->pageSize);
  486. }
  487. /**
  488. * @return int
  489. * @throws Db\Exception
  490. */
  491. public function getTotal(): int
  492. {
  493. if (false === $this->total) {
  494. $this->total = $this->size($this->countSql);
  495. }
  496. return $this->total;
  497. }
  498. /**
  499. * @param int $total the $total to set
  500. */
  501. public function setTotal(int $total)
  502. {
  503. $this->total = $total;
  504. }
  505. /**
  506. * @return string|null
  507. */
  508. public function getThemeFile(): ?string
  509. {
  510. return $this->themeFile;
  511. }
  512. /**
  513. * @param string $themeFile the $themeFile to set
  514. */
  515. public function setThemeFile(string $themeFile)
  516. {
  517. $this->themeFile = $themeFile;
  518. }
  519. /**
  520. * @return string|null
  521. */
  522. public function getThemeDir(): ?string
  523. {
  524. return $this->themeDir;
  525. }
  526. /**
  527. * @param string $themeDir the $themeDir to set
  528. */
  529. public function setThemeDir(string $themeDir)
  530. {
  531. $this->themeDir = $themeDir;
  532. }
  533. /**
  534. * 执行函数
  535. */
  536. public function execute()
  537. {
  538. /** 避免重复取数据 */
  539. if ($this->have()) {
  540. return;
  541. }
  542. $handles = [
  543. 'index' => 'indexHandle',
  544. 'index_page' => 'indexHandle',
  545. 'archive' => 'archiveEmptyHandle',
  546. 'archive_page' => 'archiveEmptyHandle',
  547. 404 => 'error404Handle',
  548. 'single' => 'singleHandle',
  549. 'page' => 'singleHandle',
  550. 'post' => 'singleHandle',
  551. 'attachment' => 'singleHandle',
  552. 'comment_page' => 'singleHandle',
  553. 'category' => 'categoryHandle',
  554. 'category_page' => 'categoryHandle',
  555. 'tag' => 'tagHandle',
  556. 'tag_page' => 'tagHandle',
  557. 'author' => 'authorHandle',
  558. 'author_page' => 'authorHandle',
  559. 'archive_year' => 'dateHandle',
  560. 'archive_year_page' => 'dateHandle',
  561. 'archive_month' => 'dateHandle',
  562. 'archive_month_page' => 'dateHandle',
  563. 'archive_day' => 'dateHandle',
  564. 'archive_day_page' => 'dateHandle',
  565. 'search' => 'searchHandle',
  566. 'search_page' => 'searchHandle'
  567. ];
  568. /** 处理搜索结果跳转 */
  569. if (isset($this->request->s)) {
  570. $filterKeywords = $this->request->filter('search')->get('s');
  571. /** 跳转到搜索页 */
  572. if (null != $filterKeywords) {
  573. $this->response->redirect(
  574. Router::url('search', ['keywords' => urlencode($filterKeywords)], $this->options->index)
  575. );
  576. }
  577. }
  578. /** 自定义首页功能 */
  579. $frontPage = $this->options->frontPage;
  580. if (!$this->invokeByFeed && ('index' == $this->parameter->type || 'index_page' == $this->parameter->type)) {
  581. //显示某个页面
  582. if (0 === strpos($frontPage, 'page:')) {
  583. // 对某些变量做hack
  584. $this->request->setParam('cid', intval(substr($frontPage, 5)));
  585. $this->parameter->type = 'page';
  586. $this->makeSinglePageAsFrontPage = true;
  587. } elseif (0 === strpos($frontPage, 'file:')) {
  588. // 显示某个文件
  589. $this->setThemeFile(substr($frontPage, 5));
  590. return;
  591. }
  592. }
  593. if ('recent' != $frontPage && $this->options->frontArchive) {
  594. $handles['archive'] = 'indexHandle';
  595. $handles['archive_page'] = 'indexHandle';
  596. $this->archiveType = 'front';
  597. }
  598. /** 初始化分页变量 */
  599. $this->currentPage = $this->request->filter('int')->page ?? 1;
  600. $hasPushed = false;
  601. /** select初始化 */
  602. $select = self::pluginHandle()->trigger($selectPlugged)->select($this);
  603. /** 定时发布功能 */
  604. if (!$selectPlugged) {
  605. if ($this->parameter->preview) {
  606. $select = $this->select();
  607. } else {
  608. if ('post' == $this->parameter->type || 'page' == $this->parameter->type) {
  609. if ($this->user->hasLogin()) {
  610. $select = $this->select()->where(
  611. 'table.contents.status = ? OR table.contents.status = ?
  612. OR (table.contents.status = ? AND table.contents.authorId = ?)',
  613. 'publish',
  614. 'hidden',
  615. 'private',
  616. $this->user->uid
  617. );
  618. } else {
  619. $select = $this->select()->where(
  620. 'table.contents.status = ? OR table.contents.status = ?',
  621. 'publish',
  622. 'hidden'
  623. );
  624. }
  625. } else {
  626. if ($this->user->hasLogin()) {
  627. $select = $this->select()->where(
  628. 'table.contents.status = ? OR (table.contents.status = ? AND table.contents.authorId = ?)',
  629. 'publish',
  630. 'private',
  631. $this->user->uid
  632. );
  633. } else {
  634. $select = $this->select()->where('table.contents.status = ?', 'publish');
  635. }
  636. }
  637. $select->where('table.contents.created < ?', $this->options->time);
  638. }
  639. }
  640. /** handle初始化 */
  641. self::pluginHandle()->handleInit($this, $select);
  642. /** 初始化其它变量 */
  643. $this->feedUrl = $this->options->feedUrl;
  644. $this->feedRssUrl = $this->options->feedRssUrl;
  645. $this->feedAtomUrl = $this->options->feedAtomUrl;
  646. $this->keywords = $this->options->keywords;
  647. $this->description = $this->options->description;
  648. $this->archiveUrl = $this->options->siteUrl;
  649. if (isset($handles[$this->parameter->type])) {
  650. $handle = $handles[$this->parameter->type];
  651. $this->{$handle}($select, $hasPushed);
  652. } else {
  653. $hasPushed = self::pluginHandle()->handle($this->parameter->type, $this, $select);
  654. }
  655. /** 初始化皮肤函数 */
  656. $functionsFile = $this->themeDir . 'functions.php';
  657. if (
  658. (!$this->invokeFromOutside || $this->parameter->type == 404 || $this->parameter->preview)
  659. && file_exists($functionsFile)
  660. ) {
  661. require_once $functionsFile;
  662. if (function_exists('themeInit')) {
  663. themeInit($this);
  664. }
  665. }
  666. /** 如果已经提前压入则直接返回 */
  667. if ($hasPushed) {
  668. return;
  669. }
  670. /** 仅输出文章 */
  671. $this->countSql = clone $select;
  672. $select->order('table.contents.created', Db::SORT_DESC)
  673. ->page($this->currentPage, $this->parameter->pageSize);
  674. $this->query($select);
  675. /** 处理超出分页的情况 */
  676. if ($this->currentPage > 1 && !$this->have()) {
  677. throw new WidgetException(_t('请求的地址不存在'), 404);
  678. }
  679. }
  680. /**
  681. * 重载select
  682. *
  683. * @return Query
  684. * @throws Db\Exception
  685. */
  686. public function select(): Query
  687. {
  688. if ($this->invokeByFeed) {
  689. // 对feed输出加入限制条件
  690. return parent::select()->where('table.contents.allowFeed = ?', 1)
  691. ->where("table.contents.password IS NULL OR table.contents.password = ''");
  692. } else {
  693. return parent::select();
  694. }
  695. }
  696. /**
  697. * 输出文章内容
  698. *
  699. * @param string $more 文章截取后缀
  700. */
  701. public function content($more = null)
  702. {
  703. parent::content($this->is('single') ? false : $more);
  704. }
  705. /**
  706. * 输出分页
  707. *
  708. * @param string $prev 上一页文字
  709. * @param string $next 下一页文字
  710. * @param int $splitPage 分割范围
  711. * @param string $splitWord 分割字符
  712. * @param string|array $template 展现配置信息
  713. * @throws Db\Exception|WidgetException
  714. */
  715. public function pageNav(
  716. string $prev = '&laquo;',
  717. string $next = '&raquo;',
  718. int $splitPage = 3,
  719. string $splitWord = '...',
  720. $template = ''
  721. ) {
  722. if ($this->have()) {
  723. $hasNav = false;
  724. $default = [
  725. 'wrapTag' => 'ol',
  726. 'wrapClass' => 'page-navigator'
  727. ];
  728. if (is_string($template)) {
  729. parse_str($template, $config);
  730. } else {
  731. $config = $template ?: [];
  732. }
  733. $template = array_merge($default, $config);
  734. $total = $this->getTotal();
  735. $query = Router::url(
  736. $this->parameter->type .
  737. (false === strpos($this->parameter->type, '_page') ? '_page' : null),
  738. $this->pageRow,
  739. $this->options->index
  740. );
  741. self::pluginHandle()->trigger($hasNav)->pageNav(
  742. $this->currentPage,
  743. $total,
  744. $this->parameter->pageSize,
  745. $prev,
  746. $next,
  747. $splitPage,
  748. $splitWord,
  749. $template,
  750. $query
  751. );
  752. if (!$hasNav && $total > $this->parameter->pageSize) {
  753. /** 使用盒状分页 */
  754. $nav = new Box(
  755. $total,
  756. $this->currentPage,
  757. $this->parameter->pageSize,
  758. $query
  759. );
  760. echo '<' . $template['wrapTag'] . (empty($template['wrapClass'])
  761. ? '' : ' class="' . $template['wrapClass'] . '"') . '>';
  762. $nav->render($prev, $next, $splitPage, $splitWord, $template);
  763. echo '</' . $template['wrapTag'] . '>';
  764. }
  765. }
  766. }
  767. /**
  768. * 前一页
  769. *
  770. * @param string $word 链接标题
  771. * @param string $page 页面链接
  772. * @throws Db\Exception|WidgetException
  773. */
  774. public function pageLink(string $word = '&laquo; Previous Entries', string $page = 'prev')
  775. {
  776. if ($this->have()) {
  777. if (empty($this->pageNav)) {
  778. $query = Router::url(
  779. $this->parameter->type .
  780. (false === strpos($this->parameter->type, '_page') ? '_page' : null),
  781. $this->pageRow,
  782. $this->options->index
  783. );
  784. /** 使用盒状分页 */
  785. $this->pageNav = new Classic(
  786. $this->getTotal(),
  787. $this->currentPage,
  788. $this->parameter->pageSize,
  789. $query
  790. );
  791. }
  792. $this->pageNav->{$page}($word);
  793. }
  794. }
  795. /**
  796. * 获取评论归档对象
  797. *
  798. * @access public
  799. * @return \Widget\Comments\Archive
  800. */
  801. public function comments(): \Widget\Comments\Archive
  802. {
  803. $parameter = [
  804. 'parentId' => $this->hidden ? 0 : $this->cid,
  805. 'parentContent' => $this->row,
  806. 'respondId' => $this->respondId,
  807. 'commentPage' => $this->request->filter('int')->commentPage,
  808. 'allowComment' => $this->allow('comment')
  809. ];
  810. return \Widget\Comments\Archive::alloc($parameter);
  811. }
  812. /**
  813. * 获取回响归档对象
  814. *
  815. * @return Ping
  816. */
  817. public function pings(): Ping
  818. {
  819. return Ping::alloc([
  820. 'parentId' => $this->hidden ? 0 : $this->cid,
  821. 'parentContent' => $this->row,
  822. 'allowPing' => $this->allow('ping')
  823. ]);
  824. }
  825. /**
  826. * 获取附件对象
  827. *
  828. * @param integer $limit 最大个数
  829. * @param integer $offset 重新
  830. * @return Related
  831. */
  832. public function attachments(int $limit = 0, int $offset = 0): Related
  833. {
  834. return Related::allocWithAlias($this->cid . '-' . uniqid(), [
  835. 'parentId' => $this->cid,
  836. 'limit' => $limit,
  837. 'offset' => $offset
  838. ]);
  839. }
  840. /**
  841. * 显示下一个内容的标题链接
  842. *
  843. * @param string $format 格式
  844. * @param string|null $default 如果没有下一篇,显示的默认文字
  845. * @param array $custom 定制化样式
  846. */
  847. public function theNext(string $format = '%s', ?string $default = null, array $custom = [])
  848. {
  849. $content = $this->db->fetchRow($this->select()->where(
  850. 'table.contents.created > ? AND table.contents.created < ?',
  851. $this->created,
  852. $this->options->time
  853. )
  854. ->where('table.contents.status = ?', 'publish')
  855. ->where('table.contents.type = ?', $this->type)
  856. ->where("table.contents.password IS NULL OR table.contents.password = ''")
  857. ->order('table.contents.created', Db::SORT_ASC)
  858. ->limit(1));
  859. if ($content) {
  860. $content = $this->filter($content);
  861. $default = [
  862. 'title' => null,
  863. 'tagClass' => null
  864. ];
  865. $custom = array_merge($default, $custom);
  866. extract($custom);
  867. $linkText = empty($title) ? $content['title'] : $title;
  868. $linkClass = empty($tagClass) ? '' : 'class="' . $tagClass . '" ';
  869. $link = '<a ' . $linkClass . 'href="' . $content['permalink']
  870. . '" title="' . $content['title'] . '">' . $linkText . '</a>';
  871. printf($format, $link);
  872. } else {
  873. echo $default;
  874. }
  875. }
  876. /**
  877. * 显示上一个内容的标题链接
  878. *
  879. * @access public
  880. * @param string $format 格式
  881. * @param string $default 如果没有上一篇,显示的默认文字
  882. * @param array $custom 定制化样式
  883. * @return void
  884. */
  885. public function thePrev($format = '%s', $default = null, $custom = [])
  886. {
  887. $content = $this->db->fetchRow($this->select()->where('table.contents.created < ?', $this->created)
  888. ->where('table.contents.status = ?', 'publish')
  889. ->where('table.contents.type = ?', $this->type)
  890. ->where("table.contents.password IS NULL OR table.contents.password = ''")
  891. ->order('table.contents.created', Db::SORT_DESC)
  892. ->limit(1));
  893. if ($content) {
  894. $content = $this->filter($content);
  895. $default = [
  896. 'title' => null,
  897. 'tagClass' => null
  898. ];
  899. $custom = array_merge($default, $custom);
  900. extract($custom);
  901. $linkText = empty($title) ? $content['title'] : $title;
  902. $linkClass = empty($tagClass) ? '' : 'class="' . $tagClass . '" ';
  903. $link = '<a ' . $linkClass . 'href="' . $content['permalink'] . '" title="' . $content['title'] . '">' . $linkText . '</a>';
  904. printf($format, $link);
  905. } else {
  906. echo $default;
  907. }
  908. }
  909. /**
  910. * 获取关联内容组件
  911. *
  912. * @param integer $limit 输出数量
  913. * @param string|null $type 关联类型
  914. * @return Contents
  915. */
  916. public function related(int $limit = 5, ?string $type = null): Contents
  917. {
  918. $type = strtolower($type ?? '');
  919. switch ($type) {
  920. case 'author':
  921. /** 如果访问权限被设置为禁止,则tag会被置为空 */
  922. return Author::alloc(
  923. ['cid' => $this->cid, 'type' => $this->type, 'author' => $this->author->uid, 'limit' => $limit]
  924. );
  925. default:
  926. /** 如果访问权限被设置为禁止,则tag会被置为空 */
  927. return \Widget\Contents\Related::alloc(
  928. ['cid' => $this->cid, 'type' => $this->type, 'tags' => $this->tags, 'limit' => $limit]
  929. );
  930. }
  931. }
  932. /**
  933. * 输出头部元数据
  934. *
  935. * @param string|null $rule 规则
  936. */
  937. public function header(?string $rule = null)
  938. {
  939. $rules = [];
  940. $allows = [
  941. 'description' => htmlspecialchars($this->description ?? ''),
  942. 'keywords' => htmlspecialchars($this->keywords ?? ''),
  943. 'generator' => $this->options->generator,
  944. 'template' => $this->options->theme,
  945. 'pingback' => $this->options->xmlRpcUrl,
  946. 'xmlrpc' => $this->options->xmlRpcUrl . '?rsd',
  947. 'wlw' => $this->options->xmlRpcUrl . '?wlw',
  948. 'rss2' => $this->feedUrl,
  949. 'rss1' => $this->feedRssUrl,
  950. 'commentReply' => 1,
  951. 'antiSpam' => 1,
  952. 'atom' => $this->feedAtomUrl
  953. ];
  954. /** 头部是否输出聚合 */
  955. $allowFeed = !$this->is('single') || $this->allow('feed') || $this->makeSinglePageAsFrontPage;
  956. if (!empty($rule)) {
  957. parse_str($rule, $rules);
  958. $allows = array_merge($allows, $rules);
  959. }
  960. $allows = self::pluginHandle()->headerOptions($allows, $this);
  961. $title = (empty($this->archiveTitle) ? '' : $this->archiveTitle . ' &raquo; ') . $this->options->title;
  962. $header = '';
  963. if (!empty($allows['description'])) {
  964. $header .= '<meta name="description" content="' . $allows['description'] . '" />' . "\n";
  965. }
  966. if (!empty($allows['keywords'])) {
  967. $header .= '<meta name="keywords" content="' . $allows['keywords'] . '" />' . "\n";
  968. }
  969. if (!empty($allows['generator'])) {
  970. $header .= '<meta name="generator" content="' . $allows['generator'] . '" />' . "\n";
  971. }
  972. if (!empty($allows['template'])) {
  973. $header .= '<meta name="template" content="' . $allows['template'] . '" />' . "\n";
  974. }
  975. if (!empty($allows['pingback']) && 2 == $this->options->allowXmlRpc) {
  976. $header .= '<link rel="pingback" href="' . $allows['pingback'] . '" />' . "\n";
  977. }
  978. if (!empty($allows['xmlrpc']) && 0 < $this->options->allowXmlRpc) {
  979. $header .= '<link rel="EditURI" type="application/rsd+xml" title="RSD" href="'
  980. . $allows['xmlrpc'] . '" />' . "\n";
  981. }
  982. if (!empty($allows['wlw']) && 0 < $this->options->allowXmlRpc) {
  983. $header .= '<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="'
  984. . $allows['wlw'] . '" />' . "\n";
  985. }
  986. if (!empty($allows['rss2']) && $allowFeed) {
  987. $header .= '<link rel="alternate" type="application/rss+xml" title="'
  988. . $title . ' &raquo; RSS 2.0" href="' . $allows['rss2'] . '" />' . "\n";
  989. }
  990. if (!empty($allows['rss1']) && $allowFeed) {
  991. $header .= '<link rel="alternate" type="application/rdf+xml" title="'
  992. . $title . ' &raquo; RSS 1.0" href="' . $allows['rss1'] . '" />' . "\n";
  993. }
  994. if (!empty($allows['atom']) && $allowFeed) {
  995. $header .= '<link rel="alternate" type="application/atom+xml" title="'
  996. . $title . ' &raquo; ATOM 1.0" href="' . $allows['atom'] . '" />' . "\n";
  997. }
  998. if ($this->options->commentsThreaded && $this->is('single')) {
  999. if ('' != $allows['commentReply']) {
  1000. if (1 == $allows['commentReply']) {
  1001. $header .= "<script type=\"text/javascript\">
  1002. (function () {
  1003. window.TypechoComment = {
  1004. dom : function (id) {
  1005. return document.getElementById(id);
  1006. },
  1007. create : function (tag, attr) {
  1008. var el = document.createElement(tag);
  1009. for (var key in attr) {
  1010. el.setAttribute(key, attr[key]);
  1011. }
  1012. return el;
  1013. },
  1014. reply : function (cid, coid) {
  1015. var comment = this.dom(cid), parent = comment.parentNode,
  1016. response = this.dom('" . $this->respondId . "'), input = this.dom('comment-parent'),
  1017. form = 'form' == response.tagName ? response : response.getElementsByTagName('form')[0],
  1018. textarea = response.getElementsByTagName('textarea')[0];
  1019. if (null == input) {
  1020. input = this.create('input', {
  1021. 'type' : 'hidden',
  1022. 'name' : 'parent',
  1023. 'id' : 'comment-parent'
  1024. });
  1025. form.appendChild(input);
  1026. }
  1027. input.setAttribute('value', coid);
  1028. if (null == this.dom('comment-form-place-holder')) {
  1029. var holder = this.create('div', {
  1030. 'id' : 'comment-form-place-holder'
  1031. });
  1032. response.parentNode.insertBefore(holder, response);
  1033. }
  1034. comment.appendChild(response);
  1035. this.dom('cancel-comment-reply-link').style.display = '';
  1036. if (null != textarea && 'text' == textarea.name) {
  1037. textarea.focus();
  1038. }
  1039. return false;
  1040. },
  1041. cancelReply : function () {
  1042. var response = this.dom('{$this->respondId}'),
  1043. holder = this.dom('comment-form-place-holder'), input = this.dom('comment-parent');
  1044. if (null != input) {
  1045. input.parentNode.removeChild(input);
  1046. }
  1047. if (null == holder) {
  1048. return true;
  1049. }
  1050. this.dom('cancel-comment-reply-link').style.display = 'none';
  1051. holder.parentNode.insertBefore(response, holder);
  1052. return false;
  1053. }
  1054. };
  1055. })();
  1056. </script>
  1057. ";
  1058. } else {
  1059. $header .= '<script src="' . $allows['commentReply'] . '" type="text/javascript"></script>';
  1060. }
  1061. }
  1062. }
  1063. /** 反垃圾设置 */
  1064. if ($this->options->commentsAntiSpam && $this->is('single')) {
  1065. if ('' != $allows['antiSpam']) {
  1066. if (1 == $allows['antiSpam']) {
  1067. $header .= "<script type=\"text/javascript\">
  1068. (function () {
  1069. var event = document.addEventListener ? {
  1070. add: 'addEventListener',
  1071. triggers: ['scroll', 'mousemove', 'keyup', 'touchstart'],
  1072. load: 'DOMContentLoaded'
  1073. } : {
  1074. add: 'attachEvent',
  1075. triggers: ['onfocus', 'onmousemove', 'onkeyup', 'ontouchstart'],
  1076. load: 'onload'
  1077. }, added = false;
  1078. document[event.add](event.load, function () {
  1079. var r = document.getElementById('{$this->respondId}'),
  1080. input = document.createElement('input');
  1081. input.type = 'hidden';
  1082. input.name = '_';
  1083. input.value = " . Common::shuffleScriptVar($this->security->getToken($this->request->getRequestUrl())) . "
  1084. if (null != r) {
  1085. var forms = r.getElementsByTagName('form');
  1086. if (forms.length > 0) {
  1087. function append() {
  1088. if (!added) {
  1089. forms[0].appendChild(input);
  1090. added = true;
  1091. }
  1092. }
  1093. for (var i = 0; i < event.triggers.length; i ++) {
  1094. var trigger = event.triggers[i];
  1095. document[event.add](trigger, append);
  1096. window[event.add](trigger, append);
  1097. }
  1098. }
  1099. }
  1100. });
  1101. })();
  1102. </script>";
  1103. } else {
  1104. $header .= '<script src="' . $allows['antiSpam'] . '" type="text/javascript"></script>';
  1105. }
  1106. }
  1107. }
  1108. /** 输出header */
  1109. echo $header;
  1110. /** 插件支持 */
  1111. self::pluginHandle()->header($header, $this);
  1112. }
  1113. /**
  1114. * 支持页脚自定义
  1115. */
  1116. public function footer()
  1117. {
  1118. self::pluginHandle()->footer($this);
  1119. }
  1120. /**
  1121. * 输出cookie记忆别名
  1122. *
  1123. * @param string $cookieName 已经记忆的cookie名称
  1124. * @param boolean $return 是否返回
  1125. * @return string|void
  1126. */
  1127. public function remember(string $cookieName, bool $return = false)
  1128. {
  1129. $cookieName = strtolower($cookieName);
  1130. if (!in_array($cookieName, ['author', 'mail', 'url'])) {
  1131. return '';
  1132. }
  1133. $value = Cookie::get('__typecho_remember_' . $cookieName);
  1134. if ($return) {
  1135. return $value;
  1136. } else {
  1137. echo htmlspecialchars($value ?? '');
  1138. }
  1139. }
  1140. /**
  1141. * 输出归档标题
  1142. *
  1143. * @param mixed $defines
  1144. * @param string $before
  1145. * @param string $end
  1146. */
  1147. public function archiveTitle($defines = null, string $before = ' &raquo; ', string $end = '')
  1148. {
  1149. if ($this->archiveTitle) {
  1150. $define = '%s';
  1151. if (is_array($defines) && !empty($defines[$this->archiveType])) {
  1152. $define = $defines[$this->archiveType];
  1153. }
  1154. echo $before . sprintf($define, $this->archiveTitle) . $end;
  1155. }
  1156. }
  1157. /**
  1158. * 输出关键字
  1159. *
  1160. * @param string $split
  1161. * @param string $default
  1162. */
  1163. public function keywords(string $split = ',', string $default = '')
  1164. {
  1165. echo empty($this->keywords) ? $default : str_replace(',', $split, htmlspecialchars($this->keywords ?? ''));
  1166. }
  1167. /**
  1168. * 获取主题文件
  1169. *
  1170. * @param string $fileName 主题文件
  1171. */
  1172. public function need(string $fileName)
  1173. {
  1174. require $this->themeDir . $fileName;
  1175. }
  1176. /**
  1177. * 输出视图
  1178. * @throws WidgetException
  1179. */
  1180. public function render()
  1181. {
  1182. /** 处理静态链接跳转 */
  1183. $this->checkPermalink();
  1184. /** 添加Pingback */
  1185. if (2 == $this->options->allowXmlRpc) {
  1186. $this->response->setHeader('X-Pingback', $this->options->xmlRpcUrl);
  1187. }
  1188. $validated = false;
  1189. //~ 自定义模板
  1190. if (!empty($this->themeFile)) {
  1191. if (file_exists($this->themeDir . $this->themeFile)) {
  1192. $validated = true;
  1193. }
  1194. }
  1195. if (!$validated && !empty($this->archiveType)) {
  1196. //~ 首先找具体路径, 比如 category/default.php
  1197. if (!$validated && !empty($this->archiveSlug)) {
  1198. $themeFile = $this->archiveType . '/' . $this->archiveSlug . '.php';
  1199. if (file_exists($this->themeDir . $themeFile)) {
  1200. $this->themeFile = $themeFile;
  1201. $validated = true;
  1202. }
  1203. }
  1204. //~ 然后找归档类型路径, 比如 category.php
  1205. if (!$validated) {
  1206. $themeFile = $this->archiveType . '.php';
  1207. if (file_exists($this->themeDir . $themeFile)) {
  1208. $this->themeFile = $themeFile;
  1209. $validated = true;
  1210. }
  1211. }
  1212. //针对attachment的hook
  1213. if (!$validated && 'attachment' == $this->archiveType) {
  1214. if (file_exists($this->themeDir . 'page.php')) {
  1215. $this->themeFile = 'page.php';
  1216. $validated = true;
  1217. } elseif (file_exists($this->themeDir . 'post.php')) {
  1218. $this->themeFile = 'post.php';
  1219. $validated = true;
  1220. }
  1221. }
  1222. //~ 最后找归档路径, 比如 archive.php 或者 single.php
  1223. if (!$validated && 'index' != $this->archiveType && 'front' != $this->archiveType) {
  1224. $themeFile = $this->archiveSingle ? 'single.php' : 'archive.php';
  1225. if (file_exists($this->themeDir . $themeFile)) {
  1226. $this->themeFile = $themeFile;
  1227. $validated = true;
  1228. }
  1229. }
  1230. if (!$validated) {
  1231. $themeFile = 'index.php';
  1232. if (file_exists($this->themeDir . $themeFile)) {
  1233. $this->themeFile = $themeFile;
  1234. $validated = true;
  1235. }
  1236. }
  1237. }
  1238. /** 文件不存在 */
  1239. if (!$validated) {
  1240. throw new WidgetException(_t('文件不存在'), 500);
  1241. }
  1242. /** 挂接插件 */
  1243. self::pluginHandle()->beforeRender($this);
  1244. /** 输出模板 */
  1245. require_once $this->themeDir . $this->themeFile;
  1246. /** 挂接插件 */
  1247. self::pluginHandle()->afterRender($this);
  1248. }
  1249. /**
  1250. * 输出feed
  1251. *
  1252. * @throws WidgetException
  1253. */
  1254. public function feed()
  1255. {
  1256. if ($this->feedType == Feed::RSS1) {
  1257. $feedUrl = $this->feedRssUrl;
  1258. } elseif ($this->feedType == Feed::ATOM1) {
  1259. $feedUrl = $this->feedAtomUrl;
  1260. } else {
  1261. $feedUrl = $this->feedUrl;
  1262. }
  1263. $this->checkPermalink($feedUrl);
  1264. $this->feed->setSubTitle($this->description);
  1265. $this->feed->setFeedUrl($feedUrl);
  1266. $this->feed->setBaseUrl($this->archiveUrl);
  1267. if ($this->is('single') || 'comments' == $this->parameter->type) {
  1268. $this->feed->setTitle(_t(
  1269. '%s 的评论',
  1270. $this->options->title . ($this->archiveTitle ? ' - ' . $this->archiveTitle : null)
  1271. ));
  1272. if ('comments' == $this->parameter->type) {
  1273. $comments = Recent::alloc('pageSize=10');
  1274. } else {
  1275. $comments = Recent::alloc('pageSize=10&parentId=' . $this->cid);
  1276. }
  1277. while ($comments->next()) {
  1278. $suffix = self::pluginHandle()->trigger($plugged)->commentFeedItem($this->feedType, $comments);
  1279. if (!$plugged) {
  1280. $suffix = null;
  1281. }
  1282. $this->feed->addItem([
  1283. 'title' => $comments->author,
  1284. 'content' => $comments->content,
  1285. 'date' => $comments->created,
  1286. 'link' => $comments->permalink,
  1287. 'author' => (object)[
  1288. 'screenName' => $comments->author,
  1289. 'url' => $comments->url,
  1290. 'mail' => $comments->mail
  1291. ],
  1292. 'excerpt' => strip_tags($comments->content),
  1293. 'suffix' => $suffix
  1294. ]);
  1295. }
  1296. } else {
  1297. $this->feed->setTitle($this->options->title . ($this->archiveTitle ? ' - ' . $this->archiveTitle : null));
  1298. while ($this->next()) {
  1299. $suffix = self::pluginHandle()->trigger($plugged)->feedItem($this->feedType, $this);
  1300. if (!$plugged) {
  1301. $suffix = null;
  1302. }
  1303. $feedUrl = '';
  1304. if (Feed::RSS2 == $this->feedType) {
  1305. $feedUrl = $this->feedUrl;
  1306. } elseif (Feed::RSS1 == $this->feedType) {
  1307. $feedUrl = $this->feedRssUrl;
  1308. } elseif (Feed::ATOM1 == $this->feedType) {
  1309. $feedUrl = $this->feedAtomUrl;
  1310. }
  1311. $this->feed->addItem([
  1312. 'title' => $this->title,
  1313. 'content' => $this->options->feedFullText ? $this->content
  1314. : (false !== strpos($this->text, '<!--more-->') ? $this->excerpt .
  1315. "<p class=\"more\"><a href=\"{$this->permalink}\" title=\"{$this->title}\">[...]</a></p>"
  1316. : $this->content),
  1317. 'date' => $this->created,
  1318. 'link' => $this->permalink,
  1319. 'author' => $this->author,
  1320. 'excerpt' => $this->___description(),
  1321. 'comments' => $this->commentsNum,
  1322. 'commentsFeedUrl' => $feedUrl,
  1323. 'suffix' => $suffix
  1324. ]);
  1325. }
  1326. }
  1327. $this->response->setContentType($this->feedContentType);
  1328. echo (string) $this->feed;
  1329. }
  1330. /**
  1331. * 判断归档类型和名称
  1332. *
  1333. * @access public
  1334. * @param string $archiveType 归档类型
  1335. * @param string|null $archiveSlug 归档名称
  1336. * @return boolean
  1337. */
  1338. public function is(string $archiveType, ?string $archiveSlug = null)
  1339. {
  1340. return ($archiveType == $this->archiveType ||
  1341. (($this->archiveSingle ? 'single' : 'archive') == $archiveType && 'index' != $this->archiveType) ||
  1342. ('index' == $archiveType && $this->makeSinglePageAsFrontPage))
  1343. && (empty($archiveSlug) || $archiveSlug == $this->archiveSlug);
  1344. }
  1345. /**
  1346. * 提交查询
  1347. *
  1348. * @param mixed $select 查询对象
  1349. * @throws Db\Exception
  1350. */
  1351. public function query($select)
  1352. {
  1353. self::pluginHandle()->trigger($queryPlugged)->query($this, $select);
  1354. if (!$queryPlugged) {
  1355. $this->db->fetchAll($select, [$this, 'push']);
  1356. }
  1357. }
  1358. /**
  1359. * 评论地址
  1360. *
  1361. * @return string
  1362. */
  1363. protected function ___commentUrl(): string
  1364. {
  1365. /** 生成反馈地址 */
  1366. /** 评论 */
  1367. $commentUrl = parent::___commentUrl();
  1368. //不依赖js的父级评论
  1369. $reply = $this->request->filter('int')->replyTo;
  1370. if ($reply && $this->is('single')) {
  1371. $commentUrl .= '?parent=' . $reply;
  1372. }
  1373. return $commentUrl;
  1374. }
  1375. /**
  1376. * 导入对象
  1377. *
  1378. * @param Archive $widget 需要导入的对象
  1379. */
  1380. private function import(Archive $widget)
  1381. {
  1382. $currentProperties = get_object_vars($this);
  1383. foreach ($currentProperties as $name => $value) {
  1384. if (false !== strpos('|request|response|parameter|feed|feedType|currentFeedUrl|', '|' . $name . '|')) {
  1385. continue;
  1386. }
  1387. if (isset($widget->{$name})) {
  1388. $this->{$name} = $widget->{$name};
  1389. } else {
  1390. $method = ucfirst($name);
  1391. $setMethod = 'set' . $method;
  1392. $getMethod = 'get' . $method;
  1393. if (
  1394. method_exists($this, $setMethod)
  1395. && method_exists($widget, $getMethod)
  1396. ) {
  1397. $value = $widget->{$getMethod}();
  1398. if ($value !== null) {
  1399. $this->{$setMethod}($widget->{$getMethod}());
  1400. }
  1401. }
  1402. }
  1403. }
  1404. }
  1405. /**
  1406. * 检查链接是否正确
  1407. *
  1408. * @param string|null $permalink
  1409. */
  1410. private function checkPermalink(?string $permalink = null)
  1411. {
  1412. if (!isset($permalink)) {
  1413. $type = $this->parameter->type;
  1414. if (
  1415. in_array($type, ['index', 'comment_page', 404])
  1416. || $this->makeSinglePageAsFrontPage // 自定义首页不处理
  1417. || !$this->parameter->checkPermalink
  1418. ) { // 强制关闭
  1419. return;
  1420. }
  1421. if ($this->archiveSingle) {
  1422. $permalink = $this->permalink;
  1423. } else {
  1424. $value = array_merge($this->pageRow, [
  1425. 'page' => $this->currentPage
  1426. ]);
  1427. $path = Router::url($type, $value);
  1428. $permalink = Common::url($path, $this->options->index);
  1429. }
  1430. }
  1431. $requestUrl = $this->request->getRequestUrl();
  1432. $src = parse_url($permalink);
  1433. $target = parse_url($requestUrl);
  1434. if ($src['host'] != $target['host'] || urldecode($src['path']) != urldecode($target['path'])) {
  1435. //$this->response->redirect($permalink, true);
  1436. }
  1437. }
  1438. /**
  1439. * 处理index
  1440. *
  1441. * @param Query $select 查询对象
  1442. * @param boolean $hasPushed 是否已经压入队列
  1443. */
  1444. private function indexHandle(Query $select, bool &$hasPushed)
  1445. {
  1446. $select->where('table.contents.type = ?', 'post');
  1447. /** 插件接口 */
  1448. self::pluginHandle()->indexHandle($this, $select);
  1449. }
  1450. /**
  1451. * 默认的非首页归档处理
  1452. *
  1453. * @param Query $select 查询对象
  1454. * @param boolean $hasPushed 是否已经压入队列
  1455. * @throws WidgetException
  1456. */
  1457. private function archiveEmptyHandle(Query $select, bool &$hasPushed)
  1458. {
  1459. throw new WidgetException(_t('请求的地址不存在'), 404);
  1460. }
  1461. /**
  1462. * 404页面处理
  1463. *
  1464. * @param Query $select 查询对象
  1465. * @param boolean $hasPushed 是否已经压入队列
  1466. */
  1467. private function error404Handle(Query $select, bool &$hasPushed)
  1468. {
  1469. /** 设置header */
  1470. $this->response->setStatus(404);
  1471. /** 设置标题 */
  1472. $this->archiveTitle = _t('页面没找到');
  1473. /** 设置归档类型 */
  1474. $this->archiveType = 'archive';
  1475. /** 设置归档缩略名 */
  1476. $this->archiveSlug = 404;
  1477. /** 设置归档模板 */
  1478. $this->themeFile = '404.php';
  1479. /** 设置单一归档类型 */
  1480. $this->archiveSingle = false;
  1481. $hasPushed = true;
  1482. /** 插件接口 */
  1483. self::pluginHandle()->error404Handle($this, $select);
  1484. }
  1485. /**
  1486. * 独立页处理
  1487. *
  1488. * @param Query $select 查询对象
  1489. * @param boolean $hasPushed 是否已经压入队列
  1490. * @throws WidgetException|Db\Exception
  1491. */
  1492. private function singleHandle(Query $select, bool &$hasPushed)
  1493. {
  1494. if ('comment_page' == $this->parameter->type) {
  1495. $params = [];
  1496. $matched = Router::match($this->request->permalink);
  1497. if ($matched && $matched instanceof Archive && $matched->is('single')) {
  1498. $this->import($matched);
  1499. $hasPushed = true;
  1500. return;
  1501. }
  1502. }
  1503. /** 将这两个设置提前是为了保证在调用query的plugin时可以在插件中使用is判断初步归档类型 */
  1504. /** 如果需要更细判断,则可以使用singleHandle来实现 */
  1505. $this->archiveSingle = true;
  1506. /** 默认归档类型 */
  1507. $this->archiveType = 'single';
  1508. /** 匹配类型 */
  1509. if ('single' != $this->parameter->type) {
  1510. $select->where('table.contents.type = ?', $this->parameter->type);
  1511. }
  1512. /** 如果是单篇文章或独立页面 */
  1513. if (isset($this->request->cid)) {
  1514. $select->where('table.contents.cid = ?', $this->request->filter('int')->cid);
  1515. }
  1516. /** 匹配缩略名 */
  1517. if (isset($this->request->slug) && !$this->parameter->preview) {
  1518. $select->where('table.contents.slug = ?', $this->request->slug);
  1519. }
  1520. /** 匹配时间 */
  1521. if (isset($this->request->year) && !$this->parameter->preview) {
  1522. $year = $this->request->filter('int')->year;
  1523. $fromMonth = 1;
  1524. $toMonth = 12;
  1525. $fromDay = 1;
  1526. $toDay = 31;
  1527. if (isset($this->request->month)) {
  1528. $fromMonth = $this->request->filter('int')->month;
  1529. $toMonth = $fromMonth;
  1530. $fromDay = 1;
  1531. $toDay = date('t', mktime(0, 0, 0, $toMonth, 1, $year));
  1532. if (isset($this->request->day)) {
  1533. $fromDay = $this->request->filter('int')->day;
  1534. $toDay = $fromDay;
  1535. }
  1536. }
  1537. /** 获取起始GMT时间的unix时间戳 */
  1538. $from = mktime(0, 0, 0, $fromMonth, $fromDay, $year)
  1539. - $this->options->timezone + $this->options->serverTimezone;
  1540. $to = mktime(23, 59, 59, $toMonth, $toDay, $year)
  1541. - $this->options->timezone + $this->options->serverTimezone;
  1542. $select->where('table.contents.created >= ? AND table.contents.created < ?', $from, $to);
  1543. }
  1544. /** 保存密码至cookie */
  1545. $isPasswordPosted = false;
  1546. if (
  1547. $this->request->isPost()
  1548. && isset($this->request->protectPassword)
  1549. && !$this->parameter->preview
  1550. ) {
  1551. $this->security->protect();
  1552. Cookie::set(
  1553. 'protectPassword_' . $this->request->filter('int')->protectCID,
  1554. $this->request->protectPassword
  1555. );
  1556. $isPasswordPosted = true;
  1557. }
  1558. /** 匹配类型 */
  1559. $select->limit(1);
  1560. $this->query($select);
  1561. if (
  1562. !$this->have()
  1563. || (isset($this->request->category)
  1564. && $this->category != $this->request->category && !$this->parameter->preview)
  1565. || (isset($this->request->directory)
  1566. && $this->request->directory != implode('/', $this->directory) && !$this->parameter->preview)
  1567. ) {
  1568. if (!$this->invokeFromOutside) {
  1569. /** 对没有索引情况下的判断 */
  1570. throw new WidgetException(_t('请求的地址不存在'), 404);
  1571. } else {
  1572. $hasPushed = true;
  1573. return;
  1574. }
  1575. }
  1576. /** 密码表单判断逻辑 */
  1577. if ($isPasswordPosted && $this->hidden) {
  1578. throw new WidgetException(_t('对不起,您输入的密码错误'), 403);
  1579. }
  1580. /** 设置模板 */
  1581. if ($this->template) {
  1582. /** 应用自定义模板 */
  1583. $this->themeFile = $this->template;
  1584. }
  1585. /** 设置头部feed */
  1586. /** RSS 2.0 */
  1587. //对自定义首页使用全局变量
  1588. if (!$this->makeSinglePageAsFrontPage) {
  1589. $this->feedUrl = $this->row['feedUrl'];
  1590. /** RSS 1.0 */
  1591. $this->feedRssUrl = $this->row['feedRssUrl'];
  1592. /** ATOM 1.0 */
  1593. $this->feedAtomUrl = $this->row['feedAtomUrl'];
  1594. /** 设置标题 */
  1595. $this->archiveTitle = $this->title;
  1596. /** 设置关键词 */
  1597. $this->keywords = implode(',', array_column($this->tags, 'name'));
  1598. /** 设置描述 */
  1599. $this->description = $this->___description();
  1600. }
  1601. /** 设置归档类型 */
  1602. [$this->archiveType] = explode('_', $this->type);
  1603. /** 设置归档缩略名 */
  1604. $this->archiveSlug = ('post' == $this->type || 'attachment' == $this->type) ? $this->cid : $this->slug;
  1605. /** 设置归档地址 */
  1606. $this->archiveUrl = $this->permalink;
  1607. /** 设置403头 */
  1608. if ($this->hidden) {
  1609. $this->response->setStatus(403);
  1610. }
  1611. $hasPushed = true;
  1612. /** 插件接口 */
  1613. self::pluginHandle()->singleHandle($this, $select);
  1614. }
  1615. /**
  1616. * 处理分类
  1617. *
  1618. * @param Query $select 查询对象
  1619. * @param boolean $hasPushed 是否已经压入队列
  1620. * @throws WidgetException|Db\Exception
  1621. */
  1622. private function categoryHandle(Query $select, bool &$hasPushed)
  1623. {
  1624. /** 如果是分类 */
  1625. $categorySelect = $this->db->select()
  1626. ->from('table.metas')
  1627. ->where('type = ?', 'category')
  1628. ->limit(1);
  1629. if (isset($this->request->mid)) {
  1630. $categorySelect->where('mid = ?', $this->request->filter('int')->mid);
  1631. }
  1632. if (isset($this->request->slug)) {
  1633. $categorySelect->where('slug = ?', $this->request->slug);
  1634. }
  1635. if (isset($this->request->directory)) {
  1636. $directory = explode('/', $this->request->directory);
  1637. $categorySelect->where('slug = ?', $directory[count($directory) - 1]);
  1638. }
  1639. $category = $this->db->fetchRow($categorySelect);
  1640. if (empty($category)) {
  1641. throw new WidgetException(_t('分类不存在'), 404);
  1642. }
  1643. $categoryListWidget = Rows::alloc('current=' . $category['mid']);
  1644. $category = $categoryListWidget->filter($category);
  1645. if (isset($directory) && ($this->request->directory != implode('/', $category['directory']))) {
  1646. throw new WidgetException(_t('父级分类不存在'), 404);
  1647. }
  1648. $children = $categoryListWidget->getAllChildren($category['mid']);
  1649. $children[] = $category['mid'];
  1650. /** fix sql92 by 70 */
  1651. $select->join('table.relationships', 'table.contents.cid = table.relationships.cid')
  1652. ->where('table.relationships.mid IN ?', $children)
  1653. ->where('table.contents.type = ?', 'post')
  1654. ->group('table.contents.cid');
  1655. /** 设置分页 */
  1656. $this->pageRow = array_merge($category, [
  1657. 'slug' => urlencode($category['slug']),
  1658. 'directory' => implode('/', array_map('urlencode', $category['directory']))
  1659. ]);
  1660. /** 设置关键词 */
  1661. $this->keywords = $category['name'];
  1662. /** 设置描述 */
  1663. $this->description = $category['description'];
  1664. /** 设置头部feed */
  1665. /** RSS 2.0 */
  1666. $this->feedUrl = $category['feedUrl'];
  1667. /** RSS 1.0 */
  1668. $this->feedRssUrl = $category['feedRssUrl'];
  1669. /** ATOM 1.0 */
  1670. $this->feedAtomUrl = $category['feedAtomUrl'];
  1671. /** 设置标题 */
  1672. $this->archiveTitle = $category['name'];
  1673. /** 设置归档类型 */
  1674. $this->archiveType = 'category';
  1675. /** 设置归档缩略名 */
  1676. $this->archiveSlug = $category['slug'];
  1677. /** 设置归档地址 */
  1678. $this->archiveUrl = $category['permalink'];
  1679. /** 插件接口 */
  1680. self::pluginHandle()->categoryHandle($this, $select);
  1681. }
  1682. /**
  1683. * 处理标签
  1684. *
  1685. * @param Query $select 查询对象
  1686. * @param boolean $hasPushed 是否已经压入队列
  1687. * @throws WidgetException|Db\Exception
  1688. */
  1689. private function tagHandle(Query $select, bool &$hasPushed)
  1690. {
  1691. $tagSelect = $this->db->select()->from('table.metas')
  1692. ->where('type = ?', 'tag')->limit(1);
  1693. if (isset($this->request->mid)) {
  1694. $tagSelect->where('mid = ?', $this->request->filter('int')->mid);
  1695. }
  1696. if (isset($this->request->slug)) {
  1697. $tagSelect->where('slug = ?', $this->request->slug);
  1698. }
  1699. /** 如果是标签 */
  1700. $tag = $this->db->fetchRow(
  1701. $tagSelect,
  1702. [Metas::alloc(), 'filter']
  1703. );
  1704. if (!$tag) {
  1705. throw new WidgetException(_t('标签不存在'), 404);
  1706. }
  1707. /** fix sql92 by 70 */
  1708. $select->join('table.relationships', 'table.contents.cid = table.relationships.cid')
  1709. ->where('table.relationships.mid = ?', $tag['mid'])
  1710. ->where('table.contents.type = ?', 'post');
  1711. /** 设置分页 */
  1712. $this->pageRow = array_merge($tag, [
  1713. 'slug' => urlencode($tag['slug'])
  1714. ]);
  1715. /** 设置关键词 */
  1716. $this->keywords = $tag['name'];
  1717. /** 设置描述 */
  1718. $this->description = $tag['description'];
  1719. /** 设置头部feed */
  1720. /** RSS 2.0 */
  1721. $this->feedUrl = $tag['feedUrl'];
  1722. /** RSS 1.0 */
  1723. $this->feedRssUrl = $tag['feedRssUrl'];
  1724. /** ATOM 1.0 */
  1725. $this->feedAtomUrl = $tag['feedAtomUrl'];
  1726. /** 设置标题 */
  1727. $this->archiveTitle = $tag['name'];
  1728. /** 设置归档类型 */
  1729. $this->archiveType = 'tag';
  1730. /** 设置归档缩略名 */
  1731. $this->archiveSlug = $tag['slug'];
  1732. /** 设置归档地址 */
  1733. $this->archiveUrl = $tag['permalink'];
  1734. /** 插件接口 */
  1735. self::pluginHandle()->tagHandle($this, $select);
  1736. }
  1737. /**
  1738. * 处理作者
  1739. *
  1740. * @param Query $select 查询对象
  1741. * @param boolean $hasPushed 是否已经压入队列
  1742. * @throws WidgetException|Db\Exception
  1743. */
  1744. private function authorHandle(Query $select, bool &$hasPushed)
  1745. {
  1746. $uid = $this->request->filter('int')->uid;
  1747. $author = $this->db->fetchRow(
  1748. $this->db->select()->from('table.users')
  1749. ->where('uid = ?', $uid),
  1750. [User::alloc(), 'filter']
  1751. );
  1752. if (!$author) {
  1753. throw new WidgetException(_t('作者不存在'), 404);
  1754. }
  1755. $select->where('table.contents.authorId = ?', $uid)
  1756. ->where('table.contents.type = ?', 'post');
  1757. /** 设置分页 */
  1758. $this->pageRow = $author;
  1759. /** 设置关键词 */
  1760. $this->keywords = $author['screenName'];
  1761. /** 设置描述 */
  1762. $this->description = $author['screenName'];
  1763. /** 设置头部feed */
  1764. /** RSS 2.0 */
  1765. $this->feedUrl = $author['feedUrl'];
  1766. /** RSS 1.0 */
  1767. $this->feedRssUrl = $author['feedRssUrl'];
  1768. /** ATOM 1.0 */
  1769. $this->feedAtomUrl = $author['feedAtomUrl'];
  1770. /** 设置标题 */
  1771. $this->archiveTitle = $author['screenName'];
  1772. /** 设置归档类型 */
  1773. $this->archiveType = 'author';
  1774. /** 设置归档缩略名 */
  1775. $this->archiveSlug = $author['uid'];
  1776. /** 设置归档地址 */
  1777. $this->archiveUrl = $author['permalink'];
  1778. /** 插件接口 */
  1779. self::pluginHandle()->authorHandle($this, $select);
  1780. }
  1781. /**
  1782. * 处理日期
  1783. *
  1784. * @access private
  1785. * @param Query $select 查询对象
  1786. * @param boolean $hasPushed 是否已经压入队列
  1787. * @return void
  1788. */
  1789. private function dateHandle(Query $select, &$hasPushed)
  1790. {
  1791. /** 如果是按日期归档 */
  1792. $year = $this->request->filter('int')->year;
  1793. $month = $this->request->filter('int')->month;
  1794. $day = $this->request->filter('int')->day;
  1795. if (!empty($year) && !empty($month) && !empty($day)) {
  1796. /** 如果按日归档 */
  1797. $from = mktime(0, 0, 0, $month, $day, $year);
  1798. $to = mktime(23, 59, 59, $month, $day, $year);
  1799. /** 归档缩略名 */
  1800. $this->archiveSlug = 'day';
  1801. /** 设置标题 */
  1802. $this->archiveTitle = _t('%d年%d月%d日', $year, $month, $day);
  1803. } elseif (!empty($year) && !empty($month)) {
  1804. /** 如果按月归档 */
  1805. $from = mktime(0, 0, 0, $month, 1, $year);
  1806. $to = mktime(23, 59, 59, $month, date('t', $from), $year);
  1807. /** 归档缩略名 */
  1808. $this->archiveSlug = 'month';
  1809. /** 设置标题 */
  1810. $this->archiveTitle = _t('%d年%d月', $year, $month);
  1811. } elseif (!empty($year)) {
  1812. /** 如果按年归档 */
  1813. $from = mktime(0, 0, 0, 1, 1, $year);
  1814. $to = mktime(23, 59, 59, 12, 31, $year);
  1815. /** 归档缩略名 */
  1816. $this->archiveSlug = 'year';
  1817. /** 设置标题 */
  1818. $this->archiveTitle = _t('%d年', $year);
  1819. }
  1820. $select->where('table.contents.created >= ?', $from - $this->options->timezone + $this->options->serverTimezone)
  1821. ->where('table.contents.created <= ?', $to - $this->options->timezone + $this->options->serverTimezone)
  1822. ->where('table.contents.type = ?', 'post');
  1823. /** 设置归档类型 */
  1824. $this->archiveType = 'date';
  1825. /** 设置头部feed */
  1826. $value = [
  1827. 'year' => $year,
  1828. 'month' => str_pad($month, 2, '0', STR_PAD_LEFT),
  1829. 'day' => str_pad($day, 2, '0', STR_PAD_LEFT)
  1830. ];
  1831. /** 设置分页 */
  1832. $this->pageRow = $value;
  1833. /** 获取当前路由,过滤掉翻页情况 */
  1834. $currentRoute = str_replace('_page', '', $this->parameter->type);
  1835. /** RSS 2.0 */
  1836. $this->feedUrl = Router::url($currentRoute, $value, $this->options->feedUrl);
  1837. /** RSS 1.0 */
  1838. $this->feedRssUrl = Router::url($currentRoute, $value, $this->options->feedRssUrl);
  1839. /** ATOM 1.0 */
  1840. $this->feedAtomUrl = Router::url($currentRoute, $value, $this->options->feedAtomUrl);
  1841. /** 设置归档地址 */
  1842. $this->archiveUrl = Router::url($currentRoute, $value, $this->options->index);
  1843. /** 插件接口 */
  1844. self::pluginHandle()->dateHandle($this, $select);
  1845. }
  1846. /**
  1847. * 处理搜索
  1848. *
  1849. * @access private
  1850. * @param Query $select 查询对象
  1851. * @param boolean $hasPushed 是否已经压入队列
  1852. * @return void
  1853. */
  1854. private function searchHandle(Query $select, &$hasPushed)
  1855. {
  1856. /** 增加自定义搜索引擎接口 */
  1857. //~ fix issue 40
  1858. $keywords = $this->request->filter('url', 'search')->keywords;
  1859. self::pluginHandle()->trigger($hasPushed)->search($keywords, $this);
  1860. if (!$hasPushed) {
  1861. $searchQuery = '%' . str_replace(' ', '%', $keywords) . '%';
  1862. /** 搜索无法进入隐私项保护归档 */
  1863. if ($this->user->hasLogin()) {
  1864. //~ fix issue 941
  1865. $select->where("table.contents.password IS NULL
  1866. OR table.contents.password = '' OR table.contents.authorId = ?", $this->user->uid);
  1867. } else {
  1868. $select->where("table.contents.password IS NULL OR table.contents.password = ''");
  1869. }
  1870. $op = $this->db->getAdapter()->getDriver() == 'pgsql' ? 'ILIKE' : 'LIKE';
  1871. $select->where("table.contents.title {$op} ? OR table.contents.text {$op} ?", $searchQuery, $searchQuery)
  1872. ->where('table.contents.type = ?', 'post');
  1873. }
  1874. /** 设置关键词 */
  1875. $this->keywords = $keywords;
  1876. /** 设置分页 */
  1877. $this->pageRow = ['keywords' => urlencode($keywords)];
  1878. /** 设置头部feed */
  1879. /** RSS 2.0 */
  1880. $this->feedUrl = Router::url('search', ['keywords' => $keywords], $this->options->feedUrl);
  1881. /** RSS 1.0 */
  1882. $this->feedRssUrl = Router::url('search', ['keywords' => $keywords], $this->options->feedAtomUrl);
  1883. /** ATOM 1.0 */
  1884. $this->feedAtomUrl = Router::url('search', ['keywords' => $keywords], $this->options->feedAtomUrl);
  1885. /** 设置标题 */
  1886. $this->archiveTitle = $keywords;
  1887. /** 设置归档类型 */
  1888. $this->archiveType = 'search';
  1889. /** 设置归档缩略名 */
  1890. $this->archiveSlug = $keywords;
  1891. /** 设置归档地址 */
  1892. $this->archiveUrl = Router::url('search', ['keywords' => $keywords], $this->options->index);
  1893. /** 插件接口 */
  1894. self::pluginHandle()->searchHandle($this, $select);
  1895. }
  1896. }