igb hace 2 meses
padre
commit
c02c162b55
Se han modificado 100 ficheros con 8762 adiciones y 0 borrados
  1. 282 0
      LICENSE.txt
  2. 106 0
      admin/backup.php
  3. 23 0
      admin/category.php
  4. 142 0
      admin/common-js.php
  5. 54 0
      admin/common.php
  6. 13 0
      admin/copyright.php
  7. 211 0
      admin/css/grid.css
  8. 24 0
      admin/css/install.css
  9. 341 0
      admin/css/normalize.css
  10. 944 0
      admin/css/style.css
  11. 50 0
      admin/custom-fields-js.php
  12. 84 0
      admin/custom-fields.php
  13. 242 0
      admin/editor-js.php
  14. 14 0
      admin/extending.php
  15. 229 0
      admin/file-upload-js.php
  16. 30 0
      admin/file-upload.php
  17. 7 0
      admin/footer.php
  18. 26 0
      admin/form-js.php
  19. 23 0
      admin/header.php
  20. BIN
      admin/img/ajax-loader.gif
  21. BIN
      admin/img/editor.png
  22. BIN
      admin/img/editor@2x.png
  23. BIN
      admin/img/icons.png
  24. BIN
      admin/img/icons@2x.png
  25. BIN
      admin/img/noscreen.png
  26. 6 0
      admin/img/typecho-logo.svg
  27. 155 0
      admin/index.php
  28. BIN
      admin/js/Moxie.swf
  29. 0 0
      admin/js/hyperdown.js
  30. 0 0
      admin/js/jquery-ui.js
  31. 0 0
      admin/js/jquery.js
  32. 0 0
      admin/js/moxie.js
  33. 0 0
      admin/js/pagedown.js
  34. 0 0
      admin/js/paste.js
  35. 0 0
      admin/js/plupload.js
  36. 0 0
      admin/js/purify.js
  37. 0 0
      admin/js/timepicker.js
  38. 0 0
      admin/js/tokeninput.js
  39. 0 0
      admin/js/typecho.js
  40. 56 0
      admin/login.php
  41. 172 0
      admin/manage-categories.php
  42. 378 0
      admin/manage-comments.php
  43. 144 0
      admin/manage-medias.php
  44. 156 0
      admin/manage-pages.php
  45. 258 0
      admin/manage-posts.php
  46. 102 0
      admin/manage-tags.php
  47. 139 0
      admin/manage-users.php
  48. 200 0
      admin/media.php
  49. 18 0
      admin/menu.php
  50. 23 0
      admin/options-discussion.php
  51. 23 0
      admin/options-general.php
  52. 24 0
      admin/options-permalink.php
  53. 23 0
      admin/options-plugin.php
  54. 37 0
      admin/options-reading.php
  55. 33 0
      admin/options-theme.php
  56. 8 0
      admin/page-title.php
  57. 133 0
      admin/plugins.php
  58. 27 0
      admin/preview.php
  59. 62 0
      admin/profile.php
  60. 50 0
      admin/register.php
  61. 19 0
      admin/table-js.php
  62. 65 0
      admin/theme-editor.php
  63. 85 0
      admin/themes.php
  64. 45 0
      admin/upgrade.php
  65. 23 0
      admin/user.php
  66. 36 0
      admin/welcome.php
  67. 386 0
      admin/write-js.php
  68. 179 0
      admin/write-page.php
  69. 216 0
      admin/write-post.php
  70. 33 0
      config.inc.php
  71. 26 0
      index.php
  72. 1489 0
      install.php
  73. 84 0
      install/Mysql.php
  74. 152 0
      install/Mysql.sql
  75. 37 0
      install/Pgsql.php
  76. 130 0
      install/Pgsql.sql
  77. 9 0
      install/SQLite.php
  78. 87 0
      install/SQLite.sql
  79. BIN
      usr/langs/ceb.mo
  80. BIN
      usr/langs/el_GR.mo
  81. BIN
      usr/langs/en_US.mo
  82. BIN
      usr/langs/es_ES.mo
  83. BIN
      usr/langs/fil_PH.mo
  84. BIN
      usr/langs/fr_FR.mo
  85. BIN
      usr/langs/ja_JP.mo
  86. BIN
      usr/langs/pt_BR.mo
  87. BIN
      usr/langs/ru_RU.mo
  88. BIN
      usr/langs/tr_TR.mo
  89. BIN
      usr/langs/ug_CN.mo
  90. BIN
      usr/langs/zh_TW.mo
  91. 29 0
      usr/plugins/BearDocsCore/Action.php
  92. 229 0
      usr/plugins/BearDocsCore/Plugin.php
  93. 182 0
      usr/plugins/BearDocsCore/assets/css/color-picker.css
  94. 1 0
      usr/plugins/BearDocsCore/assets/css/color-picker.min.css
  95. 0 0
      usr/plugins/BearDocsCore/assets/css/style-rtl.min.css
  96. 74 0
      usr/plugins/BearDocsCore/assets/css/style.min.css
  97. 74 0
      usr/plugins/BearDocsCore/assets/css/style.scss
  98. BIN
      usr/plugins/BearDocsCore/assets/fonts/dashicons.eot
  99. 0 0
      usr/plugins/BearDocsCore/assets/fonts/dashicons.svg
  100. BIN
      usr/plugins/BearDocsCore/assets/fonts/dashicons.ttf

+ 282 - 0
LICENSE.txt

@@ -0,0 +1,282 @@
+The GNU General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies
+
+of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License. (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code. (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS

+ 106 - 0
admin/backup.php

@@ -0,0 +1,106 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+$actionUrl = $security->getTokenUrl(
+    \Typecho\Router::url('do', array('action' => 'backup', 'widget' => 'Backup'),
+        \Typecho\Common::url('index.php', $options->rootUrl)));
+
+$backupFiles = \Widget\Backup::alloc()->listFiles();
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12 col-tb-8">
+                <div id="typecho-welcome">
+                    <form action="<?php echo $actionUrl; ?>" method="post">
+                    <h3><?php _e('备份您的数据'); ?></h3>
+                    <ul>
+                        <li><?php _e('此备份操作仅包含<strong>内容数据</strong>, 并不会涉及任何<strong>设置信息</strong>'); ?></li>
+                        <li><?php _e('如果您的数据量过大, 为了避免操作超时, 建议您直接使用数据库提供的备份工具备份数据'); ?></li>
+                        <li><strong class="warning"><?php _e('为了缩小备份文件体积, 建议您在备份前删除不必要的数据'); ?></strong></li>
+                    </ul>
+                    <p><button class="btn primary" type="submit"><?php _e('开始备份 &raquo;'); ?></button></p>
+                        <input tabindex="1" type="hidden" name="do" value="export">
+                    </form>
+                </div>
+            </div>
+
+            <div id="backup-secondary" class="col-mb-12 col-tb-4" role="form">
+                <h3><?php _e('恢复数据'); ?></h3>
+                <ul class="typecho-option-tabs clearfix">
+                    <li class="active w-50"><a href="#from-upload"><?php _e('上传'); ?></a></li>
+                    <li class="w-50"><a href="#from-server"><?php _e('从服务器'); ?></a></li>
+                </ul>
+
+                <form action="<?php echo $actionUrl; ?>" id="from-upload" class="tab-content" method="post" enctype="multipart/form-data">
+                    <ul class="typecho-option">
+                        <li>
+                            <input tabindex="2" id="backup-upload-file" name="file" type="file" class="file">
+                        </li>
+                    </ul>
+                    <ul class="typecho-option typecho-option-submit">
+                        <li>
+                            <button tabindex="4" type="submit" class="btn primary"><?php _e('上传并恢复 &raquo;'); ?></button>
+                            <input type="hidden" name="do" value="import">
+                        </li>
+                    </ul>
+                </form>
+
+                <form action="<?php echo $actionUrl; ?>" id="from-server" class="tab-content hidden" method="post">
+                    <?php if (empty($backupFiles)): ?>
+                    <ul class="typecho-option">
+                        <li>
+                            <p class="description"><?php _e('将备份文件手动上传至服务器的 %s 目录下后, 这里会出现文件选项', __TYPECHO_BACKUP_DIR__); ?></p>
+                        </li>
+                    </ul>
+                    <?php else: ?>
+                    <ul class="typecho-option">
+                        <li>
+                            <label class="typecho-label" for="backup-select-file"><?php _e('选择一个备份文件恢复数据'); ?></label>
+                            <select tabindex="5" name="file" id="backup-select-file">
+                                <?php foreach ($backupFiles as $file): ?>
+                                    <option value="<?php echo $file; ?>"><?php echo $file; ?></option>
+                                <?php endforeach; ?>
+                            </select>
+                        </li>
+                    </ul>
+                    <?php endif; ?>
+                    <ul class="typecho-option typecho-option-submit">
+                        <li>
+                            <button tabindex="7" type="submit" class="btn primary"><?php _e('选择并恢复 &raquo;'); ?></button>
+                            <input type="hidden" name="do" value="import">
+                        </li>
+                    </ul>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+?>
+<script>
+    $('#backup-secondary .typecho-option-tabs li').click(function() {
+        $('#backup-secondary .typecho-option-tabs li').removeClass('active');
+        $(this).addClass('active');
+        $(this).parents('#backup-secondary').find('.tab-content').addClass('hidden');
+
+        var selected_tab = $(this).find('a').attr('href');
+        $(selected_tab).removeClass('hidden');
+
+        return false;
+    });
+
+    $('#backup-secondary form').submit(function (e) {
+        if (!confirm('<?php _e('恢复操作将清除所有现有数据, 是否继续?'); ?>')) {
+            return false;
+        }
+    });
+</script>
+<?php include 'footer.php'; ?>

+ 23 - 0
admin/category.php

@@ -0,0 +1,23 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="form">
+            <div class="col-mb-12 col-tb-6 col-tb-offset-3">
+                <?php \Widget\Metas\Category\Edit::alloc()->form()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+include 'footer.php';
+?>

+ 142 - 0
admin/common-js.php

@@ -0,0 +1,142 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<script src="<?php $options->adminStaticUrl('js', 'jquery.js'); ?>"></script>
+<script src="<?php $options->adminStaticUrl('js', 'jquery-ui.js'); ?>"></script>
+<script src="<?php $options->adminStaticUrl('js', 'typecho.js'); ?>"></script>
+<script>
+    (function () {
+        $(document).ready(function() {
+            // 处理消息机制
+            (function () {
+                var prefix = '<?php echo \Typecho\Cookie::getPrefix(); ?>',
+                    cookies = {
+                        notice      :   $.cookie(prefix + '__typecho_notice'),
+                        noticeType  :   $.cookie(prefix + '__typecho_notice_type'),
+                        highlight   :   $.cookie(prefix + '__typecho_notice_highlight')
+                    },
+                    path = '<?php echo \Typecho\Cookie::getPath(); ?>',
+                    domain = '<?php echo \Typecho\Cookie::getDomain(); ?>',
+                    secure = <?php echo json_encode(\Typecho\Cookie::getSecure()); ?>;
+
+                if (!!cookies.notice && 'success|notice|error'.indexOf(cookies.noticeType) >= 0) {
+                    var head = $('.typecho-head-nav'),
+                        p = $('<div class="message popup ' + cookies.noticeType + '">'
+                        + '<ul><li>' + $.parseJSON(cookies.notice).join('</li><li>') 
+                        + '</li></ul></div>'), offset = 0;
+
+                    if (head.length > 0) {
+                        p.insertAfter(head);
+                        offset = head.outerHeight();
+                    } else {
+                        p.prependTo(document.body);
+                    }
+
+                    function checkScroll () {
+                        if ($(window).scrollTop() >= offset) {
+                            p.css({
+                                'position'  :   'fixed',
+                                'top'       :   0
+                            });
+                        } else {
+                            p.css({
+                                'position'  :   'absolute',
+                                'top'       :   offset
+                            });
+                        }
+                    }
+
+                    $(window).scroll(function () {
+                        checkScroll();
+                    });
+
+                    checkScroll();
+
+                    p.slideDown(function () {
+                        var t = $(this), color = '#C6D880';
+                        
+                        if (t.hasClass('error')) {
+                            color = '#FBC2C4';
+                        } else if (t.hasClass('notice')) {
+                            color = '#FFD324';
+                        }
+
+                        t.effect('highlight', {color : color})
+                            .delay(5000).fadeOut(function () {
+                            $(this).remove();
+                        });
+                    });
+
+                    $.cookie(prefix + '__typecho_notice', null, {path : path, domain: domain, secure: secure});
+                    $.cookie(prefix + '__typecho_notice_type', null, {path : path, domain: domain, secure: secure});
+                }
+
+                if (cookies.highlight) {
+                    $('#' + cookies.highlight).effect('highlight', 1000);
+                    $.cookie(prefix + '__typecho_notice_highlight', null, {path : path, domain: domain, secure: secure});
+                }
+            })();
+
+
+            // 导航菜单 tab 聚焦时展开下拉菜单
+            const menuBar = $('.menu-bar').click(function () {
+                const nav = $(this).next('#typecho-nav-list');
+                if (!$(this).toggleClass('focus').hasClass('focus')) {
+                    nav.removeClass('expanded noexpanded');
+                }
+            });
+
+            $('.main, .typecho-foot').on('click touchstart', function () {
+                if (menuBar.hasClass('focus')) {
+                    menuBar.trigger('click');
+                }
+            });
+
+            $('#typecho-nav-list ul.root').each(function () {
+                const ul = $(this), nav = ul.parent();
+                let focused = false;
+
+                ul.on('click touchend', '.parent a', function (e) {
+                    nav.removeClass('noexpanded').addClass('expanded');
+                    if ($(window).width() < 576 && e.type == 'click') {
+                        return false;
+                    }
+                }).find('.child')
+                .append($('<li class="return"><a><?php _e('返回'); ?></a></li>').click(function () {
+                    nav.removeClass('expanded').addClass('noexpanded');
+                    return false;
+                }));
+
+                $('a', ul).focus(function () {
+                    ul.addClass('expanded');
+                    focused = true;
+                }).blur(function () {
+                    focused = false;
+
+                    setTimeout(function () {
+                        if (!focused) {
+                            ul.removeClass('expanded');
+                        }
+                    });
+                });
+            });
+
+            if ($('.typecho-login').length == 0) {
+                $('a').each(function () {
+                    var t = $(this), href = t.attr('href');
+
+                    if ((href && href[0] == '#')
+                        || /^<?php echo preg_quote($options->adminUrl, '/'); ?>.*$/.exec(href) 
+                            || /^<?php echo substr(preg_quote(\Typecho\Common::url('s', $options->index), '/'), 0, -1); ?>action\/[_a-zA-Z0-9\/]+.*$/.exec(href)) {
+                        return;
+                    }
+
+                    t.attr('target', '_blank')
+                        .attr('rel', 'noopener noreferrer');
+                });
+            }
+
+            $('.main form').submit(function () {
+                $('button[type=submit]', this).attr('disabled', 'disabled');
+            });
+        });
+    })();
+</script>

+ 54 - 0
admin/common.php

@@ -0,0 +1,54 @@
+<?php
+if (!defined('__DIR__')) {
+    define('__DIR__', dirname(__FILE__));
+}
+
+define('__TYPECHO_ADMIN__', true);
+
+/** 载入配置文件 */
+if (!defined('__TYPECHO_ROOT_DIR__') && !@include_once __DIR__ . '/../config.inc.php') {
+    file_exists(__DIR__ . '/../install.php') ? header('Location: ../install.php') : print('Missing Config File');
+    exit;
+}
+
+/** 初始化组件 */
+\Widget\Init::alloc();
+
+/** 注册一个初始化插件 */
+\Typecho\Plugin::factory('admin/common.php')->begin();
+
+\Widget\Options::alloc()->to($options);
+\Widget\User::alloc()->to($user);
+\Widget\Security::alloc()->to($security);
+\Widget\Menu::alloc()->to($menu);
+
+/** 初始化上下文 */
+$request = $options->request;
+$response = $options->response;
+
+/** 检测是否是第一次登录 */
+$currentMenu = $menu->getCurrentMenu();
+
+if (!empty($currentMenu)) {
+    $params = parse_url($currentMenu[2]);
+    $adminFile = basename($params['path']);
+
+    if (!$user->logged && !\Typecho\Cookie::get('__typecho_first_run')) {
+        if ('welcome.php' != $adminFile) {
+            $response->redirect(\Typecho\Common::url('welcome.php', $options->adminUrl));
+        } else {
+            \Typecho\Cookie::set('__typecho_first_run', 1);
+        }
+    } elseif ($user->pass('administrator', true)) {
+        /** 检测版本是否升级 */
+        $mustUpgrade = version_compare(\Typecho\Common::VERSION, $options->version, '>');
+
+        if ($mustUpgrade && 'upgrade.php' != $adminFile && 'backup.php' != $adminFile) {
+            $response->redirect(\Typecho\Common::url('upgrade.php', $options->adminUrl));
+        } elseif (!$mustUpgrade && 'upgrade.php' == $adminFile) {
+            $response->redirect($options->adminUrl);
+        } elseif (!$mustUpgrade && 'welcome.php' == $adminFile && $user->logged) {
+            $response->redirect($options->adminUrl);
+        }
+    }
+}

+ 13 - 0
admin/copyright.php

@@ -0,0 +1,13 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<div class="typecho-foot" role="contentinfo">
+    <div class="copyright">
+        <a href="https://typecho.org" class="i-logo-s">Typecho</a>
+        <p><?php _e('由 <a href="https://typecho.org">%s</a> 强力驱动, 版本 %s', $options->software, $options->version); ?></p>
+    </div>
+    <nav class="resource">
+        <a href="https://docs.typecho.org"><?php _e('帮助文档'); ?></a> &bull;
+        <a href="https://forum.typecho.org"><?php _e('支持论坛'); ?></a> &bull;
+        <a href="https://github.com/typecho/typecho/issues"><?php _e('报告错误'); ?></a> &bull;
+        <a href="https://typecho.org/download"><?php _e('资源下载'); ?></a>
+    </nav>
+</div>

+ 211 - 0
admin/css/grid.css

@@ -0,0 +1,211 @@
+/* Bento Grid System Source: https://github.com/fenbox/bento Version: 1.2.8 Update: 2013.11.25 */
+.container, .row [class*="col-"] { box-sizing: border-box; }
+
+.container { margin-left: auto; margin-right: auto; padding-left: 10px; padding-right: 10px; }
+
+.row { margin-right: -10px; margin-left: -10px; }
+
+.row [class*="col-"] { float: left; min-height: 1px; padding-right: 10px; padding-left: 10px; }
+
+.row [class*="-push-"], .row [class*="-pull-"] { position: relative; }
+
+/* Mobile and up */
+.col-mb-1 { width: 8.33333%; }
+
+.col-mb-2 { width: 16.66667%; }
+
+.col-mb-3 { width: 25%; }
+
+.col-mb-4 { width: 33.33333%; }
+
+.col-mb-5 { width: 41.66667%; }
+
+.col-mb-6 { width: 50%; }
+
+.col-mb-7 { width: 58.33333%; }
+
+.col-mb-8 { width: 66.66667%; }
+
+.col-mb-9 { width: 75%; }
+
+.col-mb-10 { width: 83.33333%; }
+
+.col-mb-11 { width: 91.66667%; }
+
+.col-mb-12 { width: 100%; }
+
+/* Tablet and up */
+@media (min-width: 768px) { .container { max-width: 728px; }
+  .col-tb-1 { width: 8.33333%; }
+  .col-tb-2 { width: 16.66667%; }
+  .col-tb-3 { width: 25%; }
+  .col-tb-4 { width: 33.33333%; }
+  .col-tb-5 { width: 41.66667%; }
+  .col-tb-6 { width: 50%; }
+  .col-tb-7 { width: 58.33333%; }
+  .col-tb-8 { width: 66.66667%; }
+  .col-tb-9 { width: 75%; }
+  .col-tb-10 { width: 83.33333%; }
+  .col-tb-11 { width: 91.66667%; }
+  .col-tb-12 { width: 100%; }
+  .col-tb-offset-0 { margin-left: 0%; }
+  .col-tb-offset-1 { margin-left: 8.33333%; }
+  .col-tb-offset-2 { margin-left: 16.66667%; }
+  .col-tb-offset-3 { margin-left: 25%; }
+  .col-tb-offset-4 { margin-left: 33.33333%; }
+  .col-tb-offset-5 { margin-left: 41.66667%; }
+  .col-tb-offset-6 { margin-left: 50%; }
+  .col-tb-offset-7 { margin-left: 58.33333%; }
+  .col-tb-offset-8 { margin-left: 66.66667%; }
+  .col-tb-offset-9 { margin-left: 75%; }
+  .col-tb-offset-10 { margin-left: 83.33333%; }
+  .col-tb-offset-11 { margin-left: 91.66667%; }
+  .col-tb-offset-12 { margin-left: 100%; }
+  .col-tb-pull-0 { right: 0%; }
+  .col-tb-pull-1 { right: 8.33333%; }
+  .col-tb-pull-2 { right: 16.66667%; }
+  .col-tb-pull-3 { right: 25%; }
+  .col-tb-pull-4 { right: 33.33333%; }
+  .col-tb-pull-5 { right: 41.66667%; }
+  .col-tb-pull-6 { right: 50%; }
+  .col-tb-pull-7 { right: 58.33333%; }
+  .col-tb-pull-8 { right: 66.66667%; }
+  .col-tb-pull-9 { right: 75%; }
+  .col-tb-pull-10 { right: 83.33333%; }
+  .col-tb-pull-11 { right: 91.66667%; }
+  .col-tb-pull-12 { right: 100%; }
+  .col-tb-push-0 { left: 0%; }
+  .col-tb-push-1 { left: 8.33333%; }
+  .col-tb-push-2 { left: 16.66667%; }
+  .col-tb-push-3 { left: 25%; }
+  .col-tb-push-4 { left: 33.33333%; }
+  .col-tb-push-5 { left: 41.66667%; }
+  .col-tb-push-6 { left: 50%; }
+  .col-tb-push-7 { left: 58.33333%; }
+  .col-tb-push-8 { left: 66.66667%; }
+  .col-tb-push-9 { left: 75%; }
+  .col-tb-push-10 { left: 83.33333%; }
+  .col-tb-push-11 { left: 91.66667%; }
+  .col-tb-push-12 { left: 100%; } }
+
+/* Desktop and up */
+@media (min-width: 992px) { .container { max-width: 952px; }
+  .col-1 { width: 8.33333%; }
+  .col-2 { width: 16.66667%; }
+  .col-3 { width: 25%; }
+  .col-4 { width: 33.33333%; }
+  .col-5 { width: 41.66667%; }
+  .col-6 { width: 50%; }
+  .col-7 { width: 58.33333%; }
+  .col-8 { width: 66.66667%; }
+  .col-9 { width: 75%; }
+  .col-10 { width: 83.33333%; }
+  .col-11 { width: 91.66667%; }
+  .col-12 { width: 100%; }
+  .col-offset-0 { margin-left: 0%; }
+  .col-offset-1 { margin-left: 8.33333%; }
+  .col-offset-2 { margin-left: 16.66667%; }
+  .col-offset-3 { margin-left: 25%; }
+  .col-offset-4 { margin-left: 33.33333%; }
+  .col-offset-5 { margin-left: 41.66667%; }
+  .col-offset-6 { margin-left: 50%; }
+  .col-offset-7 { margin-left: 58.33333%; }
+  .col-offset-8 { margin-left: 66.66667%; }
+  .col-offset-9 { margin-left: 75%; }
+  .col-offset-10 { margin-left: 83.33333%; }
+  .col-offset-11 { margin-left: 91.66667%; }
+  .col-offset-12 { margin-left: 100%; }
+  .col-pull-0 { right: 0%; }
+  .col-pull-1 { right: 8.33333%; }
+  .col-pull-2 { right: 16.66667%; }
+  .col-pull-3 { right: 25%; }
+  .col-pull-4 { right: 33.33333%; }
+  .col-pull-5 { right: 41.66667%; }
+  .col-pull-6 { right: 50%; }
+  .col-pull-7 { right: 58.33333%; }
+  .col-pull-8 { right: 66.66667%; }
+  .col-pull-9 { right: 75%; }
+  .col-pull-10 { right: 83.33333%; }
+  .col-pull-11 { right: 91.66667%; }
+  .col-pull-12 { right: 100%; }
+  .col-push-0 { left: 0%; }
+  .col-push-1 { left: 8.33333%; }
+  .col-push-2 { left: 16.66667%; }
+  .col-push-3 { left: 25%; }
+  .col-push-4 { left: 33.33333%; }
+  .col-push-5 { left: 41.66667%; }
+  .col-push-6 { left: 50%; }
+  .col-push-7 { left: 58.33333%; }
+  .col-push-8 { left: 66.66667%; }
+  .col-push-9 { left: 75%; }
+  .col-push-10 { left: 83.33333%; }
+  .col-push-11 { left: 91.66667%; }
+  .col-push-12 { left: 100%; } }
+
+/* Widescreen and up */
+@media (min-width: 1200px) { .container { max-width: 1160px; }
+  .col-wd-1 { width: 8.33333%; }
+  .col-wd-2 { width: 16.66667%; }
+  .col-wd-3 { width: 25%; }
+  .col-wd-4 { width: 33.33333%; }
+  .col-wd-5 { width: 41.66667%; }
+  .col-wd-6 { width: 50%; }
+  .col-wd-7 { width: 58.33333%; }
+  .col-wd-8 { width: 66.66667%; }
+  .col-wd-9 { width: 75%; }
+  .col-wd-10 { width: 83.33333%; }
+  .col-wd-11 { width: 91.66667%; }
+  .col-wd-12 { width: 100%; }
+  .col-wd-offset-0 { margin-left: 0%; }
+  .col-wd-offset-1 { margin-left: 8.33333%; }
+  .col-wd-offset-2 { margin-left: 16.66667%; }
+  .col-wd-offset-3 { margin-left: 25%; }
+  .col-wd-offset-4 { margin-left: 33.33333%; }
+  .col-wd-offset-5 { margin-left: 41.66667%; }
+  .col-wd-offset-6 { margin-left: 50%; }
+  .col-wd-offset-7 { margin-left: 58.33333%; }
+  .col-wd-offset-8 { margin-left: 66.66667%; }
+  .col-wd-offset-9 { margin-left: 75%; }
+  .col-wd-offset-10 { margin-left: 83.33333%; }
+  .col-wd-offset-11 { margin-left: 91.66667%; }
+  .col-wd-offset-12 { margin-left: 100%; }
+  .col-wd-pull-0 { right: 0%; }
+  .col-wd-pull-1 { right: 8.33333%; }
+  .col-wd-pull-2 { right: 16.66667%; }
+  .col-wd-pull-3 { right: 25%; }
+  .col-wd-pull-4 { right: 33.33333%; }
+  .col-wd-pull-5 { right: 41.66667%; }
+  .col-wd-pull-6 { right: 50%; }
+  .col-wd-pull-7 { right: 58.33333%; }
+  .col-wd-pull-8 { right: 66.66667%; }
+  .col-wd-pull-9 { right: 75%; }
+  .col-wd-pull-10 { right: 83.33333%; }
+  .col-wd-pull-11 { right: 91.66667%; }
+  .col-wd-pull-12 { right: 100%; }
+  .col-wd-push-0 { left: 0%; }
+  .col-wd-push-1 { left: 8.33333%; }
+  .col-wd-push-2 { left: 16.66667%; }
+  .col-wd-push-3 { left: 25%; }
+  .col-wd-push-4 { left: 33.33333%; }
+  .col-wd-push-5 { left: 41.66667%; }
+  .col-wd-push-6 { left: 50%; }
+  .col-wd-push-7 { left: 58.33333%; }
+  .col-wd-push-8 { left: 66.66667%; }
+  .col-wd-push-9 { left: 75%; }
+  .col-wd-push-10 { left: 83.33333%; }
+  .col-wd-push-11 { left: 91.66667%; }
+  .col-wd-push-12 { left: 100%; } }
+
+/* Responsive kit */
+@media (max-width: 575px) { .kit-hidden-mb { display: none; } }
+
+@media (max-width: 767px) { .kit-hidden-tb { display: none; } }
+
+@media (max-width: 991px) { .kit-hidden { display: none; } }
+
+/* Clearfix */
+.clearfix, .row { zoom: 1; }
+
+.clearfix:before, .row:before, .clearfix:after, .row:after { content: " "; display: table; }
+
+.clearfix:after, .row:after { clear: both; }

+ 24 - 0
admin/css/install.css

@@ -0,0 +1,24 @@
+h1 { text-align: center; }
+
+details summary { cursor: pointer; }
+
+@keyframes fadein { from { opacity: 0; }
+  to { opacity: 1; } }
+
+.fresh .keep-word { display: none; }
+
+.keep .fresh-word { display: none; }
+
+form > .message { display: none; padding: 20px; border-radius: 5px; }
+
+.message textarea { width: 100%; height: 200px; resize: none; margin: 10px 0; }
+
+.message.fade { display: block; animation: fadein .5s linear; }
+
+.message *:last-child { margin-bottom: 0; }
+
+.message p { margin-top: 10px; }
+
+.message p button { margin-left: 5px; }
+
+.message p button:first-child { margin-left: 0; }

+ 341 - 0
admin/css/normalize.css

@@ -0,0 +1,341 @@
+/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+   ========================================================================== */
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+
+html {
+  line-height: 1.15; /* 1 */
+  -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/* Sections
+   ========================================================================== */
+
+/**
+ * Remove the margin in all browsers.
+ */
+
+body {
+  margin: 0;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+/* Grouping content
+   ========================================================================== */
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+  box-sizing: content-box; /* 1 */
+  height: 0; /* 1 */
+  overflow: visible; /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+   ========================================================================== */
+
+/**
+ * Remove the gray background on active links in IE 10.
+ */
+
+a {
+  background-color: transparent;
+}
+
+/**
+ * 1. Remove the bottom border in Chrome 57-
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+  border-bottom: none; /* 1 */
+  text-decoration: underline; /* 2 */
+  text-decoration: underline dotted; /* 2 */
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+  font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+  font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+sup {
+  top: -0.5em;
+}
+
+/* Embedded content
+   ========================================================================== */
+
+/**
+ * Remove the border on images inside links in IE 10.
+ */
+
+img {
+  border-style: none;
+}
+
+/* Forms
+   ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers.
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+  font-family: inherit; /* 1 */
+  font-size: 100%; /* 1 */
+  line-height: 1.15; /* 1 */
+  margin: 0; /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input { /* 1 */
+  overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select { /* 1 */
+  text-transform: none;
+}
+
+/**
+ * Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+  -webkit-appearance: button;
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+  border-style: none;
+  padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+  outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+
+fieldset {
+  padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ *    `fieldset` elements in all browsers.
+ */
+
+legend {
+  box-sizing: border-box; /* 1 */
+  color: inherit; /* 2 */
+  display: table; /* 1 */
+  max-width: 100%; /* 1 */
+  padding: 0; /* 3 */
+  white-space: normal; /* 1 */
+}
+
+/**
+ * Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+  vertical-align: baseline;
+}
+
+/**
+ * Remove the default vertical scrollbar in IE 10+.
+ */
+
+textarea {
+  overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10.
+ * 2. Remove the padding in IE 10.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+  box-sizing: border-box; /* 1 */
+  padding: 0; /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+  -webkit-appearance: textfield; /* 1 */
+  outline-offset: -2px; /* 2 */
+}
+
+/**
+ * Remove the inner padding in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+  -webkit-appearance: button; /* 1 */
+  font: inherit; /* 2 */
+}
+
+/* Interactive
+   ========================================================================== */
+
+/*
+ * Add the correct display in Edge, IE 10+, and Firefox.
+ */
+
+details {
+  display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+  display: list-item;
+}
+
+/* Misc
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 10+.
+ */
+
+template {
+  display: none;
+}
+
+/**
+ * Add the correct display in IE 10.
+ */
+
+[hidden] {
+  display: none;
+}

+ 944 - 0
admin/css/style.css

@@ -0,0 +1,944 @@
+@charset "UTF-8";
+/* vim: set et sw=2 ts=2 sts=2 fdm=marker ff=unix fenc=utf8 */
+/** Typecho 后台样式 @author  Typecho Team @since   2008-09-26 @update  2013-11-02 @link    http://www.typecho.org/ @version 0.9 */
+/** Typecho 全局样式 */
+html { height: 100%; }
+
+body { font-family: 'Helvetica Neue', Helvetica, Arial, -apple-system, system-ui, sans-serif; background: #F6F6F3; color: #444; font-size: 87.5%; line-height: 1.5; }
+
+a { color: #467B96; text-decoration: none; }
+
+a:hover { color: #499BC3; text-decoration: underline; }
+
+code, pre, .mono { font-family: 'SF Mono', Menlo, Monaco, Consolas, 'Courier New', -apple-system, system-ui, monospace; }
+
+.p { margin: 1em 0; }
+
+.body-100 { height: 100%; }
+
+a.balloon-button { display: inline-block; padding: 0 6px; min-width: 12px; height: 18px; line-height: 18px; background: #D8E7EE; font-size: .85714em; text-align: center; text-decoration: none; /** 修正ie中文不对齐 */ zoom: 1; border-radius: 30px; white-space: nowrap; }
+
+a.button:hover, a.balloon-button:hover { background-color: #A5CADC; color: #FFF; text-decoration: none; }
+
+/** Forms */
+input[type=text], input[type=password], input[type=email], textarea { background: #FFF; border: 1px solid #D9D9D6; padding: 7px; border-radius: 2px; box-sizing: border-box; }
+
+input[type=text]:disabled, input[type=text]:read-only, input[type=password]:disabled, input[type=password]:read-only, input[type=email]:disabled, input[type=email]:read-only, textarea:disabled, textarea:read-only { background: #F3F3F3; }
+
+textarea { resize: vertical; line-height: 1.5; }
+
+input[type="radio"], input[type="checkbox"] { margin-right: 3px; }
+
+input[type="radio"], input[type="checkbox"], input[type="radio"] + label, input[type="checkbox"] + label { vertical-align: middle; }
+
+input.text-s, textarea.text-s { padding: 5px; }
+
+input.text-l, textarea.text-l { padding: 10px; font-size: 1.14286em; }
+
+.w-10 { width: 10%; }
+
+.w-20 { width: 20%; }
+
+.w-30 { width: 30%; }
+
+.w-40 { width: 40%; }
+
+.w-50 { width: 50%; }
+
+.w-60 { width: 60%; }
+
+.w-70 { width: 70%; }
+
+.w-80 { width: 80%; }
+
+.w-90 { width: 90%; }
+
+.w-100 { width: 100%; }
+
+select { border: 1px solid #CCC; height: 28px; }
+
+/** Buttons */
+.btn, #ui-datepicker-div .ui-datepicker-current, #ui-datepicker-div .ui-datepicker-close { border: none; background-color: #E9E9E6; cursor: pointer; border-radius: 2px; display: inline-block; padding: 0 12px; height: 32px; color: #666; vertical-align: middle; zoom: 1; }
+
+.btn:hover, #ui-datepicker-div .ui-datepicker-current:hover, #ui-datepicker-div .ui-datepicker-close:hover { transition-duration: .4s; background-color: #dbdbd6; }
+
+.btn:active, #ui-datepicker-div .ui-datepicker-current:active, #ui-datepicker-div .ui-datepicker-close:active, .btn.active, #ui-datepicker-div .active.ui-datepicker-current, #ui-datepicker-div .active.ui-datepicker-close { background-color: #d6d6d0; }
+
+.btn:disabled, #ui-datepicker-div .ui-datepicker-current:disabled, #ui-datepicker-div .ui-datepicker-close:disabled { background-color: #f7f7f6; cursor: default; }
+
+.btn:disabled, #ui-datepicker-div .ui-datepicker-current:disabled, #ui-datepicker-div .ui-datepicker-close:disabled { color: #999; }
+
+.btn-xs, #ui-datepicker-div .ui-datepicker-current, #ui-datepicker-div .ui-datepicker-close { padding: 0 10px; height: 25px; font-size: 13px; }
+
+.btn-s { height: 28px; }
+
+.btn-l { height: 40px; font-size: 1.14286em; font-weight: bold; }
+
+.primary { border: none; background-color: #467B96; cursor: pointer; border-radius: 2px; color: #FFF; }
+
+.primary:hover { transition-duration: .4s; background-color: #3c6a81; }
+
+.primary:active, .primary.active { background-color: #39647a; }
+
+.primary:disabled { background-color: #508cab; cursor: default; }
+
+.btn-group { display: inline-block; }
+
+.btn-warn { border: none; background-color: #B94A48; cursor: pointer; border-radius: 2px; color: #FFF; }
+
+.btn-warn:hover { transition-duration: .4s; background-color: #a4403f; }
+
+.btn-warn:active, .btn-warn.active { background-color: #9c3e3c; }
+
+.btn-warn:disabled { background-color: #c1605e; cursor: default; }
+
+.btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active, .btn-link.active { background-color: transparent; }
+
+/* 下拉菜单 */
+.btn-drop { position: relative; }
+
+.dropdown-toggle { padding-right: 8px; }
+
+.dropdown-menu { list-style: none; position: absolute; z-index: 2; left: 0; margin: 0; padding: 0; border: 1px solid #D9D9D6; background: #FFF; text-align: left; min-width: 108px; display: none; }
+
+.dropdown-menu li { white-space: nowrap; }
+
+.dropdown-menu li.multiline { padding: 5px 12px 12px; }
+
+.dropdown-menu a { display: block; padding: 5px 12px; color: #666; }
+
+.dropdown-menu a:hover { background: #F6F6F3; text-decoration: none !important; }
+
+/** 提示信息框 */
+.message { padding: 8px 10px; border-radius: 2px; }
+
+.message a { font-weight: bold; text-decoration: underline; }
+
+.error { background: #FBE3E4; color: #8A1F11; }
+
+.error a { color: #8A1F11; }
+
+.notice { background: #FFF6BF; color: #8A6D3B; }
+
+.notice a { color: #8A6D3B; }
+
+.success { background: #E6EFC2; color: #264409; }
+
+.success a { color: #264409; }
+
+.balloon { display: inline-block; padding: 0 4px; min-width: 10px; height: 14px; line-height: 14px; background: #B9B9B6; vertical-align: text-top; text-align: center; font-size: 12px; color: #FFF; border-radius: 20px; }
+
+/** 后台分页 */
+.typecho-pager { list-style: none; float: right; margin: 0; padding: 0; line-height: 1; text-align: center; zoom: 1; }
+
+.typecho-pager li { display: inline-block; margin: 0 3px; height: 28px; line-height: 28px; }
+
+.typecho-pager a { display: block; padding: 0 10px; border-radius: 2px; }
+
+.typecho-pager a:hover { text-decoration: none; background: #E9E9E6; }
+
+.typecho-pager li.current a { background: #E9E9E6; color: #444; }
+
+/** 后台头部导航 */
+.typecho-head-nav { padding: 0 10px; background: #292D33; position: relative; }
+
+.typecho-head-nav a, .typecho-head-nav button.menu-bar { padding: 0 20px; height: 36px; line-height: 36px; color: #BBB; }
+
+.typecho-head-nav a:focus, .typecho-head-nav a:hover, .typecho-head-nav button.menu-bar:focus, .typecho-head-nav button.menu-bar:hover { color: #FFF; text-decoration: none; }
+
+.typecho-head-nav button.menu-bar { display: none; }
+
+.typecho-head-nav #typecho-nav-list { float: left; }
+
+.typecho-head-nav #typecho-nav-list > ul { list-style: none; margin: 0; padding: 0; position: relative; float: left; }
+
+.typecho-head-nav #typecho-nav-list > ul:first-child { border-left: 1px solid #383D45; }
+
+.typecho-head-nav #typecho-nav-list > ul .parent a { display: inline-block; border-right: 1px solid #383D45; background: #292D33; }
+
+.typecho-head-nav #typecho-nav-list > ul .child { position: absolute; list-style: none; top: 36px; display: none; margin: 0; padding: 0; min-width: 160px; max-width: 240px; background: #202328; z-index: 250; }
+
+.typecho-head-nav #typecho-nav-list > ul .child li.return { display: none; }
+
+.typecho-head-nav #typecho-nav-list > ul .child li a { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; }
+
+.typecho-head-nav #typecho-nav-list > ul .child li a:hover, .typecho-head-nav #typecho-nav-list > ul .child li a:focus { background: #292D33; }
+
+.typecho-head-nav #typecho-nav-list > ul .child li.focus a { color: #6DA1BB; font-weight: bold; }
+
+.typecho-head-nav #typecho-nav-list > ul .parent a:hover, .typecho-head-nav #typecho-nav-list > ul.focus .parent a, .typecho-head-nav #typecho-nav-list > ul.root:hover .parent a { background: #202328; }
+
+.typecho-head-nav #typecho-nav-list > ul.focus .parent a { font-weight: bold; }
+
+.typecho-head-nav #typecho-nav-list > ul.root:hover .child, .typecho-head-nav #typecho-nav-list > ul.root.expanded .child { display: block; }
+
+.typecho-head-nav .operate { float: right; }
+
+.typecho-head-nav .operate a { display: inline-block; margin-left: -1px; border: 1px solid #383D45; border-width: 0 1px; }
+
+.typecho-head-nav .operate a:hover { background-color: #202328; }
+
+@media (max-width: 575px) { @keyframes out { from { left: 0%; }
+    to { left: -100%; } }
+  @keyframes in { from { left: -100%; }
+    to { left: 0%; } }
+  .typecho-head-nav { padding: 0; position: fixed; bottom: 0; width: 100%; z-index: 10; }
+  .typecho-head-nav #typecho-nav-list { display: none; }
+  .typecho-head-nav .operate a:last-child { border-right-width: 0; }
+  .typecho-head-nav button.menu-bar { display: inline-block; border: none; background: #292D33; border-right: 1px solid #383D45; }
+  .typecho-head-nav button.menu-bar.focus { color: #FFF; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list { display: block; float: none; position: absolute; bottom: 36px; width: 100%; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list > ul { float: none; border-bottom: 1px solid #383D45; position: static; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list > ul:first-child { border-left: none; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list > ul .parent a { display: block; border: none; background: #202328; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list > ul .child { position: absolute; bottom: 0; left: 100%; top: auto; z-index: 20; width: 100%; max-width: 100%; min-width: auto; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list > ul .child li { border-bottom: 1px solid #383D45; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list > ul .child li.return { display: block; text-align: center; font-size: 12px; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list > ul .child li.return a { color: #777; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list.expanded { animation: out .15s ease-out forwards; }
+  .typecho-head-nav button.menu-bar.focus + #typecho-nav-list.noexpanded { animation: in .15s ease-out forwards; } }
+
+/** 注脚 */
+.typecho-foot { padding: 4em 0 3em; color: #999; line-height: 1.8; text-align: center; }
+
+.typecho-foot .copyright p { margin: 10px 0 0; }
+
+.typecho-foot .resource { color: #CCC; }
+
+.typecho-foot .resource a { margin: 0 3px; color: #999; }
+
+/** 顶部消息样式 by 70 */
+.popup { display: none; position: absolute; top: 0; left: 0; margin: 0; padding: 8px 0; border: none; width: 100%; z-index: 10; text-align: center; border-radius: 0; }
+
+.popup ul { list-style: none; margin: 0; padding: 0; text-align: center; }
+
+.popup ul li { display: inline-block; margin-right: 10px; }
+
+/** logo 的样式 */
+/** 载入状态 */
+.loading { padding-left: 20px !important; background: transparent url(../img/ajax-loader.gif) no-repeat left center; }
+
+/** 典型配置选项 */
+.typecho-option { list-style: none; margin: 1em 0; padding: 0; }
+
+.typecho-option-submit li { border-bottom: none; }
+
+.typecho-option label.typecho-label { display: block; margin-bottom: .5em; font-weight: bold; }
+
+.typecho-option label.required:after { content: " *"; color: #B94A48; }
+
+.typecho-option span { margin-right: 15px; }
+
+.typecho-option .description { margin: .5em 0 0; color: #999; font-size: .92857em; }
+
+.typecho-option input.file { width: 100%; margin: .7em 0; }
+
+.front-archive { padding-left: 1.5em; }
+
+.profile-avatar { width: 220px; height: 220px; border-radius: 10px; }
+
+/** 增加配置面板内部的错误样式 by 70 */
+/** 欢迎界面 */
+#typecho-welcome { margin: 1em 0; padding: 1em 2em; background-color: #E9E9E6; }
+
+.welcome-board { color: #999; font-size: 1.15em; }
+
+.welcome-board em { color: #444; font-size: 2em; font-style: normal; font-family: Georgia, serif; }
+
+#start-link { margin-bottom: 25px; padding: 0 0 35px; border-bottom: 1px solid #ECECEC; }
+
+#start-link li { float: left; margin-right: 1.5em; }
+
+#start-link .balloon { margin-top: 2px; }
+
+.latest-link li { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+
+.latest-link span { display: inline-block; margin-right: 4px; padding-right: 8px; border-right: 1px solid #ECECEC; width: 37px; text-align: right; color: #999; }
+
+.update-check { font-size: 14px; }
+
+/** 登录框 */
+.typecho-login-wrap { display: table; margin: 0 auto; height: 100%; }
+
+.typecho-login { display: table-cell; padding: 30px 0 100px; width: 280px; text-align: center; vertical-align: middle; }
+
+.typecho-login h1 { margin: 0 0 1em; }
+
+.typecho-login .more-link { margin-top: 2em; color: #CCC; }
+
+.typecho-login .more-link a { margin: 0 3px; }
+
+/** 标题 */
+.typecho-page-title h2 { margin: 25px 0 10px; font-size: 1.28571em; }
+
+.typecho-page-title h2 a { margin-left: 10px; padding: 3px 8px; background: #E9E9E6; font-size: .8em; border-radius: 2px; }
+
+.typecho-page-title h2 a:hover { text-decoration: none; }
+
+/** 后台页面主体 */
+/** 主页主体 */
+.typecho-dashboard ul { list-style: none; padding: 0; }
+
+.typecho-dashboard li { margin-bottom: 5px; }
+
+/** 标签页 */
+.typecho-option-tabs { list-style: none; margin: 1em 0 0; padding: 0; font-size: 13px; text-align: center; }
+
+.typecho-option-tabs.fix-tabs { margin-bottom: 1em; }
+
+.typecho-option-tabs a { display: block; margin-right: -1px; border: 1px solid #D9D9D6; padding: 0 15px; height: 26px; line-height: 26px; color: #666; box-sizing: border-box; }
+
+.typecho-option-tabs a:hover { background-color: #E9E9E6; color: #666; text-decoration: none; }
+
+.typecho-option-tabs li { float: left; }
+
+.typecho-option-tabs li:first-child a { border-radius: 2px 0 0 2px; }
+
+.typecho-option-tabs li:last-child a { border-radius: 0 2px 2px 0; }
+
+.typecho-option-tabs.right { float: right; }
+
+.typecho-option-tabs li.current a, .typecho-option-tabs li.active a { background-color: #E9E9E6; }
+
+/** 表格列表页 */
+/** 列表页选项 */
+.typecho-list-operate { margin: 1em 0; }
+
+.typecho-list-operate input, .typecho-list-operate button, .typecho-list-operate select { vertical-align: bottom; }
+
+.typecho-list-operate input[type="checkbox"] { vertical-align: text-top; }
+
+@media (min-width: 576px) { .typecho-list-operate .operate { float: left; }
+  .typecho-list-operate .search { float: right; } }
+
+.typecho-list-operate span.operate-delete, a.operate-delete, .typecho-list-operate span.operate-button-delete, a.operate-button-delete { color: #B94A48; }
+
+a.operate-edit { color: #007700; }
+
+a.operate-reply { color: #545c30; }
+
+.typecho-list-operate a:hover { text-decoration: none; }
+
+/** 列表表格 */
+/** 增加表格标题 by 70 */
+.typecho-list-table-title { margin: 1em 0; color: #999; text-align: center; }
+
+.typecho-table-wrap { padding: 30px; background: #FFF; }
+
+.typecho-list-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
+
+.typecho-list-table.deactivate { color: #999; }
+
+.typecho-list-table .right { text-align: right; }
+
+.typecho-list-table th { padding: 0 10px 10px; border-bottom: 2px solid #F0F0EC; text-align: left; }
+
+.typecho-list-table td { padding: 10px; border-top: 1px solid #F0F0EC; word-break: break-all; }
+
+.typecho-list-table td pre { overflow: auto; }
+
+.typecho-list-table .status { margin-left: 5px; color: #999; font-size: .92857em; font-style: normal; }
+
+.typecho-list-table tbody tr:hover td { background-color: #F6F6F3; }
+
+.typecho-list-table tbody tr.checked td { background-color: #FFF9E8; }
+
+.typecho-list-table tr td .hidden-by-mouse { opacity: 0; }
+
+.typecho-list-table tr:hover td .hidden-by-mouse { opacity: 1; }
+
+.warning { color: #B94A48; }
+
+/** 评论管理 */
+.comment-reply-content { position: relative; margin: 1em 0; padding: 0 1em; border: 1px solid transparent; background-color: #F0F0EC; }
+
+.comment-reply-content:after { position: absolute; right: 1em; border: 8px solid #F0F0EC; border-color: #F0F0EC #F0F0EC transparent transparent; content: " "; }
+
+.comment-meta span, .comment-date { font-size: .92857em; color: #999; }
+
+.comment-action a, .comment-action span { margin-right: 4px; }
+
+.comment-edit label { display: block; }
+
+.comment-content img { max-width: 100%; }
+
+/** 评论回复 */
+#typecho-respond { padding: 10px; display: none; }
+
+/** 模板列表 */
+.typecho-theme-list img { margin: 1em 0; max-width: 100%; max-height: 240px; }
+
+.typecho-theme-list cite { font-style: normal; color: #999; }
+
+.typecho-theme-list tbody tr.current td { background-color: #FFF9E8; }
+
+/** 后台配置项 */
+.typecho-page-main .typecho-option input.text { width: 100%; }
+
+.typecho-page-main .typecho-option input.num { width: 40px; }
+
+.typecho-page-main .typecho-option textarea { width: 100%; height: 100px; }
+
+.typecho-page-main .typecho-option .multiline { display: block; margin: .3em 0; }
+
+.typecho-page-main .typecho-option .multiline.hidden { display: none; }
+
+/** 编辑模板 */
+.typecho-select-theme { height: 25px; line-height: 25px; margin: 15px 0px; }
+
+.typecho-select-theme h5 { color: #E47E00; font-weight: bold; float: left; font-size: 14px; width: 120px; margin-right: 10px; }
+
+.typecho-select-theme select { width: 150px; }
+
+/** 编辑模板(编辑详情) */
+.typecho-edit-theme ul { list-style: none; margin: 0; padding: 0; }
+
+.typecho-edit-theme li { padding: 3px 10px; }
+
+.typecho-edit-theme .current { background-color: #E6E6E3; }
+
+.typecho-edit-theme .current a { color: #444; }
+
+.typecho-edit-theme textarea { font-size: .92857em; line-height: 1.2; height: 500px; }
+
+/** 编写页面 */
+.typecho-post-area .edit-draft-notice { color: #999; font-size: .92857em; }
+
+.typecho-post-area .edit-draft-notice a { color: #B94A48; }
+
+.typecho-post-area .typecho-label { display: block; margin: 1em 0 -0.5em; font-weight: bold; }
+
+.typecho-post-area #auto-save-message { display: block; margin-top: 0.5em; color: #999; font-size: .92857em; }
+
+.typecho-post-area .submit .right button { margin-left: 5px; }
+
+.typecho-post-area .right { float: right; }
+
+.typecho-post-area .left { float: left; }
+
+.typecho-post-area .out-date { border: 1px solid #D3DBB3; padding: 3px; background: #fff; }
+
+.typecho-post-area input.title { font-size: 1.17em; font-weight: bold; }
+
+.typecho-post-area .url-slug { margin-top: -0.5em; color: #AAA; font-size: .92857em; word-break: break-word; }
+
+.typecho-post-area #slug { padding: 2px; border: none; background: #FFFBCC; color: #666; }
+
+.typecho-post-area #text { resize: none; }
+
+#advance-panel { display: none; }
+
+#custom-field { margin: 1em 0; padding: 10px 15px; background: #FFF; }
+
+#custom-field.fold table, #custom-field.fold .description { display: none; }
+
+#custom-field .description { margin-top: 10px; text-align: right; }
+
+#custom-field .description button { float: left; }
+
+#custom-field p.description { text-align: left; }
+
+#custom-field .typecho-label { margin: 0; }
+
+#custom-field .typecho-label a { display: block; color: #444; }
+
+#custom-field .typecho-label a:hover { color: #467B96; text-decoration: none; }
+
+#custom-field table { margin-top: 10px; }
+
+#custom-field td { padding: 10px 5px; font-size: .92857em; border-bottom: 1px solid #F0F0EC; vertical-align: top; }
+
+#custom-field td label { font-size: 1em; font-weight: normal; }
+
+#custom-field select { height: 27px; }
+
+.typecho-post-area .is-draft { background: #FFF1A8; }
+
+.typecho-post-option .description { margin-top: -0.5em; color: #999; font-size: .92857em; }
+
+.category-option ul { list-style: none; border: 1px solid #D9D9D6; padding: 6px 12px; max-height: 240px; overflow: auto; background-color: #FFF; border-radius: 2px; }
+
+.category-option li { margin: 3px 0; }
+
+.visibility-option ul, .allow-option ul { list-style: none; padding: 0; }
+
+/** 标签列表 */
+.typecho-page-main ul.tag-list { list-style: none; margin: 0; padding: 20px; background-color: #FFF; }
+
+.typecho-page-main ul.tag-list li { display: inline-block; margin: 0 0 5px 0; padding: 5px 5px 5px 10px; cursor: pointer; }
+
+.typecho-page-main ul.tag-list li:hover { background-color: #E9E9E6; }
+
+.typecho-page-main ul.tag-list li input { display: none; }
+
+.typecho-page-main ul.tag-list li.checked { background-color: #FFFBCC; }
+
+.typecho-page-main ul.tag-list li.size-5 { font-size: 1em; }
+
+.typecho-page-main ul.tag-list li.size-10 { font-size: 1.2em; }
+
+.typecho-page-main ul.tag-list li.size-20 { font-size: 1.4em; }
+
+.typecho-page-main ul.tag-list li.size-30 { font-size: 1.6em; }
+
+.typecho-page-main ul.tag-list li.size-0 { font-size: 1.8em; }
+
+.typecho-page-main .tag-edit-link { visibility: hidden; }
+
+.typecho-page-main li:hover .tag-edit-link { visibility: visible; }
+
+.typecho-attachment-photo { border: 1px solid #E6E6E3; max-width: 100%; }
+
+/* Upload */
+#upload-panel { border: 1px dashed #D9D9D6; background-color: #FFF; color: #999; font-size: .92857em; }
+
+#upload-panel.drag { background-color: #FFFBCC; }
+
+.upload-area { padding: 15px; text-align: center; }
+
+#file-list { list-style: none; margin: 0 10px; padding: 0; max-height: 450px; overflow: auto; word-break: break-all; }
+
+#file-list li, #file-list .insert { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
+
+#file-list li { padding: 8px 0; border-top: 1px dashed #D9D9D6; }
+
+#file-list .insert { display: block; max-width: 100%; }
+
+#file-list .file { margin-left: 5px; }
+
+#file-list .info { text-transform: uppercase; }
+
+#btn-fullscreen-upload { visibility: hidden; }
+
+/** 附件管理 */
+.edit-media button { margin-right: 6px; }
+
+/* 拖动调整 textarea 大小 */
+.resize { display: block; margin: 2px auto 0; padding: 2px 0; border: 1px solid #D9D9D6; border-width: 1px 0; width: 60px; cursor: row-resize; }
+
+.resize i { display: block; height: 1px; background-color: #D9D9D6; }
+
+/* 拖动排序 */
+.tDnD_whileDrag { background-color: #FFFBCC; }
+
+@media (max-width: 575px) { .typecho-list-operate .search { margin-top: 10px; }
+  .typecho-table-wrap { padding: 10px; margin: 0 -10px; }
+  .typecho-option-submit button[type="submit"] { width: 100%; }
+  .profile-avatar { width: 110px; height: 110px; } }
+
+/** 导入扩展样式 */
+/** icons */
+.icons-sprite, .icons-icon-delete, .icons-icon-edit, .icons-icon-exlink, .icons-icon-upload-active, .icons-icon-upload, .icons-mime-application, .icons-mime-archive, .icons-mime-audio, .icons-mime-html, .icons-mime-image, .icons-mime-office, .icons-mime-script, .icons-mime-text, .icons-mime-unknow, .icons-mime-video, .i-edit, .i-delete, .i-upload, .i-upload-active, .i-exlink, .mime-office, .mime-text, .mime-image, .mime-html, .mime-archive, .mime-application, .mime-audio, .mime-script, .mime-video, .mime-unknow { background-image: url("../img/icons.png?_=01c3ae1"); background-repeat: no-repeat; }
+
+.icons-icon-delete { background-position: 0 0; width: 16px; height: 16px; }
+
+.icons-icon-edit { background-position: 0 -16px; width: 16px; height: 16px; }
+
+.icons-icon-exlink { background-position: 0 -32px; width: 16px; height: 16px; }
+
+.icons-icon-upload-active { background-position: 0 -208px; width: 24px; height: 24px; }
+
+.icons-icon-upload { background-position: 0 -232px; width: 24px; height: 24px; }
+
+.icons-icon-upload:active, .icons-icon-upload.icon-upload-active { background-position: 0 -208px; }
+
+.icons-mime-application { background-position: 0 -48px; width: 16px; height: 16px; }
+
+.icons-mime-archive { background-position: 0 -64px; width: 16px; height: 16px; }
+
+.icons-mime-audio { background-position: 0 -80px; width: 16px; height: 16px; }
+
+.icons-mime-html { background-position: 0 -96px; width: 16px; height: 16px; }
+
+.icons-mime-image { background-position: 0 -112px; width: 16px; height: 16px; }
+
+.icons-mime-office { background-position: 0 -128px; width: 16px; height: 16px; }
+
+.icons-mime-script { background-position: 0 -144px; width: 16px; height: 16px; }
+
+.icons-mime-text { background-position: 0 -160px; width: 16px; height: 16px; }
+
+.icons-mime-unknow { background-position: 0 -176px; width: 16px; height: 16px; }
+
+.icons-mime-video { background-position: 0 -192px; width: 16px; height: 16px; }
+
+@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { .icons-sprite, .icons-icon-delete, .icons-icon-edit, .icons-icon-exlink, .icons-icon-upload-active, .icons-icon-upload, .icons-mime-application, .icons-mime-archive, .icons-mime-audio, .icons-mime-html, .icons-mime-image, .icons-mime-office, .icons-mime-script, .icons-mime-text, .icons-mime-unknow, .icons-mime-video, .i-edit, .i-delete, .i-upload, .i-upload-active, .i-exlink, .mime-office, .mime-text, .mime-image, .mime-html, .mime-archive, .mime-application, .mime-audio, .mime-script, .mime-video, .mime-unknow { background-image: url("../img/icons@2x.png?_=e65bc46"); background-repeat: no-repeat; background-size: 24px 256px; }
+  .icons-icon-delete { background-position: 0 0; }
+  .icons-icon-edit { background-position: 0 -16px; }
+  .icons-icon-exlink { background-position: 0 -32px; }
+  .icons-icon-upload-active { background-position: 0 -208px; }
+  .icons-icon-upload { background-position: 0 -232px; }
+  .icons-icon-upload:active, .icons-icon-upload.icon-upload-active { background-position: 0 -208px; }
+  .icons-mime-application { background-position: 0 -48px; }
+  .icons-mime-archive { background-position: 0 -64px; }
+  .icons-mime-audio { background-position: 0 -80px; }
+  .icons-mime-html { background-position: 0 -96px; }
+  .icons-mime-image { background-position: 0 -112px; }
+  .icons-mime-office { background-position: 0 -128px; }
+  .icons-mime-script { background-position: 0 -144px; }
+  .icons-mime-text { background-position: 0 -160px; }
+  .icons-mime-unknow { background-position: 0 -176px; }
+  .icons-mime-video { background-position: 0 -192px; }
+  .i-edit { background-position: 0 -16px; }
+  .i-delete { background-position: 0 0; }
+  .i-upload { background-position: 0 -232px; }
+  .i-upload:active, .i-upload.icon-upload-active { background-position: 0 -208px; }
+  .i-upload-active { background-position: 0 -208px; }
+  .i-exlink { background-position: 0 -32px; }
+  .mime-office { background-position: 0 -128px; }
+  .mime-text { background-position: 0 -160px; }
+  .mime-image { background-position: 0 -112px; }
+  .mime-html { background-position: 0 -96px; }
+  .mime-archive { background-position: 0 -64px; }
+  .mime-application { background-position: 0 -48px; }
+  .mime-audio { background-position: 0 -80px; }
+  .mime-script { background-position: 0 -144px; }
+  .mime-video { background-position: 0 -192px; }
+  .mime-unknow { background-position: 0 -176px; } }
+
+/* @mixin sprite-background($name) { // background-image: sprite-url($sprites); // background-position: sprite-position($sprites, $name); @include icons-sprite($name); // background-repeat: no-repeat; // display: block; // height: image-height(sprite-file($sprites, $name)); // width: image-width(sprite-file($sprites, $name)); @media  (-webkit-min-device-pixel-ratio: 2),  (min-resolution: 192dpi) { @include icons-2x-sprite($name); // Workaround for https://gist.github.com/2140082 //@if (sprite-position($sprites, $name) != sprite-position($sprites-retina, $name)) { //  $ypos: round(nth(sprite-position($sprites-retina, $name), 2) / 2); //  background-position: 0 $ypos; //} // Hard coded width of the normal sprite image. There must be a smarter way to do this. // @include background-size(auto 256px); // background-image: sprite-url($sprites-retina); } } */
+.i-edit, .i-delete, .i-exlink, .mime-office, .mime-text, .mime-image, .mime-html, .mime-archive, .mime-application, .mime-audio, .mime-script, .mime-video, .mime-unknow, .i-upload, .i-upload-active { display: inline-block; vertical-align: text-bottom; text-indent: -9999em; }
+
+.i-edit:hover, .i-delete:hover, .i-exlink:hover, .mime-office:hover, .mime-text:hover, .mime-image:hover, .mime-html:hover, .mime-archive:hover, .mime-application:hover, .mime-audio:hover, .mime-script:hover, .mime-video:hover, .mime-unknow:hover, .i-upload:hover, .i-upload-active:hover { opacity: 0.75; }
+
+.i-edit, .i-delete, .i-exlink, .mime-office, .mime-text, .mime-image, .mime-html, .mime-archive, .mime-application, .mime-audio, .mime-script, .mime-video, .mime-unknow { width: 16px; height: 16px; }
+
+.i-upload, .i-upload-active { width: 24px; height: 24px; }
+
+.i-edit { background-position: 0 -16px; }
+
+.i-delete { background-position: 0 0; }
+
+.i-upload { background-position: 0 -232px; }
+
+.i-upload:active, .i-upload.icon-upload-active { background-position: 0 -208px; }
+
+.i-upload-active { background-position: 0 -208px; }
+
+.i-caret-up, .i-caret-down, .i-caret-left, .i-caret-right { display: inline-block; border-style: solid; border-color: transparent transparent #BBB transparent; border-width: 3px 4px 5px; }
+
+.i-caret-down { border-color: #BBB transparent transparent transparent; border-width: 5px 4px 3px; }
+
+.i-caret-left { border-color: transparent #BBB transparent transparent; border-width: 4px 5px 4px 3px; }
+
+.i-caret-right { border-color: transparent transparent transparent #BBB; border-width: 4px 3px 4px 5px; }
+
+.i-exlink { background-position: 0 -32px; }
+
+/* 文件类型图标 */
+.mime-office { background-position: 0 -128px; }
+
+.mime-text { background-position: 0 -160px; }
+
+.mime-image { background-position: 0 -112px; }
+
+.mime-html { background-position: 0 -96px; }
+
+.mime-archive { background-position: 0 -64px; }
+
+.mime-application { background-position: 0 -48px; }
+
+.mime-audio { background-position: 0 -80px; }
+
+.mime-script { background-position: 0 -144px; }
+
+.mime-video { background-position: 0 -192px; }
+
+.mime-unknow { background-position: 0 -176px; }
+
+/* Logo 图标 */
+.i-logo, .i-logo-s { width: 169px; height: 40px; display: inline-block; background: url("../img/typecho-logo.svg") no-repeat; text-indent: -9999em; background-size: auto 40px; opacity: .15; }
+
+.i-logo:hover, .i-logo-s:hover { opacity: .2; }
+
+.i-logo-s { width: 26px; height: 26px; background-size: auto 26px; }
+
+/* Editor */
+.editor { margin-bottom: -0.5em; }
+
+.editor-sprite, .editor-editor-bold, .editor-editor-code, .editor-editor-exit-fullscreen, .editor-editor-fullscreen, .editor-editor-heading, .editor-editor-hr, .editor-editor-image, .editor-editor-italic, .editor-editor-link, .editor-editor-more, .editor-editor-olist, .editor-editor-quote, .editor-editor-redo, .editor-editor-ulist, .editor-editor-undo, .wmd-button-row li#wmd-bold-button span, .wmd-button-row li#wmd-italic-button span, .wmd-button-row li#wmd-link-button span, .wmd-button-row li#wmd-quote-button span, .wmd-button-row li#wmd-code-button span, .wmd-button-row li#wmd-image-button span, .wmd-button-row li#wmd-olist-button span, .wmd-button-row li#wmd-ulist-button span, .wmd-button-row li#wmd-heading-button span, .wmd-button-row li#wmd-hr-button span, .wmd-button-row li#wmd-more-button span, .wmd-button-row li#wmd-undo-button span, .wmd-button-row li#wmd-redo-button span, .wmd-button-row li#wmd-fullscreen-button span, .wmd-button-row li#wmd-exit-fullscreen-button span { background-image: url("../img/editor.png?_=ce98884"); background-repeat: no-repeat; }
+
+.editor-editor-bold { background-position: 0 0; width: 20px; height: 20px; }
+
+.editor-editor-code { background-position: 0 -20px; width: 20px; height: 20px; }
+
+.editor-editor-exit-fullscreen { background-position: 0 -40px; width: 20px; height: 20px; }
+
+.editor-editor-fullscreen { background-position: 0 -60px; width: 20px; height: 20px; }
+
+.editor-editor-heading { background-position: 0 -80px; width: 20px; height: 20px; }
+
+.editor-editor-hr { background-position: 0 -100px; width: 20px; height: 20px; }
+
+.editor-editor-image { background-position: 0 -120px; width: 20px; height: 20px; }
+
+.editor-editor-italic { background-position: 0 -140px; width: 20px; height: 20px; }
+
+.editor-editor-link { background-position: 0 -160px; width: 20px; height: 20px; }
+
+.editor-editor-more { background-position: 0 -180px; width: 20px; height: 20px; }
+
+.editor-editor-olist { background-position: 0 -200px; width: 20px; height: 20px; }
+
+.editor-editor-quote { background-position: 0 -220px; width: 20px; height: 20px; }
+
+.editor-editor-redo { background-position: 0 -240px; width: 20px; height: 20px; }
+
+.editor-editor-ulist { background-position: 0 -260px; width: 20px; height: 20px; }
+
+.editor-editor-undo { background-position: 0 -280px; width: 20px; height: 20px; }
+
+@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { .editor-sprite, .editor-editor-bold, .editor-editor-code, .editor-editor-exit-fullscreen, .editor-editor-fullscreen, .editor-editor-heading, .editor-editor-hr, .editor-editor-image, .editor-editor-italic, .editor-editor-link, .editor-editor-more, .editor-editor-olist, .editor-editor-quote, .editor-editor-redo, .editor-editor-ulist, .editor-editor-undo, .wmd-button-row li#wmd-bold-button span, .wmd-button-row li#wmd-italic-button span, .wmd-button-row li#wmd-link-button span, .wmd-button-row li#wmd-quote-button span, .wmd-button-row li#wmd-code-button span, .wmd-button-row li#wmd-image-button span, .wmd-button-row li#wmd-olist-button span, .wmd-button-row li#wmd-ulist-button span, .wmd-button-row li#wmd-heading-button span, .wmd-button-row li#wmd-hr-button span, .wmd-button-row li#wmd-more-button span, .wmd-button-row li#wmd-undo-button span, .wmd-button-row li#wmd-redo-button span, .wmd-button-row li#wmd-fullscreen-button span, .wmd-button-row li#wmd-exit-fullscreen-button span { background-image: url("../img/editor@2x.png?_=f3643ed"); background-repeat: no-repeat; background-size: 20px 300px; }
+  .editor-editor-bold { background-position: 0 0; }
+  .editor-editor-code { background-position: 0 -20px; }
+  .editor-editor-exit-fullscreen { background-position: 0 -40px; }
+  .editor-editor-fullscreen { background-position: 0 -60px; }
+  .editor-editor-heading { background-position: 0 -80px; }
+  .editor-editor-hr { background-position: 0 -100px; }
+  .editor-editor-image { background-position: 0 -120px; }
+  .editor-editor-italic { background-position: 0 -140px; }
+  .editor-editor-link { background-position: 0 -160px; }
+  .editor-editor-more { background-position: 0 -180px; }
+  .editor-editor-olist { background-position: 0 -200px; }
+  .editor-editor-quote { background-position: 0 -220px; }
+  .editor-editor-redo { background-position: 0 -240px; }
+  .editor-editor-ulist { background-position: 0 -260px; }
+  .editor-editor-undo { background-position: 0 -280px; }
+  .wmd-button-row li#wmd-bold-button span { background-position: 0 0; }
+  .wmd-button-row li#wmd-italic-button span { background-position: 0 -140px; }
+  .wmd-button-row li#wmd-link-button span { background-position: 0 -160px; }
+  .wmd-button-row li#wmd-quote-button span { background-position: 0 -220px; }
+  .wmd-button-row li#wmd-code-button span { background-position: 0 -20px; }
+  .wmd-button-row li#wmd-image-button span { background-position: 0 -120px; }
+  .wmd-button-row li#wmd-olist-button span { background-position: 0 -200px; }
+  .wmd-button-row li#wmd-ulist-button span { background-position: 0 -260px; }
+  .wmd-button-row li#wmd-heading-button span { background-position: 0 -80px; }
+  .wmd-button-row li#wmd-hr-button span { background-position: 0 -100px; }
+  .wmd-button-row li#wmd-more-button span { background-position: 0 -180px; }
+  .wmd-button-row li#wmd-undo-button span { background-position: 0 -280px; }
+  .wmd-button-row li#wmd-redo-button span { background-position: 0 -240px; }
+  .wmd-button-row li#wmd-fullscreen-button span { background-position: 0 -60px; }
+  .wmd-button-row li#wmd-exit-fullscreen-button span { background-position: 0 -40px; } }
+
+.wmd-button-row { list-style: none; margin: 0; padding: 0; height: 26px; line-height: 1; }
+
+.wmd-button-row li { display: inline-block; margin-right: 4px; padding: 3px; cursor: pointer; vertical-align: middle; border-radius: 2px; }
+
+.wmd-button-row li:hover { background-color: #E9E9E6; }
+
+.wmd-button-row li.wmd-spacer { height: 20px; margin: 0 10px 0 6px; padding: 0; width: 1px; background: #E9E9E6; cursor: default; }
+
+.wmd-button-row li span { display: block; width: 20px; height: 20px; }
+
+.wmd-button-row li#wmd-bold-button span { background-position: 0 0; }
+
+.wmd-button-row li#wmd-italic-button span { background-position: 0 -140px; }
+
+.wmd-button-row li#wmd-link-button span { background-position: 0 -160px; }
+
+.wmd-button-row li#wmd-quote-button span { background-position: 0 -220px; }
+
+.wmd-button-row li#wmd-code-button span { background-position: 0 -20px; }
+
+.wmd-button-row li#wmd-image-button span { background-position: 0 -120px; }
+
+.wmd-button-row li#wmd-olist-button span { background-position: 0 -200px; }
+
+.wmd-button-row li#wmd-ulist-button span { background-position: 0 -260px; }
+
+.wmd-button-row li#wmd-heading-button span { background-position: 0 -80px; }
+
+.wmd-button-row li#wmd-hr-button span { background-position: 0 -100px; }
+
+.wmd-button-row li#wmd-more-button span { background-position: 0 -180px; }
+
+.wmd-button-row li#wmd-undo-button span { background-position: 0 -280px; }
+
+.wmd-button-row li#wmd-redo-button span { background-position: 0 -240px; }
+
+.wmd-button-row li#wmd-fullscreen-button span { background-position: 0 -60px; }
+
+.wmd-button-row li#wmd-exit-fullscreen-button span { background-position: 0 -40px; }
+
+#btn-cancel-preview { display: none; }
+
+.wmd-edittab { float: right; margin-top: 3px; font-size: .92857em; }
+
+.wmd-edittab a { display: inline-block; padding: 0 8px; margin-left: 5px; height: 20px; line-height: 20px; }
+
+.wmd-edittab a:hover { text-decoration: none; }
+
+.wmd-edittab a.active { background: #E9E9E6; color: #999; }
+
+.wmd-hidetab { display: none; }
+
+.wmd-visualhide { visibility: hidden; }
+
+/* 对话框 */
+.wmd-prompt-background { background-color: #000; }
+
+.wmd-prompt-dialog { position: fixed; z-index: 1001; top: 50%; left: 50%; margin-top: -95px; margin-left: -200px; padding: 20px; width: 360px; background: #F6F6F3; }
+
+.wmd-prompt-dialog p { margin: 0 0 5px; }
+
+.wmd-prompt-dialog form { margin-top: 10px; }
+
+.wmd-prompt-dialog input[type="text"] { margin-bottom: 10px; width: 100%; }
+
+.wmd-prompt-dialog button { margin-right: 10px; }
+
+/* 预览 */
+#wmd-preview { background: #FFF; margin: 1em 0; padding: 0 15px; word-wrap: break-word; overflow: auto; border-radius: 2px; }
+
+#wmd-preview img { max-width: 100%; }
+
+#wmd-preview code, #wmd-preview pre { padding: 2px 4px; background: #DDD; font-size: 14px; }
+
+#wmd-preview code { color: #C13; }
+
+#wmd-preview pre { padding: 1em; }
+
+#wmd-preview pre code { padding: 0; color: #444; }
+
+#wmd-preview blockquote { margin: 1em 1.5em; padding-left: 1.5em; border-left: 4px solid #E9E9E6; color: #777; }
+
+#wmd-preview hr { margin: 2em auto; width: 100px; border: 1px solid #E9E9E6; border-width: 2px 0 0 0; }
+
+#wmd-preview .summary:after { display: block; margin: 2em 0; background: #FFF9E8; color: #cf9900; font-size: .85714em; text-align: center; content: "- more -"; }
+
+#wmd-preview .embed { border: 1px solid #ccc; height: 40px; overflow: hidden; line-height: 40px; text-align: center; font-size: 12px; color: #777; }
+
+#wmd-preview table { width: 100%; }
+
+#wmd-preview table th, #wmd-preview table td { border: 1px solid #DDD; padding: 5px 8px; word-break: break-all; }
+
+#wmd-preview table th { background: #EEE; }
+
+#wmd-preview span.line { display: inline; height: 1px; line-height: 1px; position: absolute; }
+
+#wmd-preview .focus, #wmd-preview .focus * { background-color: rgba(255, 230, 0, 0.5) !important; }
+
+/* 上传面板动画效果 */
+@keyframes fullscreen-upload { 0% { opacity: 0; }
+  100% { opacity: 1; } }
+
+/* 编辑器全屏 */
+.fullscreen #wmd-button-bar, .fullscreen #text, .fullscreen #wmd-preview, .fullscreen .submit { position: absolute; top: 0; width: 50%; background: #FFF; z-index: 999; box-sizing: border-box; border-radius: 0; }
+
+.fullscreen #wmd-button-bar { left: 0; padding: 13px 20px; border-bottom: 1px solid #F3F3F0; z-index: 1000; }
+
+.fullscreen #text { top: 53px; left: 0; padding: 20px; border: none; outline: none; }
+
+.fullscreen #wmd-preview { top: 53px; right: 0; margin: 0; padding: 5px 20px; border: none; border-left: 1px solid #F3F3F0; background: #F6F6F3; overflow: auto; }
+
+.fullscreen .submit { right: 0; margin: 0; padding: 10px 20px; border-bottom: 1px solid #F3F3F0; }
+
+.fullscreen #upload-panel { -webkit-box-shadow: 0 4px 16px rgba(0, 0, 0, 0.225); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.225); border-style: solid; }
+
+.fullscreen #tab-files { position: absolute; top: 52px; right: 0; width: 280px; z-index: 1001; animation: fullscreen-upload 0.5s; }
+
+.fullscreen .wmd-edittab, .fullscreen .typecho-post-option, .fullscreen .title, .fullscreen .url-slug, .fullscreen .typecho-page-title, .fullscreen .typecho-head-nav, .fullscreen .message { display: none; }
+
+.fullscreen .wmd-hidetab { display: block; }
+
+.fullscreen .wmd-visualhide, .fullscreen #btn-fullscreen-upload { visibility: visible; }
+
+.preview .submit { width: 100%; background: #FFFFDD; }
+
+.preview #wmd-button-bar, .preview #wmd-preview, .preview #text, .preview #upload-panel, .preview #tab-files, .preview #btn-preview, .preview #btn-fullscreen-upload, .preview #auto-save-message { display: none; }
+
+.preview .preview-frame { width: 100%; border: 0; padding: 0; margin: 0; background: #fff; z-index: 999; position: absolute; top: 53px; left: 0; }
+
+.preview .preview-loading { background-image: url(../img/ajax-loader.gif); background-position: center; background-repeat: no-repeat; }
+
+.preview #btn-cancel-preview { display: inline-block; }
+
+@media (max-width: 575px) { #wmd-spacer2, #wmd-olist-button, #wmd-ulist-button, #wmd-heading-button, #wmd-hr-button, #wmd-more-button, #wmd-spacer3, #wmd-undo-button, #wmd-redo-button, #wmd-spacer4, #wmd-fullscreen-button, #wmd-exit-fullscreen-button, #btn-fullscreen-upload { display: none; } }
+
+/** Jquery Timepicker */
+#ui-datepicker-div { display: none; margin-top: -1px; padding: 10px; border: 1px solid #D9D9D6; background: #FFF; }
+
+.ui-timepicker-div .ui-widget-header { margin-bottom: 8px; }
+
+.ui-timepicker-div dl { text-align: left; }
+
+.ui-timepicker-div dl dt { float: left; clear: left; }
+
+.ui-timepicker-div dl dd { margin: 0 0 10px 40%; }
+
+.ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; }
+
+#ui-datepicker-div .ui-datepicker-header { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #EEE; }
+
+#ui-datepicker-div .ui-datepicker-prev { float: left; cursor: pointer; }
+
+#ui-datepicker-div .ui-datepicker-next { float: right; cursor: pointer; }
+
+#ui-datepicker-div .ui-datepicker-title { font-weight: bold; text-align: center; }
+
+#ui-datepicker-div .ui-datepicker-calendar th { line-height: 24px; }
+
+#ui-datepicker-div .ui-datepicker-calendar a { display: block; width: 30px; background-color: #F3F3F0; line-height: 24px; text-align: center; }
+
+#ui-datepicker-div .ui-datepicker-calendar a:hover { background-color: #E9E9E6; text-decoration: none; }
+
+#ui-datepicker-div .ui-datepicker-today a { background-color: #E9E9E6; color: #444; }
+
+#ui-datepicker-div .ui-datepicker-current-day a { background-color: #467B96 !important; color: #FFF; }
+
+#ui-datepicker-div .ui-timepicker-div { margin-top: 20px; border-top: 1px solid #EEE; }
+
+#ui-datepicker-div .ui-slider { position: relative; margin-top: 18px; border: 1px solid #E9E9E6; background-color: #F6F6F3; height: 4px; }
+
+#ui-datepicker-div .ui-slider .ui-slider-handle { position: absolute; top: -7px; margin-left: -5px; z-index: 2; width: 10px; height: 16px; background-color: #467B96; }
+
+#ui-datepicker-div .ui-datepicker-buttonpane { padding-top: 10px; border-top: 1px solid #EEE; }
+
+#ui-datepicker-div .ui-datepicker-current, #ui-datepicker-div .ui-datepicker-close { float: left; }
+
+#ui-datepicker-div .ui-datepicker-close { float: right; }
+
+.ui-effects-transfer { border: 2px dotted #ccc; }
+
+/** Jquery Tokeninput */
+ul.token-input-list { list-style: none; margin: 0; padding: 0 4px; min-height: 32px; border: 1px solid #D9D9D6; cursor: text; z-index: 999; background-color: #FFF; clear: left; border-radius: 2px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
+
+ul.token-input-list li { margin: 4px 0; }
+
+ul.token-input-list li input { padding: 0; border: 0; width: 100%; -webkit-appearance: caret; }
+
+li.token-input-token { padding: 0 6px; height: 27px; line-height: 27px; background-color: #F3F3F0; cursor: default; font-size: .92857em; text-align: right; white-space: nowrap; }
+
+li.token-input-token p { float: left; display: inline; margin: 0; }
+
+li.token-input-token span { color: #BBB; font-weight: bold; cursor: pointer; }
+
+li.token-input-selected-token { background-color: #E9E9E6; }
+
+li.token-input-input-token { padding: 0 4px; }
+
+div.token-input-dropdown { position: absolute; background-color: #FFF; overflow: hidden; border: 1px solid #D9D9D6; border-top-width: 0; cursor: default; z-index: 1; font-size: .92857em; }
+
+div.token-input-dropdown p { margin: 0; padding: 5px 10px; color: #777; font-weight: bold; }
+
+div.token-input-dropdown ul { list-style: none; margin: 0; padding: 0; }
+
+div.token-input-dropdown ul li { padding: 4px 10px; background-color: #FFF; }
+
+div.token-input-dropdown ul li.token-input-dropdown-item { background-color: #FFF; }
+
+div.token-input-dropdown ul li em { font-style: normal; }
+
+div.token-input-dropdown ul li.token-input-selected-dropdown-item { background-color: #467B96; color: #FFF; }
+
+/* Hide from both screenreaders and browsers: h5bp.com/u */
+.hidden { display: none; }
+
+/* Hide only visually, but have it available for screenreaders: h5bp.com/v */
+.sr-only { border: 0; height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
+
+/* Extends the .sr-only class to allow the element to be focusable when navigated to via the keyboard: h5bp.com/p */
+.sr-only.focusable:active, .sr-only.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; }
+
+/* Hide visually and from screenreaders, but maintain layout */
+.invisible { visibility: hidden; }

+ 50 - 0
admin/custom-fields-js.php

@@ -0,0 +1,50 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<script>
+$(document).ready(function () {
+    // 自定义字段
+    $('#custom-field-expand').click(function() {
+        var btn = $('i', this);
+        if (btn.hasClass('i-caret-right')) {
+            btn.removeClass('i-caret-right').addClass('i-caret-down');
+        } else {
+            btn.removeClass('i-caret-down').addClass('i-caret-right');
+        }
+        $(this).parent().toggleClass('fold');
+        return false;
+    });
+
+    function attachDeleteEvent (el) {
+        $('button.btn-xs', el).click(function () {
+            if (confirm('<?php _e('确认要删除此字段吗?'); ?>')) {
+                $(this).parents('tr').fadeOut(function () {
+                    $(this).remove();
+                });
+
+                $(this).parents('form').trigger('field');
+            }
+        });
+    }
+
+    $('#custom-field table tbody tr').each(function () {
+        attachDeleteEvent(this);
+    });
+
+    $('#custom-field button.operate-add').click(function () {
+        var html = '<tr><td><input type="text" name="fieldNames[]" placeholder="<?php _e('字段名称'); ?>" class="text-s w-100"></td>'
+                + '<td><select name="fieldTypes[]" id="">'
+                + '<option value="str"><?php _e('字符'); ?></option>'
+                + '<option value="int"><?php _e('整数'); ?></option>'
+                + '<option value="float"><?php _e('小数'); ?></option>'
+                + '</select></td>'
+                + '<td><textarea name="fieldValues[]" placeholder="<?php _e('字段值'); ?>" class="text-s w-100" rows="2"></textarea></td>'
+                + '<td><button type="button" class="btn btn-xs"><?php _e('删除'); ?></button></td></tr>',
+            el = $(html).hide().appendTo('#custom-field table tbody').fadeIn();
+
+            $(':input', el).bind('input change', function () {
+                $(this).parents('form').trigger('field');
+            });
+
+        attachDeleteEvent(el);
+    });
+});
+</script>

+ 84 - 0
admin/custom-fields.php

@@ -0,0 +1,84 @@
+<?php if (!defined('__TYPECHO_ADMIN__')) exit; ?>
+<?php
+$fields = isset($post) ? $post->getFieldItems() : $page->getFieldItems();
+$defaultFields = isset($post) ? $post->getDefaultFieldItems() : $page->getDefaultFieldItems();
+?>
+<section id="custom-field"
+         class="typecho-post-option<?php if (empty($defaultFields) && empty($fields)): ?> fold<?php endif; ?>">
+    <label id="custom-field-expand" class="typecho-label"><a href="##"><i
+                class="i-caret-right"></i> <?php _e('自定义字段'); ?></a></label>
+    <table class="typecho-list-table mono">
+        <colgroup>
+            <col width="20%"/>
+            <col width="15%"/>
+            <col width="55%"/>
+            <col width="10%"/>
+        </colgroup>
+        <?php foreach ($defaultFields as $field): ?>
+            <?php [$label, $input] = $field; ?>
+            <tr>
+                <td><?php $label->render(); ?></td>
+                <td colspan="3"><?php $input->render(); ?></td>
+            </tr>
+        <?php endforeach; ?>
+        <?php foreach ($fields as $field): ?>
+            <tr>
+                <td>
+                    <label for="fieldname" class="sr-only"><?php _e('字段名称'); ?></label>
+                    <input type="text" name="fieldNames[]" value="<?php echo htmlspecialchars($field['name']); ?>"
+                           id="fieldname" class="text-s w-100">
+                </td>
+                <td>
+                    <label for="fieldtype" class="sr-only"><?php _e('字段类型'); ?></label>
+                    <select name="fieldTypes[]" id="fieldtype">
+                        <option
+                            value="str"<?php if ('str' == $field['type']): ?> selected<?php endif; ?>><?php _e('字符'); ?></option>
+                        <option
+                            value="int"<?php if ('int' == $field['type']): ?> selected<?php endif; ?>><?php _e('整数'); ?></option>
+                        <option
+                            value="float"<?php if ('float' == $field['type']): ?> selected<?php endif; ?>><?php _e('小数'); ?></option>
+                        <option
+                            value="json"<?php if ('json' == $field['type']): ?> selected<?php endif; ?>><?php _e('JSON 结构'); ?></option>
+                    </select>
+                </td>
+                <td>
+                    <label for="fieldvalue" class="sr-only"><?php _e('字段值'); ?></label>
+                    <textarea name="fieldValues[]" id="fieldvalue" class="text-s w-100"
+                              rows="2"><?php echo htmlspecialchars($field[($field['type'] == 'json' ? 'str' : $field['type']) . '_value']); ?></textarea>
+                </td>
+                <td>
+                    <button type="button" class="btn btn-xs"><?php _e('删除'); ?></button>
+                </td>
+            </tr>
+        <?php endforeach; ?>
+        <?php if (empty($defaultFields) && empty($fields)): ?>
+            <tr>
+                <td>
+                    <label for="fieldname" class="sr-only"><?php _e('字段名称'); ?></label>
+                    <input type="text" name="fieldNames[]" placeholder="<?php _e('字段名称'); ?>" id="fieldname"
+                           class="text-s w-100">
+                </td>
+                <td>
+                    <label for="fieldtype" class="sr-only"><?php _e('字段类型'); ?></label>
+                    <select name="fieldTypes[]" id="fieldtype">
+                        <option value="str"><?php _e('字符'); ?></option>
+                        <option value="int"><?php _e('整数'); ?></option>
+                        <option value="float"><?php _e('小数'); ?></option>
+                    </select>
+                </td>
+                <td>
+                    <label for="fieldvalue" class="sr-only"><?php _e('字段值'); ?></label>
+                    <textarea name="fieldValues[]" placeholder="<?php _e('字段值'); ?>" id="fieldvalue"
+                              class="text-s w-100" rows="2"></textarea>
+                </td>
+                <td>
+                    <button type="button" class="btn btn-xs"><?php _e('删除'); ?></button>
+                </td>
+            </tr>
+        <?php endif; ?>
+    </table>
+    <div class="description clearfix">
+        <button type="button" class="btn btn-xs operate-add"><?php _e('+添加字段'); ?></button>
+        <?php _e('自定义字段可以扩展你的模板功能, 使用方法参见 <a href="https://docs.typecho.org/help/custom-fields">帮助文档</a>'); ?>
+    </div>
+</section>

+ 242 - 0
admin/editor-js.php

@@ -0,0 +1,242 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<?php $content = !empty($post) ? $post : $page; if ($options->markdown): ?>
+<script src="<?php $options->adminStaticUrl('js', 'hyperdown.js'); ?>"></script>
+<script src="<?php $options->adminStaticUrl('js', 'pagedown.js'); ?>"></script>
+<script src="<?php $options->adminStaticUrl('js', 'paste.js'); ?>"></script>
+<script src="<?php $options->adminStaticUrl('js', 'purify.js'); ?>"></script>
+<script>
+$(document).ready(function () {
+    var textarea = $('#text'),
+        isFullScreen = false,
+        toolbar = $('<div class="editor" id="wmd-button-bar" />').insertBefore(textarea.parent()),
+        preview = $('<div id="wmd-preview" class="wmd-hidetab" />').insertAfter('.editor');
+
+    var options = {}, isMarkdown = <?php echo intval($content->isMarkdown || !$content->have()); ?>;
+
+    options.strings = {
+        bold: '<?php _e('加粗'); ?> <strong> Ctrl+B',
+        boldexample: '<?php _e('加粗文字'); ?>',
+            
+        italic: '<?php _e('斜体'); ?> <em> Ctrl+I',
+        italicexample: '<?php _e('斜体文字'); ?>',
+
+        link: '<?php _e('链接'); ?> <a> Ctrl+L',
+        linkdescription: '<?php _e('请输入链接描述'); ?>',
+
+        quote:  '<?php _e('引用'); ?> <blockquote> Ctrl+Q',
+        quoteexample: '<?php _e('引用文字'); ?>',
+
+        code: '<?php _e('代码'); ?> <pre><code> Ctrl+K',
+        codeexample: '<?php _e('请输入代码'); ?>',
+
+        image: '<?php _e('图片'); ?> <img> Ctrl+G',
+        imagedescription: '<?php _e('请输入图片描述'); ?>',
+
+        olist: '<?php _e('数字列表'); ?> <ol> Ctrl+O',
+        ulist: '<?php _e('普通列表'); ?> <ul> Ctrl+U',
+        litem: '<?php _e('列表项目'); ?>',
+
+        heading: '<?php _e('标题'); ?> <h1>/<h2> Ctrl+H',
+        headingexample: '<?php _e('标题文字'); ?>',
+
+        hr: '<?php _e('分割线'); ?> <hr> Ctrl+R',
+        more: '<?php _e('摘要分割线'); ?> <!--more--> Ctrl+M',
+
+        undo: '<?php _e('撤销'); ?> - Ctrl+Z',
+        redo: '<?php _e('重做'); ?> - Ctrl+Y',
+        redomac: '<?php _e('重做'); ?> - Ctrl+Shift+Z',
+
+        fullscreen: '<?php _e('全屏'); ?> - Ctrl+J',
+        exitFullscreen: '<?php _e('退出全屏'); ?> - Ctrl+E',
+        fullscreenUnsupport: '<?php _e('此浏览器不支持全屏操作'); ?>',
+
+        imagedialog: '<p><b><?php _e('插入图片'); ?></b></p><p><?php _e('请在下方的输入框内输入要插入的远程图片地址'); ?></p><p><?php _e('您也可以使用附件功能插入上传的本地图片'); ?></p>',
+        linkdialog: '<p><b><?php _e('插入链接'); ?></b></p><p><?php _e('请在下方的输入框内输入要插入的链接地址'); ?></p>',
+
+        ok: '<?php _e('确定'); ?>',
+        cancel: '<?php _e('取消'); ?>',
+
+        help: '<?php _e('Markdown语法帮助'); ?>'
+    };
+
+    var converter = new HyperDown(),
+        editor = new Markdown.Editor(converter, '', options);
+
+    // 自动跟随
+    converter.enableHtml(true);
+    converter.enableLine(true);
+    reloadScroll = scrollableEditor(textarea, preview);
+
+    // 修正白名单
+    converter.hook('makeHtml', function (html) {
+        html = html.replace('<p><!--more--></p>', '<!--more-->');
+        
+        if (html.indexOf('<!--more-->') > 0) {
+            var parts = html.split(/\s*<\!\-\-more\-\->\s*/),
+                summary = parts.shift(),
+                details = parts.join('');
+
+            html = '<div class="summary">' + summary + '</div>'
+                + '<div class="details">' + details + '</div>';
+        }
+
+        // 替换block
+        html = html.replace(/<(iframe|embed)\s+([^>]*)>/ig, function (all, tag, src) {
+            if (src[src.length - 1] == '/') {
+                src = src.substring(0, src.length - 1);
+            }
+
+            return '<div class="embed"><strong>'
+                + tag + '</strong> : ' + $.trim(src) + '</div>';
+        });
+
+        return DOMPurify.sanitize(html, {USE_PROFILES: {html: true}});
+    });
+
+    editor.hooks.chain('onPreviewRefresh', function () {
+        var images = $('img', preview), count = images.length;
+
+        if (count == 0) {
+            reloadScroll(true);
+        } else {
+            images.bind('load error', function () {
+                count --;
+
+                if (count == 0) {
+                    reloadScroll(true);
+                }
+            });
+        }
+    });
+
+    <?php \Typecho\Plugin::factory('admin/editor-js.php')->markdownEditor($content); ?>
+
+    var th = textarea.height(), ph = preview.height(),
+        uploadBtn = $('<button type="button" id="btn-fullscreen-upload" class="btn btn-link">'
+            + '<i class="i-upload"><?php _e('附件'); ?></i></button>')
+            .prependTo('.submit .right')
+            .click(function() {
+                $('a', $('.typecho-option-tabs li').not('.active')).trigger('click');
+                return false;
+            });
+
+    $('.typecho-option-tabs li').click(function () {
+        uploadBtn.find('i').toggleClass('i-upload-active',
+            $('#tab-files-btn', this).length > 0);
+    });
+
+    editor.hooks.chain('enterFakeFullScreen', function () {
+        th = textarea.height();
+        ph = preview.height();
+        $(document.body).addClass('fullscreen');
+        var h = $(window).height() - toolbar.outerHeight();
+        
+        textarea.css('height', h);
+        preview.css('height', h);
+        isFullScreen = true;
+    });
+
+    editor.hooks.chain('enterFullScreen', function () {
+        $(document.body).addClass('fullscreen');
+        
+        var h = window.screen.height - toolbar.outerHeight();
+        textarea.css('height', h);
+        preview.css('height', h);
+        isFullScreen = true;
+    });
+
+    editor.hooks.chain('exitFullScreen', function () {
+        $(document.body).removeClass('fullscreen');
+        textarea.height(th);
+        preview.height(ph);
+        isFullScreen = false;
+    });
+
+    editor.hooks.chain('commandExecuted', function () {
+        textarea.trigger('input');
+    });
+
+    function initMarkdown() {
+        editor.run();
+
+        var imageButton = $('#wmd-image-button'),
+            linkButton = $('#wmd-link-button');
+
+        Typecho.insertFileToEditor = function (file, url, isImage) {
+            var button = isImage ? imageButton : linkButton;
+
+            options.strings[isImage ? 'imagename' : 'linkname'] = file;
+            button.trigger('click');
+
+            var checkDialog = setInterval(function () {
+                if ($('.wmd-prompt-dialog').length > 0) {
+                    $('.wmd-prompt-dialog input').val(url).select();
+                    clearInterval(checkDialog);
+                    checkDialog = null;
+                }
+            }, 10);
+        };
+
+        Typecho.uploadComplete = function (file) {
+            Typecho.insertFileToEditor(file.title, file.url, file.isImage);
+        };
+
+        // 编辑预览切换
+        var edittab = $('.editor').prepend('<div class="wmd-edittab"><a href="#wmd-editarea" class="active"><?php _e('撰写'); ?></a><a href="#wmd-preview"><?php _e('预览'); ?></a></div>'),
+            editarea = $(textarea.parent()).attr("id", "wmd-editarea");
+
+        $(".wmd-edittab a").click(function() {
+            $(".wmd-edittab a").removeClass('active');
+            $(this).addClass("active");
+            $("#wmd-editarea, #wmd-preview").addClass("wmd-hidetab");
+        
+            var selected_tab = $(this).attr("href"),
+                selected_el = $(selected_tab).removeClass("wmd-hidetab");
+
+            // 预览时隐藏编辑器按钮
+            if (selected_tab == "#wmd-preview") {
+                $("#wmd-button-row").addClass("wmd-visualhide");
+            } else {
+                $("#wmd-button-row").removeClass("wmd-visualhide");
+            }
+
+            // 预览和编辑窗口高度一致
+            $("#wmd-preview").outerHeight($("#wmd-editarea").innerHeight());
+
+            return false;
+        });
+
+        // 剪贴板复制图片
+        textarea.pastableTextarea().on('pasteImage', function (e, data) {
+            var name = data.name ? data.name.replace(/[\(\)\[\]\*#!]/g, '') : (new Date()).toISOString().replace(/\..+$/, '');
+            if (!name.match(/\.[a-z0-9]{2,}$/i)) {
+                var ext = data.blob.type.split('/').pop();
+                name += '.' + ext;
+            }
+
+            Typecho.uploadFile(new File([data.blob], name), name);
+        });
+    }
+
+    if (isMarkdown) {
+        initMarkdown();
+    } else {
+        var notice = $('<div class="message notice"><?php _e('这篇文章不是由Markdown语法创建的, 继续使用Markdown编辑它吗?'); ?> '
+            + '<button class="btn btn-xs primary yes"><?php _e('是'); ?></button> ' 
+            + '<button class="btn btn-xs no"><?php _e('否'); ?></button></div>')
+            .hide().insertBefore(textarea).slideDown();
+
+        $('.yes', notice).click(function () {
+            notice.remove();
+            $('<input type="hidden" name="markdown" value="1" />').appendTo('.submit');
+            initMarkdown();
+        });
+
+        $('.no', notice).click(function () {
+            notice.remove();
+        });
+    }
+});
+</script>
+<?php endif; ?>
+

+ 14 - 0
admin/extending.php

@@ -0,0 +1,14 @@
+<?php
+
+include 'common.php';
+
+$panel = $request->get('panel');
+$panelTable = unserialize($options->panelTable);
+
+if (!isset($panelTable['file']) || !in_array(urlencode($panel), $panelTable['file'])) {
+    throw new \Typecho\Plugin\Exception(_t('页面不存在'), 404);
+}
+
+[$pluginName, $file] = explode('/', trim($panel, '/'), 2);
+
+require_once $options->pluginDir($pluginName) . '/' . $file;

+ 229 - 0
admin/file-upload-js.php

@@ -0,0 +1,229 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<?php
+if (isset($post) && $post instanceof \Typecho\Widget && $post->have()) {
+    $fileParentContent = $post;
+} elseif (isset($page) && $page instanceof \Typecho\Widget && $page->have()) {
+    $fileParentContent = $page;
+}
+
+$phpMaxFilesize = function_exists('ini_get') ? trim(ini_get('upload_max_filesize')) : 0;
+
+if (preg_match("/^([0-9]+)([a-z]{1,2})$/i", $phpMaxFilesize, $matches)) {
+    $phpMaxFilesize = strtolower($matches[1] . $matches[2] . (1 == strlen($matches[2]) ? 'b' : ''));
+}
+?>
+
+<script src="<?php $options->adminStaticUrl('js', 'moxie.js'); ?>"></script>
+<script src="<?php $options->adminStaticUrl('js', 'plupload.js'); ?>"></script>
+<script>
+$(document).ready(function() {
+    function updateAttacmentNumber () {
+        var btn = $('#tab-files-btn'),
+            balloon = $('.balloon', btn),
+            count = $('#file-list li .insert').length;
+
+        if (count > 0) {
+            if (!balloon.length) {
+                btn.html($.trim(btn.html()) + ' ');
+                balloon = $('<span class="balloon"></span>').appendTo(btn);
+            }
+
+            balloon.html(count);
+        } else if (0 == count && balloon.length > 0) {
+            balloon.remove();
+        }
+    }
+
+    $('.upload-area').bind({
+        dragenter   :   function () {
+            $(this).parent().addClass('drag');
+        },
+
+        dragover    :   function (e) {
+            $(this).parent().addClass('drag');
+        },
+
+        drop        :   function () {
+            $(this).parent().removeClass('drag');
+        },
+        
+        dragend     :   function () {
+            $(this).parent().removeClass('drag');
+        },
+
+        dragleave   :   function () {
+            $(this).parent().removeClass('drag');
+        }
+    });
+
+    updateAttacmentNumber();
+
+    function fileUploadStart (file) {
+        $('<li id="' + file.id + '" class="loading">'
+            + file.name + '</li>').appendTo('#file-list');
+    }
+
+    function fileUploadError (error) {
+        var file = error.file, code = error.code, word; 
+        
+        switch (code) {
+            case plupload.FILE_SIZE_ERROR:
+                word = '<?php _e('文件大小超过限制'); ?>';
+                break;
+            case plupload.FILE_EXTENSION_ERROR:
+                word = '<?php _e('文件扩展名不被支持'); ?>';
+                break;
+            case plupload.FILE_DUPLICATE_ERROR:
+                word = '<?php _e('文件已经上传过'); ?>';
+                break;
+            case plupload.HTTP_ERROR:
+            default:
+                word = '<?php _e('上传出现错误'); ?>';
+                break;
+        }
+
+        var fileError = '<?php _e('%s 上传失败'); ?>'.replace('%s', file.name),
+            li, exist = $('#' + file.id);
+
+        if (exist.length > 0) {
+            li = exist.removeClass('loading').html(fileError);
+        } else {
+            li = $('<li>' + fileError + '<br />' + word + '</li>').appendTo('#file-list');
+        }
+
+        li.effect('highlight', {color : '#FBC2C4'}, 2000, function () {
+            $(this).remove();
+        });
+
+        // fix issue #341
+        this.removeFile(file);
+    }
+
+    var completeFile = null;
+    function fileUploadComplete (id, url, data) {
+        var li = $('#' + id).removeClass('loading').data('cid', data.cid)
+            .data('url', data.url)
+            .data('image', data.isImage)
+            .html('<input type="hidden" name="attachment[]" value="' + data.cid + '" />'
+                + '<a class="insert" target="_blank" href="###" title="<?php _e('点击插入文件'); ?>">' + data.title + '</a><div class="info">' + data.bytes
+                + ' <a class="file" target="_blank" href="<?php $options->adminUrl('media.php'); ?>?cid=' 
+                + data.cid + '" title="<?php _e('编辑'); ?>"><i class="i-edit"></i></a>'
+                + ' <a class="delete" href="###" title="<?php _e('删除'); ?>"><i class="i-delete"></i></a></div>')
+            .effect('highlight', 1000);
+            
+        attachInsertEvent(li);
+        attachDeleteEvent(li);
+        updateAttacmentNumber();
+
+        if (!completeFile) {
+            completeFile = data;
+        }
+    }
+
+    var uploader = null, tabFilesEl = $('#tab-files').bind('init', function () {
+        uploader = new plupload.Uploader({
+            browse_button   :   $('.upload-file').get(0),
+            url             :   '<?php $security->index('/action/upload'
+                . (isset($fileParentContent) ? '?cid=' . $fileParentContent->cid : '')); ?>',
+            runtimes        :   'html5,flash,html4',
+            flash_swf_url   :   '<?php $options->adminStaticUrl('js', 'Moxie.swf'); ?>',
+            drop_element    :   $('.upload-area').get(0),
+            filters         :   {
+                max_file_size       :   '<?php echo $phpMaxFilesize ?>',
+                mime_types          :   [{'title' : '<?php _e('允许上传的文件'); ?>', 'extensions' : '<?php echo implode(',', $options->allowedAttachmentTypes); ?>'}],
+                prevent_duplicates  :   true
+            },
+
+            init            :   {
+                FilesAdded      :   function (up, files) {
+                    for (var i = 0; i < files.length; i ++) {
+                        fileUploadStart(files[i]);
+                    }
+
+                    completeFile = null;
+                    uploader.start();
+                },
+
+                UploadComplete  :   function () {
+                    if (completeFile) {
+                        Typecho.uploadComplete(completeFile);
+                    }
+                },
+
+                FileUploaded    :   function (up, file, result) {
+                    if (200 == result.status) {
+                        var data = $.parseJSON(result.response);
+
+                        if (data) {
+                            fileUploadComplete(file.id, data[0], data[1]);
+                            uploader.removeFile(file);
+                            return;
+                        }
+                    }
+
+                    fileUploadError.call(uploader, {
+                        code : plupload.HTTP_ERROR,
+                        file : file
+                    });
+                },
+
+                Error           :   function (up, error) {
+                    fileUploadError.call(uploader, error);
+                }
+            }
+        });
+
+        uploader.init();
+    });
+
+    Typecho.uploadFile = function (file, name) {
+        if (!uploader) {
+            $('#tab-files-btn').parent().trigger('click');
+        }
+        
+        var timer = setInterval(function () {
+            if (!uploader) {
+                return;
+            }
+
+            clearInterval(timer);
+            timer = null;
+
+            uploader.addFile(file, name);
+        }, 50);
+    };
+
+    function attachInsertEvent (el) {
+        $('.insert', el).click(function () {
+            var t = $(this), p = t.parents('li');
+            Typecho.insertFileToEditor(t.text(), p.data('url'), p.data('image'));
+            return false;
+        });
+    }
+
+    function attachDeleteEvent (el) {
+        var file = $('a.insert', el).text();
+        $('.delete', el).click(function () {
+            if (confirm('<?php _e('确认要删除文件 %s 吗?'); ?>'.replace('%s', file))) {
+                var cid = $(this).parents('li').data('cid');
+                $.post('<?php $security->index('/action/contents-attachment-edit'); ?>',
+                    {'do' : 'delete', 'cid' : cid},
+                    function () {
+                        $(el).fadeOut(function () {
+                            $(this).remove();
+                            updateAttacmentNumber();
+                        });
+                    });
+            }
+
+            return false;
+        });
+    }
+
+    $('#file-list li').each(function () {
+        attachInsertEvent(this);
+        attachDeleteEvent(this);
+    });
+});
+</script>
+

+ 30 - 0
admin/file-upload.php

@@ -0,0 +1,30 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+
+<?php
+if (isset($post) || isset($page)) {
+    $cid = isset($post) ? $post->cid : $page->cid;
+
+    if ($cid) {
+        \Widget\Contents\Attachment\Related::alloc(['parentId' => $cid])->to($attachment);
+    } else {
+        \Widget\Contents\Attachment\Unattached::alloc()->to($attachment);
+    }
+}
+?>
+
+<div id="upload-panel" class="p">
+    <div class="upload-area" draggable="true"><?php _e('拖放文件到这里<br>或者 %s选择文件上传%s', '<a href="###" class="upload-file">', '</a>'); ?></div>
+    <ul id="file-list">
+    <?php while ($attachment->next()): ?>
+        <li data-cid="<?php $attachment->cid(); ?>" data-url="<?php echo $attachment->attachment->url; ?>" data-image="<?php echo $attachment->attachment->isImage ? 1 : 0; ?>"><input type="hidden" name="attachment[]" value="<?php $attachment->cid(); ?>" />
+            <a class="insert" title="<?php _e('点击插入文件'); ?>" href="###"><?php $attachment->title(); ?></a>
+            <div class="info">
+                <?php echo number_format(ceil($attachment->attachment->size / 1024)); ?> Kb
+                <a class="file" target="_blank" href="<?php $options->adminUrl('media.php?cid=' . $attachment->cid); ?>" title="<?php _e('编辑'); ?>"><i class="i-edit"></i></a>
+                <a href="###" class="delete" title="<?php _e('删除'); ?>"><i class="i-delete"></i></a>
+            </div>
+        </li>
+    <?php endwhile; ?>
+    </ul>
+</div>
+

+ 7 - 0
admin/footer.php

@@ -0,0 +1,7 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<?php \Typecho\Plugin::factory('admin/footer.php')->begin(); ?>
+    </body>
+</html>
+<?php
+/** 注册一个结束插件 */
+\Typecho\Plugin::factory('admin/footer.php')->end();

+ 26 - 0
admin/form-js.php

@@ -0,0 +1,26 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<script>
+(function () {
+    $(document).ready(function () {
+        var error = $('.typecho-option .error:first');
+
+        if (error.length > 0) {
+            $('html,body').scrollTop(error.parents('.typecho-option').offset().top);
+        }
+
+        $('form').submit(function () {
+            if (this.submitted) {
+                return false;
+            } else {
+                this.submitted = true;
+            }
+        });
+
+        $('label input[type=text]').click(function (e) {
+            var check = $('#' + $(this).parents('label').attr('for'));
+            check.prop('checked', true);
+            return false;
+        });
+    });
+})();
+</script>

+ 23 - 0
admin/header.php

@@ -0,0 +1,23 @@
+<?php
+if (!defined('__TYPECHO_ADMIN__')) {
+    exit;
+}
+
+$header = '<link rel="stylesheet" href="' . $options->adminStaticUrl('css', 'normalize.css', true) . '">
+<link rel="stylesheet" href="' . $options->adminStaticUrl('css', 'grid.css', true) . '">
+<link rel="stylesheet" href="' . $options->adminStaticUrl('css', 'style.css', true) . '">';
+
+/** 注册一个初始化插件 */
+$header = \Typecho\Plugin::factory('admin/header.php')->header($header);
+
+?><!DOCTYPE HTML>
+<html>
+    <head>
+        <meta charset="<?php $options->charset(); ?>">
+        <meta name="renderer" content="webkit">
+        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+        <title><?php _e('%s - %s - Powered by Typecho', $menu->title, $options->title); ?></title>
+        <meta name="robots" content="noindex, nofollow">
+        <?php echo $header; ?>
+    </head>
+    <body<?php if (isset($bodyClass)) {echo ' class="' . $bodyClass . '"';} ?>>

BIN
admin/img/ajax-loader.gif


BIN
admin/img/editor.png


BIN
admin/img/editor@2x.png


BIN
admin/img/icons.png


BIN
admin/img/icons@2x.png


BIN
admin/img/noscreen.png


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 6 - 0
admin/img/typecho-logo.svg


+ 155 - 0
admin/index.php

@@ -0,0 +1,155 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+$stat = \Widget\Stat::alloc();
+?>
+<div class="main">
+    <div class="container typecho-dashboard">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main">
+            <div class="col-mb-12 welcome-board" role="main">
+                <p><?php _e('目前有 <em>%s</em> 篇文章, 并有 <em>%s</em> 条关于你的评论在 <em>%s</em> 个分类中.',
+                        $stat->myPublishedPostsNum, $stat->myPublishedCommentsNum, $stat->categoriesNum); ?>
+                    <br><?php _e('点击下面的链接快速开始:'); ?></p>
+
+                <ul id="start-link" class="clearfix">
+                    <?php if ($user->pass('contributor', true)): ?>
+                        <li><a href="<?php $options->adminUrl('write-post.php'); ?>"><?php _e('撰写新文章'); ?></a></li>
+                        <?php if ($user->pass('editor', true) && 'on' == $request->get('__typecho_all_comments') && $stat->waitingCommentsNum > 0): ?>
+                            <li>
+                                <a href="<?php $options->adminUrl('manage-comments.php?status=waiting'); ?>"><?php _e('待审核的评论'); ?></a>
+                                <span class="balloon"><?php $stat->waitingCommentsNum(); ?></span>
+                            </li>
+                        <?php elseif ($stat->myWaitingCommentsNum > 0): ?>
+                            <li>
+                                <a href="<?php $options->adminUrl('manage-comments.php?status=waiting'); ?>"><?php _e('待审核评论'); ?></a>
+                                <span class="balloon"><?php $stat->myWaitingCommentsNum(); ?></span>
+                            </li>
+                        <?php endif; ?>
+                        <?php if ($user->pass('editor', true) && 'on' == $request->get('__typecho_all_comments') && $stat->spamCommentsNum > 0): ?>
+                            <li>
+                                <a href="<?php $options->adminUrl('manage-comments.php?status=spam'); ?>"><?php _e('垃圾评论'); ?></a>
+                                <span class="balloon"><?php $stat->spamCommentsNum(); ?></span>
+                            </li>
+                        <?php elseif ($stat->mySpamCommentsNum > 0): ?>
+                            <li>
+                                <a href="<?php $options->adminUrl('manage-comments.php?status=spam'); ?>"><?php _e('垃圾评论'); ?></a>
+                                <span class="balloon"><?php $stat->mySpamCommentsNum(); ?></span>
+                            </li>
+                        <?php endif; ?>
+                        <?php if ($user->pass('administrator', true)): ?>
+                            <li><a href="<?php $options->adminUrl('themes.php'); ?>"><?php _e('更换外观'); ?></a></li>
+                            <li><a href="<?php $options->adminUrl('plugins.php'); ?>"><?php _e('插件管理'); ?></a></li>
+                            <li><a href="<?php $options->adminUrl('options-general.php'); ?>"><?php _e('系统设置'); ?></a>
+                            </li>
+                        <?php endif; ?>
+                    <?php endif; ?>
+                    <!--<li><a href="<?php $options->adminUrl('profile.php'); ?>"><?php _e('更新我的资料'); ?></a></li>-->
+                </ul>
+            </div>
+
+            <div class="col-mb-12 col-tb-4" role="complementary">
+                <section class="latest-link">
+                    <h3><?php _e('最近发布的文章'); ?></h3>
+                    <?php \Widget\Contents\Post\Recent::alloc('pageSize=10')->to($posts); ?>
+                    <ul>
+                        <?php if ($posts->have()): ?>
+                            <?php while ($posts->next()): ?>
+                                <li>
+                                    <span><?php $posts->date('n.j'); ?></span>
+                                    <a href="<?php $posts->permalink(); ?>" class="title"><?php $posts->title(); ?></a>
+                                </li>
+                            <?php endwhile; ?>
+                        <?php else: ?>
+                            <li><em><?php _e('暂时没有文章'); ?></em></li>
+                        <?php endif; ?>
+                    </ul>
+                </section>
+            </div>
+
+            <div class="col-mb-12 col-tb-4" role="complementary">
+                <section class="latest-link">
+                    <h3><?php _e('最近得到的回复'); ?></h3>
+                    <ul>
+                        <?php \Widget\Comments\Recent::alloc('pageSize=10')->to($comments); ?>
+                        <?php if ($comments->have()): ?>
+                            <?php while ($comments->next()): ?>
+                                <li>
+                                    <span><?php $comments->date('n.j'); ?></span>
+                                    <a href="<?php $comments->permalink(); ?>"
+                                       class="title"><?php $comments->author(false); ?></a>:
+                                    <?php $comments->excerpt(35, '...'); ?>
+                                </li>
+                            <?php endwhile; ?>
+                        <?php else: ?>
+                            <li><?php _e('暂时没有回复'); ?></li>
+                        <?php endif; ?>
+                    </ul>
+                </section>
+            </div>
+
+            <div class="col-mb-12 col-tb-4" role="complementary">
+                <section class="latest-link">
+                    <h3><?php _e('官方最新日志'); ?></h3>
+                    <div id="typecho-message">
+                        <ul>
+                            <li><?php _e('读取中...'); ?></li>
+                        </ul>
+                    </div>
+                </section>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+?>
+
+<script>
+    $(document).ready(function () {
+        var ul = $('#typecho-message ul'), cache = window.sessionStorage,
+            html = cache ? cache.getItem('feed') : '',
+            update = cache ? cache.getItem('update') : '';
+
+        if (!!html) {
+            ul.html(html);
+        } else {
+            html = '';
+            $.get('<?php $options->index('/action/ajax?do=feed'); ?>', function (o) {
+                for (var i = 0; i < o.length; i++) {
+                    var item = o[i];
+                    html += '<li><span>' + item.date + '</span> <a href="' + item.link + '" target="_blank">' + item.title
+                        + '</a></li>';
+                }
+
+                ul.html(html);
+                cache.setItem('feed', html);
+            }, 'json');
+        }
+
+        function applyUpdate(update) {
+            if (update.available) {
+                $('<div class="update-check message error"><p>'
+                    + '<?php _e('您当前使用的版本是 %s'); ?>'.replace('%s', update.current) + '<br />'
+                    + '<strong><a href="' + update.link + '" target="_blank">'
+                    + '<?php _e('官方最新版本是 %s'); ?>'.replace('%s', update.latest) + '</a></strong></p></div>')
+                    .insertAfter('.typecho-page-title').effect('highlight');
+            }
+        }
+
+        if (!!update) {
+            applyUpdate($.parseJSON(update));
+        } else {
+            $.get('<?php $options->index('/action/ajax?do=checkVersion'); ?>', function (o, status, resp) {
+                applyUpdate(o);
+                cache.setItem('update', resp.responseText);
+            }, 'json');
+        }
+    });
+
+</script>
+<?php include 'footer.php'; ?>

BIN
admin/js/Moxie.swf


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/hyperdown.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/jquery-ui.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/jquery.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/moxie.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/pagedown.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/paste.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/plupload.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/purify.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/timepicker.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/tokeninput.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
admin/js/typecho.js


+ 56 - 0
admin/login.php

@@ -0,0 +1,56 @@
+<?php
+include 'common.php';
+
+if ($user->hasLogin()) {
+    $response->redirect($options->adminUrl);
+}
+$rememberName = htmlspecialchars(\Typecho\Cookie::get('__typecho_remember_name', ''));
+\Typecho\Cookie::delete('__typecho_remember_name');
+
+$bodyClass = 'body-100';
+
+include 'header.php';
+?>
+<div class="typecho-login-wrap">
+    <div class="typecho-login">
+        <h1><a href="https://typecho.org" class="i-logo">Typecho</a></h1>
+        <form action="<?php $options->loginAction(); ?>" method="post" name="login" role="form">
+            <p>
+                <label for="name" class="sr-only"><?php _e('用户名'); ?></label>
+                <input type="text" id="name" name="name" value="<?php echo $rememberName; ?>" placeholder="<?php _e('用户名'); ?>" class="text-l w-100" autofocus />
+            </p>
+            <p>
+                <label for="password" class="sr-only"><?php _e('密码'); ?></label>
+                <input type="password" id="password" name="password" class="text-l w-100" placeholder="<?php _e('密码'); ?>" />
+            </p>
+            <p class="submit">
+                <button type="submit" class="btn btn-l w-100 primary"><?php _e('登录'); ?></button>
+                <input type="hidden" name="referer" value="<?php echo $request->filter('html')->get('referer'); ?>" />
+            </p>
+            <p>
+                <label for="remember">
+                    <input<?php if(\Typecho\Cookie::get('__typecho_remember_remember')): ?> checked<?php endif; ?> type="checkbox" name="remember" class="checkbox" value="1" id="remember" /> <?php _e('下次自动登录'); ?>
+                </label>
+            </p>
+        </form>
+        
+        <p class="more-link">
+            <a href="<?php $options->siteUrl(); ?>"><?php _e('返回首页'); ?></a>
+            <?php if($options->allowRegister): ?>
+            &bull;
+            <a href="<?php $options->registerUrl(); ?>"><?php _e('用户注册'); ?></a>
+            <?php endif; ?>
+        </p>
+    </div>
+</div>
+<?php 
+include 'common-js.php';
+?>
+<script>
+$(document).ready(function () {
+    $('#name').focus();
+});
+</script>
+<?php
+include 'footer.php';
+?>

+ 172 - 0
admin/manage-categories.php

@@ -0,0 +1,172 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+\Widget\Metas\Category\Admin::alloc()->to($categories);
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main manage-metas">
+
+            <div class="col-mb-12" role="main">
+
+                <form method="post" name="manage_categories" class="operate-form">
+                    <div class="typecho-list-operate clearfix">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox"
+                                                                                   class="typecho-table-select-all"/></label>
+                            <div class="btn-group btn-drop">
+                                <button class="btn dropdown-toggle btn-s" type="button"><i
+                                        class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i
+                                        class="i-caret-down"></i></button>
+                                <ul class="dropdown-menu">
+                                    <li><a lang="<?php _e('此分类下的所有内容将被删除, 你确认要删除这些分类吗?'); ?>"
+                                           href="<?php $security->index('/action/metas-category-edit?do=delete'); ?>"><?php _e('删除'); ?></a>
+                                    </li>
+                                    <li><a lang="<?php _e('刷新分类可能需要等待较长时间, 你确认要刷新这些分类吗?'); ?>"
+                                           href="<?php $security->index('/action/metas-category-edit?do=refresh'); ?>"><?php _e('刷新'); ?></a>
+                                    </li>
+                                    <li class="multiline">
+                                        <button type="button" class="btn merge btn-s"
+                                                rel="<?php $security->index('/action/metas-category-edit?do=merge'); ?>"><?php _e('合并到'); ?></button>
+                                        <select name="merge">
+                                            <?php $categories->parse('<option value="{mid}">{name}</option>'); ?>
+                                        </select>
+                                    </li>
+                                </ul>
+                            </div>
+                        </div>
+                        <div class="search" role="search">
+                            <?php $categories->backLink(); ?>
+                        </div>
+                    </div>
+
+                    <div class="typecho-table-wrap">
+                        <table class="typecho-list-table">
+                            <colgroup>
+                                <col width="20" class="kit-hidden-mb"/>
+                                <col width=""/>
+                                <col width="15%" class="kit-hidden-mb"/>
+                                <col width="25%"/>
+                                <col width="15%"/>
+                                <col width="10%" class="kit-hidden-mb"/>
+                            </colgroup>
+                            <thead>
+                            <tr class="nodrag">
+                                <th class="kit-hidden-mb"></th>
+                                <th><?php _e('名称'); ?></th>
+                                <th><?php _e('子分类'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('缩略名'); ?></th>
+                                <th></th>
+                                <th class="kit-hidden-mb"><?php _e('文章数'); ?></th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <?php if ($categories->have()): ?>
+                                <?php while ($categories->next()): ?>
+                                    <tr id="mid-<?php $categories->theId(); ?>">
+                                        <td class="kit-hidden-mb"><input type="checkbox"
+                                                                         value="<?php $categories->mid(); ?>"
+                                                                         name="mid[]"/></td>
+                                        <td>
+                                            <a href="<?php $options->adminUrl('category.php?mid=' . $categories->mid); ?>"><?php $categories->name(); ?></a>
+                                            <a href="<?php $categories->permalink(); ?>"
+                                               title="<?php _e('浏览 %s', $categories->name); ?>"><i class="i-exlink"></i></a>
+                                        </td>
+                                        <td>
+
+                                            <?php if (count($categories->children) > 0): ?>
+                                                <a href="<?php $options->adminUrl('manage-categories.php?parent=' . $categories->mid); ?>"><?php echo _n('一个分类', '%d个分类', count($categories->children)); ?></a>
+                                            <?php else: ?>
+                                                <a href="<?php $options->adminUrl('category.php?parent=' . $categories->mid); ?>"><?php echo _e('新增'); ?></a>
+                                            <?php endif; ?>
+                                        </td>
+                                        <td class="kit-hidden-mb"><?php $categories->slug(); ?></td>
+                                        <td>
+                                            <?php if ($options->defaultCategory == $categories->mid): ?>
+                                                <?php _e('默认'); ?>
+                                            <?php else: ?>
+                                                <a class="hidden-by-mouse"
+                                                   href="<?php $security->index('/action/metas-category-edit?do=default&mid=' . $categories->mid); ?>"
+                                                   title="<?php _e('设为默认'); ?>"><?php _e('默认'); ?></a>
+                                            <?php endif; ?>
+                                        </td>
+                                        <td class="kit-hidden-mb"><a
+                                                class="balloon-button left size-<?php echo \Typecho\Common::splitByCount($categories->count, 1, 10, 20, 50, 100); ?>"
+                                                href="<?php $options->adminUrl('manage-posts.php?category=' . $categories->mid); ?>"><?php $categories->count(); ?></a>
+                                        </td>
+                                    </tr>
+                                <?php endwhile; ?>
+                            <?php else: ?>
+                                <tr>
+                                    <td colspan="6"><h6 class="typecho-list-table-title"><?php _e('没有任何分类'); ?></h6>
+                                    </td>
+                                </tr>
+                            <?php endif; ?>
+                            </tbody>
+                        </table>
+                    </div>
+                </form>
+
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+?>
+
+<script type="text/javascript">
+    (function () {
+        $(document).ready(function () {
+            var table = $('.typecho-list-table').tableDnD({
+                onDrop: function () {
+                    var ids = [];
+
+                    $('input[type=checkbox]', table).each(function () {
+                        ids.push($(this).val());
+                    });
+
+                    $.post('<?php $security->index('/action/metas-category-edit?do=sort'); ?>',
+                        $.param({mid: ids}));
+
+                    $('tr', table).each(function (i) {
+                        if (i % 2) {
+                            $(this).addClass('even');
+                        } else {
+                            $(this).removeClass('even');
+                        }
+                    });
+                }
+            });
+
+            table.tableSelectable({
+                checkEl: 'input[type=checkbox]',
+                rowEl: 'tr',
+                selectAllEl: '.typecho-table-select-all',
+                actionEl: '.dropdown-menu a'
+            });
+
+            $('.btn-drop').dropdownMenu({
+                btnEl: '.dropdown-toggle',
+                menuEl: '.dropdown-menu'
+            });
+
+            $('.dropdown-menu button.merge').click(function () {
+                var btn = $(this);
+                btn.parents('form').attr('action', btn.attr('rel')).submit();
+            });
+
+            <?php if (isset($request->mid)): ?>
+            $('.typecho-mini-panel').effect('highlight', '#AACB36');
+            <?php endif; ?>
+        });
+    })();
+</script>
+<?php include 'footer.php'; ?>
+

+ 378 - 0
admin/manage-comments.php

@@ -0,0 +1,378 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+$stat = \Widget\Stat::alloc();
+$comments = \Widget\Comments\Admin::alloc();
+$isAllComments = ('on' == $request->get('__typecho_all_comments') || 'on' == \Typecho\Cookie::get('__typecho_all_comments'));
+?>
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12 typecho-list">
+                <div class="clearfix">
+                    <ul class="typecho-option-tabs right">
+                    <?php if($user->pass('editor', true) && !isset($request->cid)): ?>
+                        <li class="<?php if($isAllComments): ?> current<?php endif; ?>"><a href="<?php echo $request->makeUriByRequest('__typecho_all_comments=on'); ?>"><?php _e('所有'); ?></a></li>
+                        <li class="<?php if(!$isAllComments): ?> current<?php endif; ?>"><a href="<?php echo $request->makeUriByRequest('__typecho_all_comments=off'); ?>"><?php _e('我的'); ?></a></li>
+                    <?php endif; ?>
+                    </ul>
+                    <ul class="typecho-option-tabs">
+                        <li<?php if(!isset($request->status) || 'approved' == $request->get('status')): ?> class="current"<?php endif; ?>><a href="<?php $options->adminUrl('manage-comments.php'
+                        . (isset($request->cid) ? '?cid=' . $request->filter('encode')->cid : '')); ?>"><?php _e('已通过'); ?></a></li>
+                        <li<?php if('waiting' == $request->get('status')): ?> class="current"<?php endif; ?>><a href="<?php $options->adminUrl('manage-comments.php?status=waiting'
+                        . (isset($request->cid) ? '&cid=' . $request->filter('encode')->cid : '')); ?>"><?php _e('待审核'); ?>
+                        <?php if(!$isAllComments && $stat->myWaitingCommentsNum > 0 && !isset($request->cid)): ?> 
+                            <span class="balloon"><?php $stat->myWaitingCommentsNum(); ?></span>
+                        <?php elseif($isAllComments && $stat->waitingCommentsNum > 0 && !isset($request->cid)): ?>
+                            <span class="balloon"><?php $stat->waitingCommentsNum(); ?></span>
+                        <?php elseif(isset($request->cid) && $stat->currentWaitingCommentsNum > 0): ?>
+                            <span class="balloon"><?php $stat->currentWaitingCommentsNum(); ?></span>
+                        <?php endif; ?>
+                        </a></li>
+                        <li<?php if('spam' == $request->get('status')): ?> class="current"<?php endif; ?>><a href="<?php $options->adminUrl('manage-comments.php?status=spam'
+                        . (isset($request->cid) ? '&cid=' . $request->filter('encode')->cid : '')); ?>"><?php _e('垃圾'); ?>
+                        <?php if(!$isAllComments && $stat->mySpamCommentsNum > 0 && !isset($request->cid)): ?> 
+                            <span class="balloon"><?php $stat->mySpamCommentsNum(); ?></span>
+                        <?php elseif($isAllComments && $stat->spamCommentsNum > 0 && !isset($request->cid)): ?>
+                            <span class="balloon"><?php $stat->spamCommentsNum(); ?></span>
+                        <?php elseif(isset($request->cid) && $stat->currentSpamCommentsNum > 0): ?>
+                            <span class="balloon"><?php $stat->currentSpamCommentsNum(); ?></span>
+                        <?php endif; ?>
+                        </a></li>
+                    </ul>
+                </div>
+            
+                <div class="typecho-list-operate clearfix">
+                    <form method="get">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox" class="typecho-table-select-all" /></label>
+                            <div class="btn-group btn-drop">
+                            <button class="btn dropdown-toggle btn-s" type="button"><i class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i class="i-caret-down"></i></button>
+                            <ul class="dropdown-menu">
+                                <li><a href="<?php $security->index('/action/comments-edit?do=approved'); ?>"><?php _e('通过'); ?></a></li>
+                                <li><a href="<?php $security->index('/action/comments-edit?do=waiting'); ?>"><?php _e('待审核'); ?></a></li>
+                                <li><a href="<?php $security->index('/action/comments-edit?do=spam'); ?>"><?php _e('标记垃圾'); ?></a></li>
+                                <li><a lang="<?php _e('你确认要删除这些评论吗?'); ?>" href="<?php $security->index('/action/comments-edit?do=delete'); ?>"><?php _e('删除'); ?></a></li>
+                            </ul>
+                            <?php if('spam' == $request->get('status')): ?>
+                                <button lang="<?php _e('你确认要删除所有垃圾评论吗?'); ?>" class="btn btn-s btn-warn btn-operate" href="<?php $security->index('/action/comments-edit?do=delete-spam'); ?>"><?php _e('删除所有垃圾评论'); ?></button>
+                            <?php endif; ?>
+                            </div>
+                        </div>
+                        <div class="search" role="search">
+                            <?php if ('' != $request->keywords || '' != $request->category): ?>
+                            <a href="<?php $options->adminUrl('manage-comments.php' 
+                            . (isset($request->status) || isset($request->cid) ? '?' .
+                            (isset($request->status) ? 'status=' . $request->filter('encode')->status : '') .
+                            (isset($request->cid) ? (isset($request->status) ? '&' : '') . 'cid=' . $request->filter('encode')->cid : '') : '')); ?>"><?php _e('&laquo; 取消筛选'); ?></a>
+                            <?php endif; ?>
+                            <input type="text" class="text-s" placeholder="<?php _e('请输入关键字'); ?>" value="<?php echo $request->filter('html')->keywords; ?>"<?php if ('' == $request->keywords): ?> onclick="value='';name='keywords';" <?php else: ?> name="keywords"<?php endif; ?>/>
+                            <?php if(isset($request->status)): ?>
+                                <input type="hidden" value="<?php echo $request->filter('html')->status; ?>" name="status" />
+                            <?php endif; ?>
+                            <?php if(isset($request->cid)): ?>
+                                <input type="hidden" value="<?php echo $request->filter('html')->cid; ?>" name="cid" />
+                            <?php endif; ?>
+                            <button type="submit" class="btn btn-s"><?php _e('筛选'); ?></button>
+                        </div>
+                    </form>
+                </div><!-- end .typecho-list-operate -->
+                
+                <form method="post" name="manage_comments" class="operate-form">
+                <div class="typecho-table-wrap">
+                    <table class="typecho-list-table">
+                        <colgroup>
+                            <col width="3%" class="kit-hidden-mb"/>
+                            <col width="6%" class="kit-hidden-mb" />
+                            <col width="20%"/>
+                            <col width="71%"/>
+                        </colgroup>
+                        <thead>
+                            <tr>
+                                <th class="kit-hidden-mb"> </th>
+                                <th><?php _e('作者'); ?></th>
+                                <th class="kit-hidden-mb"> </th>
+                                <th><?php _e('内容'); ?></th>
+                            </tr>
+                        </thead>
+                        <tbody>
+
+                        <?php if($comments->have()): ?>
+                        <?php while($comments->next()): ?>
+                        <tr id="<?php $comments->theId(); ?>" data-comment="<?php
+                        $comment = array(
+                            'author'    =>  $comments->author,
+                            'mail'      =>  $comments->mail,
+                            'url'       =>  $comments->url,
+                            'ip'        =>  $comments->ip,
+                            'type'        =>  $comments->type,
+                            'text'      =>  $comments->text
+                        );
+
+                        echo htmlspecialchars(json_encode($comment));
+                        ?>">
+                            <td valign="top" class="kit-hidden-mb">
+                                <input type="checkbox" value="<?php $comments->coid(); ?>" name="coid[]"/>
+                            </td>
+                            <td valign="top" class="kit-hidden-mb">
+                                <div class="comment-avatar">
+                                    <?php if ('comment' == $comments->type): ?>
+                                    <?php $comments->gravatar(40); ?>
+                                    <?php endif; ?>
+                                    <?php if ('comment' != $comments->type): ?>
+                                    <?php _e('引用'); ?>
+                                    <?php endif; ?>
+                                </div>
+                            </td>
+                            <td valign="top" class="comment-head">
+                                <div class="comment-meta">
+                                    <strong class="comment-author"><?php $comments->author(true); ?></strong>
+                                    <?php if($comments->mail): ?>
+                                    <br /><span><a href="<?php $comments->mail(true); ?>"><?php $comments->mail(); ?></a></span>
+                                    <?php endif; ?>
+                                    <?php if($comments->ip): ?>
+                                    <br /><span><?php $comments->ip(); ?></span>
+                                    <?php endif; ?>
+                                </div>
+                            </td>
+                            <td valign="top" class="comment-body">
+                                <div class="comment-date"><?php $comments->dateWord(); ?> 于 <a href="<?php $comments->permalink(); ?>"><?php $comments->title(); ?></a></div>
+                                <div class="comment-content">
+                                    <?php $comments->content(); ?>
+                                </div> 
+                                <div class="comment-action hidden-by-mouse">
+                                    <?php if('approved' == $comments->status): ?>
+                                    <span class="weak"><?php _e('通过'); ?></span>
+                                    <?php else: ?>
+                                    <a href="<?php $security->index('/action/comments-edit?do=approved&coid=' . $comments->coid); ?>" class="operate-approved"><?php _e('通过'); ?></a>
+                                    <?php endif; ?>
+                                    
+                                    <?php if('waiting' == $comments->status): ?>
+                                    <span class="weak"><?php _e('待审核'); ?></span>
+                                    <?php else: ?>
+                                    <a href="<?php $security->index('/action/comments-edit?do=waiting&coid=' . $comments->coid); ?>" class="operate-waiting"><?php _e('待审核'); ?></a>
+                                    <?php endif; ?>
+                                    
+                                    <?php if('spam' == $comments->status): ?>
+                                    <span class="weak"><?php _e('垃圾'); ?></span>
+                                    <?php else: ?>
+                                    <a href="<?php $security->index('/action/comments-edit?do=spam&coid=' . $comments->coid); ?>" class="operate-spam"><?php _e('垃圾'); ?></a>
+                                    <?php endif; ?>
+                                    
+                                    <a href="#<?php $comments->theId(); ?>" rel="<?php $security->index('/action/comments-edit?do=edit&coid=' . $comments->coid); ?>" class="operate-edit"><?php _e('编辑'); ?></a>
+
+                                    <?php if('approved' == $comments->status && 'comment' == $comments->type): ?>
+                                    <a href="#<?php $comments->theId(); ?>" rel="<?php $security->index('/action/comments-edit?do=reply&coid=' . $comments->coid); ?>" class="operate-reply"><?php _e('回复'); ?></a>
+                                    <?php endif; ?>
+                                    
+                                    <a lang="<?php _e('你确认要删除%s的评论吗?', htmlspecialchars($comments->author)); ?>" href="<?php $security->index('/action/comments-edit?do=delete&coid=' . $comments->coid); ?>" class="operate-delete"><?php _e('删除'); ?></a>
+                                </div>
+                            </td>
+                        </tr>
+                        <?php endwhile; ?>
+                        <?php else: ?>
+                        <tr>
+                            <td colspan="4"><h6 class="typecho-list-table-title"><?php _e('没有评论') ?></h6></td>
+                        </tr>
+                        <?php endif; ?>
+                        </tbody>
+                    </table><!-- end .typecho-list-table -->
+                </div><!-- end .typecho-table-wrap -->
+
+                <?php if(isset($request->cid)): ?>
+                <input type="hidden" value="<?php echo $request->filter('html')->cid; ?>" name="cid" />
+                <?php endif; ?>
+                </form><!-- end .operate-form -->
+
+                <div class="typecho-list-operate clearfix">
+                    <form method="get">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox" class="typecho-table-select-all" /></label>
+                            <div class="btn-group btn-drop">
+                            <button class="btn dropdown-toggle btn-s" type="button"><i class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i class="i-caret-down"></i></button>
+                            <ul class="dropdown-menu">
+                                <li><a href="<?php $security->index('/action/comments-edit?do=approved'); ?>"><?php _e('通过'); ?></a></li>
+                                <li><a href="<?php $security->index('/action/comments-edit?do=waiting'); ?>"><?php _e('待审核'); ?></a></li>
+                                <li><a href="<?php $security->index('/action/comments-edit?do=spam'); ?>"><?php _e('标记垃圾'); ?></a></li>
+                                <li><a lang="<?php _e('你确认要删除这些评论吗?'); ?>" href="<?php $security->index('/action/comments-edit?do=delete'); ?>"><?php _e('删除'); ?></a></li>
+                            </ul>
+                            <?php if('spam' == $request->get('status')): ?>
+                                <button lang="<?php _e('你确认要删除所有垃圾评论吗?'); ?>" class="btn btn-s btn-warn btn-operate" href="<?php $security->index('/action/comments-edit?do=delete-spam'); ?>"><?php _e('删除所有垃圾评论'); ?></button>
+                            <?php endif; ?>
+                            </div>
+                        </div>
+                        <?php if($comments->have()): ?>
+                        <ul class="typecho-pager">
+                            <?php $comments->pageNav(); ?>
+                        </ul>
+                        <?php endif; ?>
+                    </form>
+                </div><!-- end .typecho-list-operate -->
+            </div><!-- end .typecho-list -->
+        </div><!-- end .typecho-page-main -->
+    </div>
+</div>
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'table-js.php';
+?>
+<script type="text/javascript">
+$(document).ready(function () {
+    // 记住滚动条
+    function rememberScroll () {
+        $(window).bind('beforeunload', function () {
+            $.cookie('__typecho_comments_scroll', $('body').scrollTop());
+        });
+    }
+
+    // 自动滚动
+    (function () {
+        var scroll = $.cookie('__typecho_comments_scroll');
+
+        if (scroll) {
+            $.cookie('__typecho_comments_scroll', null);
+            $('html, body').scrollTop(scroll);
+        }
+    })();
+
+    $('.operate-delete').click(function () {
+        var t = $(this), href = t.attr('href'), tr = t.parents('tr');
+
+        if (confirm(t.attr('lang'))) {
+            tr.fadeOut(function () {
+                rememberScroll();
+                window.location.href = href;
+            });
+        }
+
+        return false;
+    });
+
+    $('.operate-approved, .operate-waiting, .operate-spam').click(function () {
+        rememberScroll();
+        window.location.href = $(this).attr('href');
+        return false;
+    });
+
+    $('.operate-reply').click(function () {
+        var td = $(this).parents('td'), t = $(this);
+
+        if ($('.comment-reply', td).length > 0) {
+            $('.comment-reply').remove();
+        } else {
+            var form = $('<form method="post" action="'
+                + t.attr('rel') + '" class="comment-reply">'
+                + '<p><label for="text" class="sr-only"><?php _e('内容'); ?></label><textarea id="text" name="text" class="w-90 mono" rows="3"></textarea></p>'
+                + '<p><button type="submit" class="btn btn-s primary"><?php _e('回复'); ?></button> <button type="button" class="btn btn-s cancel"><?php _e('取消'); ?></button></p>'
+                + '</form>').insertBefore($('.comment-action', td));
+
+            $('.cancel', form).click(function () {
+                $(this).parents('.comment-reply').remove();
+            });
+
+            var textarea = $('textarea', form).focus();
+
+            form.submit(function () {
+                var t = $(this), tr = t.parents('tr'), 
+                    reply = $('<div class="comment-reply-content"></div>').insertAfter($('.comment-content', tr));
+
+                var html = DOMPurify.sanitize(textarea.val(), {USE_PROFILES: {html: true}});
+                reply.html('<p>' + html + '</p>');
+                $.post(t.attr('action'), t.serialize(), function (o) {
+                    var html = DOMPurify.sanitize(o.comment.content, {USE_PROFILES: {html: true}});
+                    reply.html(html)
+                        .effect('highlight');
+                }, 'json');
+
+                t.remove();
+                return false;
+            });
+        }
+
+        return false;
+    });
+
+    $('.operate-edit').click(function () {
+        var tr = $(this).parents('tr'), t = $(this), id = tr.attr('id'), comment = tr.data('comment');
+        tr.hide();
+
+        var edit = $('<tr class="comment-edit"><td> </td>'
+                        + '<td colspan="2" valign="top"><form method="post" action="'
+                        + t.attr('rel') + '" class="comment-edit-info">'
+                        + '<p><label for="' + id + '-author"><?php _e('用户名'); ?></label><input class="text-s w-100" id="'
+                        + id + '-author" name="author" type="text"></p>'
+                        + '<p><label for="' + id + '-mail"><?php _e('电子邮箱'); ?></label>'
+                        + '<input class="text-s w-100" type="email" name="mail" id="' + id + '-mail"></p>'
+                        + '<p><label for="' + id + '-url"><?php _e('个人主页'); ?></label>'
+                        + '<input class="text-s w-100" type="text" name="url" id="' + id + '-url"></p></form></td>'
+                        + '<td valign="top"><form method="post" action="'
+                        + t.attr('rel') + '" class="comment-edit-content"><p><label for="' + id + '-text"><?php _e('内容'); ?></label>'
+                        + '<textarea name="text" id="' + id + '-text" rows="6" class="w-90 mono"></textarea></p>'
+                        + '<p><button type="submit" class="btn btn-s primary"><?php _e('提交'); ?></button> '
+                        + '<button type="button" class="btn btn-s cancel"><?php _e('取消'); ?></button></p></form></td></tr>')
+                        .data('id', id).data('comment', comment).insertAfter(tr);
+
+        $('input[name=author]', edit).val(comment.author);
+        $('input[name=mail]', edit).val(comment.mail);
+        $('input[name=url]', edit).val(comment.url);
+        $('textarea[name=text]', edit).val(comment.text).focus();
+
+        $('.cancel', edit).click(function () {
+            var tr = $(this).parents('tr');
+
+            $('#' + tr.data('id')).show();
+            tr.remove();
+        });
+
+        $('form', edit).submit(function () {
+            var t = $(this), tr = t.parents('tr'),
+                oldTr = $('#' + tr.data('id')),
+                comment = oldTr.data('comment');
+
+            $('form', tr).each(function () {
+                var items  = $(this).serializeArray();
+
+                for (var i = 0; i < items.length; i ++) {
+                    var item = items[i];
+                    comment[item.name] = item.value;
+                }
+            });
+
+            var unsafeHTML = '<strong class="comment-author">'
+                + (comment.url ? '<a target="_blank" href="' + comment.url + '">'
+                + comment.author + '</a>' : comment.author) + '</strong>'
+                + ('comment' != comment.type ? '<small><?php _e('引用'); ?></small>' : '')
+                + (comment.mail ? '<br /><span><a href="mailto:' + comment.mail + '">'
+                + comment.mail + '</a></span>' : '')
+                + (comment.ip ? '<br /><span>' + comment.ip + '</span>' : '');
+
+            var html = DOMPurify.sanitize(unsafeHTML, {USE_PROFILES: {html: true}});
+            var content = DOMPurify.sanitize(comment.text, {USE_PROFILES: {html: true}});
+            $('.comment-meta', oldTr).html(html)
+                .effect('highlight');
+            $('.comment-content', oldTr).html('<p>' + content + '</p>');
+            oldTr.data('comment', comment);
+
+            $.post(t.attr('action'), comment, function (o) {
+                var content = DOMPurify.sanitize(o.comment.content, {USE_PROFILES: {html: true}});
+                $('.comment-content', oldTr).html('<p>' + content + '</p>')
+                    .effect('highlight');
+            }, 'json');
+            
+            oldTr.show();
+            tr.remove();
+
+            return false;
+        });
+
+        return false;
+    });
+});
+</script>
+<?php
+include 'footer.php';
+?>

+ 144 - 0
admin/manage-medias.php

@@ -0,0 +1,144 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+$stat = \Widget\Stat::alloc();
+$attachments = \Widget\Contents\Attachment\Admin::alloc();
+?>
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12">
+
+                <div class="typecho-list-operate clearfix">
+                    <form method="get">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox"
+                                                                                   class="typecho-table-select-all"/></label>
+                            <div class="btn-group btn-drop">
+                                <button class="btn dropdown-toggle btn-s" type="button"><i
+                                        class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i
+                                        class="i-caret-down"></i></button>
+                                <ul class="dropdown-menu">
+                                    <li><a lang="<?php _e('你确认要删除这些文件吗?'); ?>"
+                                           href="<?php $security->index('/action/contents-attachment-edit?do=delete'); ?>"><?php _e('删除'); ?></a>
+                                    </li>
+                                </ul>
+                                <button class="btn btn-s btn-warn btn-operate"
+                                        href="<?php $security->index('/action/contents-attachment-edit?do=clear'); ?>"
+                                        lang="<?php _e('您确认要清理未归档的文件吗?'); ?>"><?php _e('清理未归档文件'); ?></button>
+                            </div>
+                        </div>
+                        <div class="search" role="search">
+                            <?php if ('' != $request->keywords): ?>
+                                <a href="<?php $options->adminUrl('manage-medias.php'); ?>"><?php _e('&laquo; 取消筛选'); ?></a>
+                            <?php endif; ?>
+                            <input type="text" class="text-s" placeholder="<?php _e('请输入关键字'); ?>"
+                                   value="<?php echo $request->filter('html')->keywords; ?>"<?php if ('' == $request->keywords): ?> onclick="value='';name='keywords';" <?php else: ?> name="keywords"<?php endif; ?>/>
+                            <button type="submit" class="btn btn-s"><?php _e('筛选'); ?></button>
+                        </div>
+                    </form>
+                </div><!-- end .typecho-list-operate -->
+
+                <form method="post" name="manage_medias" class="operate-form">
+                    <div class="typecho-table-wrap">
+                        <table class="typecho-list-table draggable">
+                            <colgroup>
+                                <col width="20" class="kit-hidden-mb"/>
+                                <col width="6%" class="kit-hidden-mb"/>
+                                <col width="30%"/>
+                                <col width="" class="kit-hidden-mb"/>
+                                <col width="30%" class="kit-hidden-mb"/>
+                                <col width="16%"/>
+                            </colgroup>
+                            <thead>
+                            <tr>
+                                <th class="kit-hidden-mb"></th>
+                                <th class="kit-hidden-mb"></th>
+                                <th><?php _e('文件名'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('上传者'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('所属文章'); ?></th>
+                                <th><?php _e('发布日期'); ?></th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <?php if ($attachments->have()): ?>
+                                <?php while ($attachments->next()): ?>
+                                    <?php $mime = \Typecho\Common::mimeIconType($attachments->attachment->mime); ?>
+                                    <tr id="<?php $attachments->theId(); ?>">
+                                        <td class="kit-hidden-mb"><input type="checkbox"
+                                                                         value="<?php $attachments->cid(); ?>"
+                                                                         name="cid[]"/></td>
+                                        <td class="kit-hidden-mb"><a
+                                                href="<?php $options->adminUrl('manage-comments.php?cid=' . $attachments->cid); ?>"
+                                                class="balloon-button size-<?php echo \Typecho\Common::splitByCount($attachments->commentsNum, 1, 10, 20, 50, 100); ?>"><?php $attachments->commentsNum(); ?></a>
+                                        </td>
+                                        <td>
+                                            <i class="mime-<?php echo $mime; ?>"></i>
+                                            <a href="<?php $options->adminUrl('media.php?cid=' . $attachments->cid); ?>"><?php $attachments->title(); ?></a>
+                                            <a href="<?php $attachments->permalink(); ?>"
+                                               title="<?php _e('浏览 %s', $attachments->title); ?>"><i
+                                                    class="i-exlink"></i></a>
+                                        </td>
+                                        <td class="kit-hidden-mb"><?php $attachments->author(); ?></td>
+                                        <td class="kit-hidden-mb">
+                                            <?php if ($attachments->parentPost->cid): ?>
+                                                <a href="<?php $options->adminUrl('write-' . (0 === strpos($attachments->parentPost->type, 'post') ? 'post' : 'page') . '.php?cid=' . $attachments->parentPost->cid); ?>"><?php $attachments->parentPost->title(); ?></a>
+                                            <?php else: ?>
+                                                <span class="description"><?php _e('未归档'); ?></span>
+                                            <?php endif; ?>
+                                        </td>
+                                        <td><?php $attachments->dateWord(); ?></td>
+                                    </tr>
+                                <?php endwhile; ?>
+                            <?php else: ?>
+                                <tr>
+                                    <td colspan="6"><h6 class="typecho-list-table-title"><?php _e('没有任何文件'); ?></h6>
+                                    </td>
+                                </tr>
+                            <?php endif; ?>
+                            </tbody>
+                        </table><!-- end .typecho-list-table -->
+                    </div><!-- end .typecho-table-wrap -->
+                </form><!-- end .operate-form -->
+
+                <div class="typecho-list-operate clearfix">
+                    <form method="get">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox"
+                                                                                   class="typecho-table-select-all"/></label>
+                            <div class="btn-group btn-drop">
+                                <button class="btn dropdown-toggle btn-s" type="button"><i
+                                        class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i
+                                        class="i-caret-down"></i></button>
+                                <ul class="dropdown-menu">
+                                    <li><a lang="<?php _e('你确认要删除这些文件吗?'); ?>"
+                                           href="<?php $security->index('/action/contents-attachment-edit?do=delete'); ?>"><?php _e('删除'); ?></a>
+                                    </li>
+                                </ul>
+                            </div>
+                            <button class="btn btn-s btn-warn btn-operate"
+                                    href="<?php $security->index('/action/contents-attachment-edit?do=clear'); ?>"
+                                    lang="<?php _e('您确认要清理未归档的文件吗?'); ?>"><?php _e('清理未归档文件'); ?></button>
+                        </div>
+                        <?php if ($attachments->have()): ?>
+                            <ul class="typecho-pager">
+                                <?php $attachments->pageNav(); ?>
+                            </ul>
+                        <?php endif; ?>
+                    </form>
+                </div><!-- end .typecho-list-operate -->
+
+            </div>
+        </div><!-- end .typecho-page-main -->
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'table-js.php';
+include 'footer.php';
+?>

+ 156 - 0
admin/manage-pages.php

@@ -0,0 +1,156 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+$stat = \Widget\Stat::alloc();
+$pages = \Widget\Contents\Page\Admin::alloc();
+?>
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12 typecho-list">
+                <div class="typecho-list-operate clearfix">
+                    <form method="get">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox"
+                                                                                   class="typecho-table-select-all"/></label>
+                            <div class="btn-group btn-drop">
+                                <button class="btn dropdown-toggle btn-s" type="button"><i
+                                        class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i
+                                        class="i-caret-down"></i></button>
+                                <ul class="dropdown-menu">
+                                    <li><a lang="<?php _e('你确认要删除这些页面吗?'); ?>"
+                                           href="<?php $security->index('/action/contents-page-edit?do=delete'); ?>"><?php _e('删除'); ?></a>
+                                    </li>
+                                    <li>
+                                        <a href="<?php $security->index('/action/contents-page-edit?do=mark&status=publish'); ?>"><?php _e('标记为<strong>%s</strong>', _t('公开')); ?></a>
+                                    </li>
+                                    <li>
+                                        <a href="<?php $security->index('/action/contents-page-edit?do=mark&status=hidden'); ?>"><?php _e('标记为<strong>%s</strong>', _t('隐藏')); ?></a>
+                                    </li>
+                                </ul>
+                            </div>
+                        </div>
+
+                        <div class="search" role="search">
+                            <?php if ('' != $request->keywords): ?>
+                                <a href="<?php $options->adminUrl('manage-pages.php'); ?>"><?php _e('&laquo; 取消筛选'); ?></a>
+                            <?php endif; ?>
+                            <input type="text" class="text-s" placeholder="<?php _e('请输入关键字'); ?>"
+                                   value="<?php echo $request->filter('html')->keywords; ?>" name="keywords"/>
+                            <button type="submit" class="btn btn-s"><?php _e('筛选'); ?></button>
+                        </div>
+                    </form>
+                </div><!-- end .typecho-list-operate -->
+
+                <form method="post" name="manage_pages" class="operate-form">
+                    <div class="typecho-table-wrap">
+                        <table class="typecho-list-table">
+                            <colgroup>
+                                <col width="20" class="kit-hidden-mb"/>
+                                <col width="6%" class="kit-hidden-mb"/>
+                                <col width="30%"/>
+                                <col width="30%"/>
+                                <col width="" class="kit-hidden-mb"/>
+                                <col width="16%"/>
+                            </colgroup>
+                            <thead>
+                            <tr class="nodrag">
+                                <th class="kit-hidden-mb"></th>
+                                <th class="kit-hidden-mb"></th>
+                                <th><?php _e('标题'); ?></th>
+                                <th><?php _e('缩略名'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('作者'); ?></th>
+                                <th><?php _e('日期'); ?></th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <?php if ($pages->have()): ?>
+                                <?php while ($pages->next()): ?>
+                                    <tr id="<?php $pages->theId(); ?>">
+                                        <td class="kit-hidden-mb"><input type="checkbox" value="<?php $pages->cid(); ?>"
+                                                                         name="cid[]"/></td>
+                                        <td class="kit-hidden-mb"><a
+                                                href="<?php $options->adminUrl('manage-comments.php?cid=' . $pages->cid); ?>"
+                                                class="balloon-button size-<?php echo \Typecho\Common::splitByCount($pages->commentsNum, 1, 10, 20, 50, 100); ?>"
+                                                title="<?php $pages->commentsNum(); ?> <?php _e('评论'); ?>"><?php $pages->commentsNum(); ?></a>
+                                        </td>
+                                        <td>
+                                            <a href="<?php $options->adminUrl('write-page.php?cid=' . $pages->cid); ?>"><?php $pages->title(); ?></a>
+                                            <?php
+                                            if ($pages->hasSaved || 'page_draft' == $pages->type) {
+                                                echo '<em class="status">' . _t('草稿') . '</em>';
+                                            }
+
+                                            if ('hidden' == $pages->status) {
+                                                echo '<em class="status">' . _t('隐藏') . '</em>';
+                                            }
+                                            ?>
+                                            <a href="<?php $options->adminUrl('write-page.php?cid=' . $pages->cid); ?>"
+                                               title="<?php _e('编辑 %s', htmlspecialchars($pages->title)); ?>"><i
+                                                    class="i-edit"></i></a>
+                                            <?php if ('page_draft' != $pages->type): ?>
+                                                <a href="<?php $pages->permalink(); ?>"
+                                                   title="<?php _e('浏览 %s', htmlspecialchars($pages->title)); ?>"><i
+                                                        class="i-exlink"></i></a>
+                                            <?php endif; ?>
+                                        </td>
+                                        <td><?php $pages->slug(); ?></td>
+                                        <td class="kit-hidden-mb"><?php $pages->author(); ?></td>
+                                        <td>
+                                            <?php if ($pages->hasSaved): ?>
+                                                <span class="description">
+                                <?php $modifyDate = new \Typecho\Date($pages->modified); ?>
+                                <?php _e('保存于 %s', $modifyDate->word()); ?>
+                                </span>
+                                            <?php else: ?>
+                                                <?php $pages->dateWord(); ?>
+                                            <?php endif; ?>
+                                        </td>
+                                    </tr>
+                                <?php endwhile; ?>
+                            <?php else: ?>
+                                <tr>
+                                    <td colspan="6"><h6 class="typecho-list-table-title"><?php _e('没有任何页面'); ?></h6>
+                                    </td>
+                                </tr>
+                            <?php endif; ?>
+                            </tbody>
+                        </table>
+                    </div><!-- end .typecho-table-wrap -->
+                </form><!-- end .operate-form -->
+            </div><!-- end .typecho-list -->
+        </div><!-- end .typecho-page-main -->
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'table-js.php';
+?>
+
+<?php if (!isset($request->status) || 'publish' == $request->get('status')): ?>
+    <script type="text/javascript">
+        (function () {
+            $(document).ready(function () {
+                var table = $('.typecho-list-table').tableDnD({
+                    onDrop: function () {
+                        var ids = [];
+
+                        $('input[type=checkbox]', table).each(function () {
+                            ids.push($(this).val());
+                        });
+
+                        $.post('<?php $security->index('/action/contents-page-edit?do=sort'); ?>',
+                            $.param({cid: ids}));
+                    }
+                });
+            });
+        })();
+    </script>
+<?php endif; ?>
+
+<?php include 'footer.php'; ?>

+ 258 - 0
admin/manage-posts.php

@@ -0,0 +1,258 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+$stat = \Widget\Stat::alloc();
+$posts = \Widget\Contents\Post\Admin::alloc();
+$isAllPosts = ('on' == $request->get('__typecho_all_posts') || 'on' == \Typecho\Cookie::get('__typecho_all_posts'));
+?>
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12 typecho-list">
+                <div class="clearfix">
+                    <ul class="typecho-option-tabs right">
+                        <?php if ($user->pass('editor', true) && !isset($request->uid)): ?>
+                            <li class="<?php if ($isAllPosts): ?> current<?php endif; ?>"><a
+                                    href="<?php echo $request->makeUriByRequest('__typecho_all_posts=on&page=1'); ?>"><?php _e('所有'); ?></a>
+                            </li>
+                            <li class="<?php if (!$isAllPosts): ?> current<?php endif; ?>"><a
+                                    href="<?php echo $request->makeUriByRequest('__typecho_all_posts=off&page=1'); ?>"><?php _e('我的'); ?></a>
+                            </li>
+                        <?php endif; ?>
+                    </ul>
+                    <ul class="typecho-option-tabs">
+                        <li<?php if (!isset($request->status) || 'all' == $request->get('status')): ?> class="current"<?php endif; ?>>
+                            <a href="<?php $options->adminUrl('manage-posts.php'
+                                . (isset($request->uid) ? '?uid=' . $request->filter('encode')->uid : '')); ?>"><?php _e('可用'); ?></a>
+                        </li>
+                        <li<?php if ('waiting' == $request->get('status')): ?> class="current"<?php endif; ?>><a
+                                href="<?php $options->adminUrl('manage-posts.php?status=waiting'
+                                    . (isset($request->uid) ? '&uid=' . $request->filter('encode')->uid : '')); ?>"><?php _e('待审核'); ?>
+                                <?php if (!$isAllPosts && $stat->myWaitingPostsNum > 0 && !isset($request->uid)): ?>
+                                    <span class="balloon"><?php $stat->myWaitingPostsNum(); ?></span>
+                                <?php elseif ($isAllPosts && $stat->waitingPostsNum > 0 && !isset($request->uid)): ?>
+                                    <span class="balloon"><?php $stat->waitingPostsNum(); ?></span>
+                                <?php elseif (isset($request->uid) && $stat->currentWaitingPostsNum > 0): ?>
+                                    <span class="balloon"><?php $stat->currentWaitingPostsNum(); ?></span>
+                                <?php endif; ?>
+                            </a></li>
+                        <li<?php if ('draft' == $request->get('status')): ?> class="current"<?php endif; ?>><a
+                                href="<?php $options->adminUrl('manage-posts.php?status=draft'
+                                    . (isset($request->uid) ? '&uid=' . $request->filter('encode')->uid : '')); ?>"><?php _e('草稿'); ?>
+                                <?php if (!$isAllPosts && $stat->myDraftPostsNum > 0 && !isset($request->uid)): ?>
+                                    <span class="balloon"><?php $stat->myDraftPostsNum(); ?></span>
+                                <?php elseif ($isAllPosts && $stat->draftPostsNum > 0 && !isset($request->uid)): ?>
+                                    <span class="balloon"><?php $stat->draftPostsNum(); ?></span>
+                                <?php elseif (isset($request->uid) && $stat->currentDraftPostsNum > 0): ?>
+                                    <span class="balloon"><?php $stat->currentDraftPostsNum(); ?></span>
+                                <?php endif; ?>
+                            </a></li>
+                    </ul>
+                </div>
+
+                <div class="typecho-list-operate clearfix">
+                    <form method="get">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox"
+                                                                                   class="typecho-table-select-all"/></label>
+                            <div class="btn-group btn-drop">
+                                <button class="btn dropdown-toggle btn-s" type="button"><i
+                                        class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i
+                                        class="i-caret-down"></i></button>
+                                <ul class="dropdown-menu">
+                                    <li><a lang="<?php _e('你确认要删除这些文章吗?'); ?>"
+                                           href="<?php $security->index('/action/contents-post-edit?do=delete'); ?>"><?php _e('删除'); ?></a>
+                                    </li>
+                                    <?php if ($user->pass('editor', true)): ?>
+                                        <li>
+                                            <a href="<?php $security->index('/action/contents-post-edit?do=mark&status=publish'); ?>"><?php _e('标记为<strong>%s</strong>', _t('公开')); ?></a>
+                                        </li>
+                                        <li>
+                                            <a href="<?php $security->index('/action/contents-post-edit?do=mark&status=waiting'); ?>"><?php _e('标记为<strong>%s</strong>', _t('待审核')); ?></a>
+                                        </li>
+                                        <li>
+                                            <a href="<?php $security->index('/action/contents-post-edit?do=mark&status=hidden'); ?>"><?php _e('标记为<strong>%s</strong>', _t('隐藏')); ?></a>
+                                        </li>
+                                        <li>
+                                            <a href="<?php $security->index('/action/contents-post-edit?do=mark&status=private'); ?>"><?php _e('标记为<strong>%s</strong>', _t('私密')); ?></a>
+                                        </li>
+                                    <?php endif; ?>
+                                </ul>
+                            </div>
+                        </div>
+                        <div class="search" role="search">
+                            <?php if ('' != $request->keywords || '' != $request->category): ?>
+                                <a href="<?php $options->adminUrl('manage-posts.php'
+                                    . (isset($request->status) || isset($request->uid) ? '?' .
+                                        (isset($request->status) ? 'status=' . $request->filter('encode')->status : '') .
+                                        (isset($request->uid) ? (isset($request->status) ? '&' : '') . 'uid=' . $request->filter('encode')->uid : '') : '')); ?>"><?php _e('&laquo; 取消筛选'); ?></a>
+                            <?php endif; ?>
+                            <input type="text" class="text-s" placeholder="<?php _e('请输入关键字'); ?>"
+                                   value="<?php echo $request->filter('html')->keywords; ?>" name="keywords"/>
+                            <select name="category">
+                                <option value=""><?php _e('所有分类'); ?></option>
+                                <?php \Widget\Metas\Category\Rows::alloc()->to($category); ?>
+                                <?php while ($category->next()): ?>
+                                    <option
+                                        value="<?php $category->mid(); ?>"<?php if ($request->get('category') == $category->mid): ?> selected="true"<?php endif; ?>><?php $category->name(); ?></option>
+                                <?php endwhile; ?>
+                            </select>
+                            <button type="submit" class="btn btn-s"><?php _e('筛选'); ?></button>
+                            <?php if (isset($request->uid)): ?>
+                                <input type="hidden" value="<?php echo $request->filter('html')->uid; ?>"
+                                       name="uid"/>
+                            <?php endif; ?>
+                            <?php if (isset($request->status)): ?>
+                                <input type="hidden" value="<?php echo $request->filter('html')->status; ?>"
+                                       name="status"/>
+                            <?php endif; ?>
+                        </div>
+                    </form>
+                </div><!-- end .typecho-list-operate -->
+
+                <form method="post" name="manage_posts" class="operate-form">
+                    <div class="typecho-table-wrap">
+                        <table class="typecho-list-table">
+                            <colgroup>
+                                <col width="20" class="kit-hidden-mb"/>
+                                <col width="6%" class="kit-hidden-mb"/>
+                                <col width="45%"/>
+                                <col width="" class="kit-hidden-mb"/>
+                                <col width="18%" class="kit-hidden-mb"/>
+                                <col width="16%"/>
+                            </colgroup>
+                            <thead>
+                            <tr>
+                                <th class="kit-hidden-mb"></th>
+                                <th class="kit-hidden-mb"></th>
+                                <th><?php _e('标题'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('作者'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('分类'); ?></th>
+                                <th><?php _e('日期'); ?></th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <?php if ($posts->have()): ?>
+                                <?php while ($posts->next()): ?>
+                                    <tr id="<?php $posts->theId(); ?>">
+                                        <td class="kit-hidden-mb"><input type="checkbox" value="<?php $posts->cid(); ?>"
+                                                                         name="cid[]"/></td>
+                                        <td class="kit-hidden-mb"><a
+                                                href="<?php $options->adminUrl('manage-comments.php?cid=' . ($posts->parentId ? $posts->parentId : $posts->cid)); ?>"
+                                                class="balloon-button size-<?php echo \Typecho\Common::splitByCount($posts->commentsNum, 1, 10, 20, 50, 100); ?>"
+                                                title="<?php $posts->commentsNum(); ?> <?php _e('评论'); ?>"><?php $posts->commentsNum(); ?></a>
+                                        </td>
+                                        <td>
+                                            <a href="<?php $options->adminUrl('write-post.php?cid=' . $posts->cid); ?>"><?php $posts->title(); ?></a>
+                                            <?php
+                                            if ($posts->hasSaved || 'post_draft' == $posts->type) {
+                                                echo '<em class="status">' . _t('草稿') . '</em>';
+                                            }
+
+                                            if ('hidden' == $posts->status) {
+                                                echo '<em class="status">' . _t('隐藏') . '</em>';
+                                            } elseif ('waiting' == $posts->status) {
+                                                echo '<em class="status">' . _t('待审核') . '</em>';
+                                            } elseif ('private' == $posts->status) {
+                                                echo '<em class="status">' . _t('私密') . '</em>';
+                                            } elseif ($posts->password) {
+                                                echo '<em class="status">' . _t('密码保护') . '</em>';
+                                            }
+                                            ?>
+                                            <a href="<?php $options->adminUrl('write-post.php?cid=' . $posts->cid); ?>"
+                                               title="<?php _e('编辑 %s', htmlspecialchars($posts->title)); ?>"><i
+                                                    class="i-edit"></i></a>
+                                            <?php if ('post_draft' != $posts->type): ?>
+                                                <a href="<?php $posts->permalink(); ?>"
+                                                   title="<?php _e('浏览 %s', htmlspecialchars($posts->title)); ?>"><i
+                                                        class="i-exlink"></i></a>
+                                            <?php endif; ?>
+                                        </td>
+                                        <td class="kit-hidden-mb"><a
+                                                href="<?php $options->adminUrl('manage-posts.php?__typecho_all_posts=off&uid=' . $posts->author->uid); ?>"><?php $posts->author(); ?></a>
+                                        </td>
+                                        <td class="kit-hidden-mb"><?php $categories = $posts->categories;
+                                            $length = count($categories); ?>
+                                            <?php foreach ($categories as $key => $val): ?>
+                                                <?php echo '<a href="';
+                                                $options->adminUrl('manage-posts.php?category=' . $val['mid']
+                                                    . (isset($request->uid) ? '&uid=' . $request->filter('encode')->uid : '')
+                                                    . (isset($request->status) ? '&status=' . $request->filter('encode')->status : ''));
+                                                echo '">' . $val['name'] . '</a>' . ($key < $length - 1 ? ', ' : ''); ?>
+                                            <?php endforeach; ?>
+                                        </td>
+                                        <td>
+                                            <?php if ($posts->hasSaved): ?>
+                                                <span class="description">
+                                <?php $modifyDate = new \Typecho\Date($posts->modified); ?>
+                                <?php _e('保存于 %s', $modifyDate->word()); ?>
+                                </span>
+                                            <?php else: ?>
+                                                <?php $posts->dateWord(); ?>
+                                            <?php endif; ?>
+                                        </td>
+                                    </tr>
+                                <?php endwhile; ?>
+                            <?php else: ?>
+                                <tr>
+                                    <td colspan="6"><h6 class="typecho-list-table-title"><?php _e('没有任何文章'); ?></h6>
+                                    </td>
+                                </tr>
+                            <?php endif; ?>
+                            </tbody>
+                        </table>
+                    </div>
+                </form><!-- end .operate-form -->
+
+                <div class="typecho-list-operate clearfix">
+                    <form method="get">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox"
+                                                                                   class="typecho-table-select-all"/></label>
+                            <div class="btn-group btn-drop">
+                                <button class="btn dropdown-toggle btn-s" type="button"><i
+                                        class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i
+                                        class="i-caret-down"></i></button>
+                                <ul class="dropdown-menu">
+                                    <li><a lang="<?php _e('你确认要删除这些文章吗?'); ?>"
+                                           href="<?php $security->index('/action/contents-post-edit?do=delete'); ?>"><?php _e('删除'); ?></a>
+                                    </li>
+                                    <?php if ($user->pass('editor', true)): ?>
+                                        <li>
+                                            <a href="<?php $security->index('/action/contents-post-edit?do=mark&status=publish'); ?>"><?php _e('标记为<strong>%s</strong>', _t('公开')); ?></a>
+                                        </li>
+                                        <li>
+                                            <a href="<?php $security->index('/action/contents-post-edit?do=mark&status=waiting'); ?>"><?php _e('标记为<strong>%s</strong>', _t('待审核')); ?></a>
+                                        </li>
+                                        <li>
+                                            <a href="<?php $security->index('/action/contents-post-edit?do=mark&status=hidden'); ?>"><?php _e('标记为<strong>%s</strong>', _t('隐藏')); ?></a>
+                                        </li>
+                                        <li>
+                                            <a href="<?php $security->index('/action/contents-post-edit?do=mark&status=private'); ?>"><?php _e('标记为<strong>%s</strong>', _t('私密')); ?></a>
+                                        </li>
+                                    <?php endif; ?>
+                                </ul>
+                            </div>
+                        </div>
+
+                        <?php if ($posts->have()): ?>
+                            <ul class="typecho-pager">
+                                <?php $posts->pageNav(); ?>
+                            </ul>
+                        <?php endif; ?>
+                    </form>
+                </div><!-- end .typecho-list-operate -->
+            </div><!-- end .typecho-list -->
+        </div><!-- end .typecho-page-main -->
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'table-js.php';
+include 'footer.php';
+?>

+ 102 - 0
admin/manage-tags.php

@@ -0,0 +1,102 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+\Widget\Metas\Tag\Admin::alloc()->to($tags);
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main manage-metas">
+
+            <div class="col-mb-12 col-tb-8" role="main">
+
+                <form method="post" name="manage_tags" class="operate-form">
+                    <div class="typecho-list-operate clearfix">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox"
+                                                                                   class="typecho-table-select-all"/></label>
+                            <div class="btn-group btn-drop">
+                                <button class="btn dropdown-toggle btn-s" type="button"><i
+                                        class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i
+                                        class="i-caret-down"></i></button>
+                                <ul class="dropdown-menu">
+                                    <li><a lang="<?php _e('你确认要删除这些标签吗?'); ?>"
+                                           href="<?php $security->index('/action/metas-tag-edit?do=delete'); ?>"><?php _e('删除'); ?></a>
+                                    </li>
+                                    <li><a lang="<?php _e('刷新标签可能需要等待较长时间, 你确认要刷新这些标签吗?'); ?>"
+                                           href="<?php $security->index('/action/metas-tag-edit?do=refresh'); ?>"><?php _e('刷新'); ?></a>
+                                    </li>
+                                    <li class="multiline">
+                                        <button type="button" class="btn btn-s merge"
+                                                rel="<?php $security->index('/action/metas-tag-edit?do=merge'); ?>"><?php _e('合并到'); ?></button>
+                                        <input type="text" name="merge" class="text-s"/>
+                                    </li>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+
+                    <ul class="typecho-list-notable tag-list clearfix">
+                        <?php if ($tags->have()): ?>
+                            <?php while ($tags->next()): ?>
+                                <li class="size-<?php $tags->split(5, 10, 20, 30); ?>" id="<?php $tags->theId(); ?>">
+                                    <input type="checkbox" value="<?php $tags->mid(); ?>" name="mid[]"/>
+                                    <span
+                                        rel="<?php echo $request->makeUriByRequest('mid=' . $tags->mid); ?>"><?php $tags->name(); ?></span>
+                                    <a class="tag-edit-link"
+                                       href="<?php echo $request->makeUriByRequest('mid=' . $tags->mid); ?>"><i
+                                            class="i-edit"></i></a>
+                                </li>
+                            <?php endwhile; ?>
+                        <?php else: ?>
+                            <h6 class="typecho-list-table-title"><?php _e('没有任何标签'); ?></h6>
+                        <?php endif; ?>
+                    </ul>
+                    <input type="hidden" name="do" value="delete"/>
+                </form>
+
+            </div>
+            <div class="col-mb-12 col-tb-4" role="form">
+                <?php \Widget\Metas\Tag\Edit::alloc()->form()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+?>
+
+<script type="text/javascript">
+    (function () {
+        $(document).ready(function () {
+
+            $('.typecho-list-notable').tableSelectable({
+                checkEl: 'input[type=checkbox]',
+                rowEl: 'li',
+                selectAllEl: '.typecho-table-select-all',
+                actionEl: '.dropdown-menu a'
+            });
+
+            $('.btn-drop').dropdownMenu({
+                btnEl: '.dropdown-toggle',
+                menuEl: '.dropdown-menu'
+            });
+
+            $('.dropdown-menu button.merge').click(function () {
+                var btn = $(this);
+                btn.parents('form').attr('action', btn.attr('rel')).submit();
+            });
+
+            <?php if (isset($request->mid)): ?>
+            $('.typecho-mini-panel').effect('highlight', '#AACB36');
+            <?php endif; ?>
+        });
+    })();
+</script>
+<?php include 'footer.php'; ?>
+

+ 139 - 0
admin/manage-users.php

@@ -0,0 +1,139 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+$users = \Widget\Users\Admin::alloc();
+?>
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12 typecho-list">
+                <div class="typecho-list-operate clearfix">
+                    <form method="get">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox"
+                                                                                   class="typecho-table-select-all"/></label>
+                            <div class="btn-group btn-drop">
+                                <button class="btn dropdown-toggle btn-s" type="button"><i
+                                        class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i
+                                        class="i-caret-down"></i></button>
+                                <ul class="dropdown-menu">
+                                    <li><a lang="<?php _e('你确认要删除这些用户吗?'); ?>"
+                                           href="<?php $security->index('/action/users-edit?do=delete'); ?>"><?php _e('删除'); ?></a>
+                                    </li>
+                                </ul>
+                            </div>
+                        </div>
+                        <div class="search" role="search">
+                            <?php if ('' != $request->keywords): ?>
+                                <a href="<?php $options->adminUrl('manage-users.php'); ?>"><?php _e('&laquo; 取消筛选'); ?></a>
+                            <?php endif; ?>
+                            <input type="text" class="text-s" placeholder="<?php _e('请输入关键字'); ?>"
+                                   value="<?php echo $request->filter('html')->keywords; ?>" name="keywords"/>
+                            <button type="submit" class="btn btn-s"><?php _e('筛选'); ?></button>
+                        </div>
+                    </form>
+                </div><!-- end .typecho-list-operate -->
+
+                <form method="post" name="manage_users" class="operate-form">
+                    <div class="typecho-table-wrap">
+                        <table class="typecho-list-table">
+                            <colgroup>
+                                <col width="20" class="kit-hidden-mb"/>
+                                <col width="6%" class="kit-hidden-mb"/>
+                                <col width="30%"/>
+                                <col width="" class="kit-hidden-mb"/>
+                                <col width="25%" class="kit-hidden-mb"/>
+                                <col width="15%"/>
+                            </colgroup>
+                            <thead>
+                            <tr>
+                                <th class="kit-hidden-mb"></th>
+                                <th class="kit-hidden-mb"></th>
+                                <th><?php _e('用户名'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('昵称'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('电子邮件'); ?></th>
+                                <th><?php _e('用户组'); ?></th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <?php while ($users->next()): ?>
+                                <tr id="user-<?php $users->uid(); ?>">
+                                    <td class="kit-hidden-mb"><input type="checkbox" value="<?php $users->uid(); ?>"
+                                                                     name="uid[]"/></td>
+                                    <td class="kit-hidden-mb"><a
+                                            href="<?php $options->adminUrl('manage-posts.php?__typecho_all_posts=off&uid=' . $users->uid); ?>"
+                                            class="balloon-button left size-<?php echo \Typecho\Common::splitByCount($users->postsNum, 1, 10, 20, 50, 100); ?>"><?php $users->postsNum(); ?></a>
+                                    </td>
+                                    <td>
+                                        <a href="<?php $options->adminUrl('user.php?uid=' . $users->uid); ?>"><?php $users->name(); ?></a>
+                                        <a href="<?php $users->permalink(); ?>"
+                                           title="<?php _e('浏览 %s', $users->screenName); ?>"><i
+                                                class="i-exlink"></i></a>
+                                    </td>
+                                    <td class="kit-hidden-mb"><?php $users->screenName(); ?></td>
+                                    <td class="kit-hidden-mb"><?php if ($users->mail): ?><a
+                                            href="mailto:<?php $users->mail(); ?>"><?php $users->mail(); ?></a><?php else: _e('暂无'); endif; ?>
+                                    </td>
+                                    <td><?php switch ($users->group) {
+                                            case 'administrator':
+                                                _e('管理员');
+                                                break;
+                                            case 'editor':
+                                                _e('编辑');
+                                                break;
+                                            case 'contributor':
+                                                _e('贡献者');
+                                                break;
+                                            case 'subscriber':
+                                                _e('关注者');
+                                                break;
+                                            case 'visitor':
+                                                _e('访问者');
+                                                break;
+                                            default:
+                                                break;
+                                        } ?></td>
+                                </tr>
+                            <?php endwhile; ?>
+                            </tbody>
+                        </table><!-- end .typecho-list-table -->
+                    </div><!-- end .typecho-table-wrap -->
+                </form><!-- end .operate-form -->
+
+                <div class="typecho-list-operate clearfix">
+                    <form method="get">
+                        <div class="operate">
+                            <label><i class="sr-only"><?php _e('全选'); ?></i><input type="checkbox"
+                                                                                   class="typecho-table-select-all"/></label>
+                            <div class="btn-group btn-drop">
+                                <button class="btn dropdown-toggle btn-s" type="button"><i
+                                        class="sr-only"><?php _e('操作'); ?></i><?php _e('选中项'); ?> <i
+                                        class="i-caret-down"></i></button>
+                                <ul class="dropdown-menu">
+                                    <li><a lang="<?php _e('你确认要删除这些用户吗?'); ?>"
+                                           href="<?php $security->index('/action/users-edit?do=delete'); ?>"><?php _e('删除'); ?></a>
+                                    </li>
+                                </ul>
+                            </div>
+                        </div>
+                        <?php if ($users->have()): ?>
+                            <ul class="typecho-pager">
+                                <?php $users->pageNav(); ?>
+                            </ul>
+                        <?php endif; ?>
+                    </form>
+                </div><!-- end .typecho-list-operate -->
+            </div><!-- end .typecho-list -->
+        </div><!-- end .typecho-page-main -->
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'table-js.php';
+include 'footer.php';
+?>

+ 200 - 0
admin/media.php

@@ -0,0 +1,200 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+$phpMaxFilesize = function_exists('ini_get') ? trim(ini_get('upload_max_filesize')) : 0;
+
+if (preg_match("/^([0-9]+)([a-z]{1,2})$/i", $phpMaxFilesize, $matches)) {
+    $phpMaxFilesize = strtolower($matches[1] . $matches[2] . (1 == strlen($matches[2]) ? 'b' : ''));
+}
+
+$attachment = \Widget\Contents\Attachment\Edit::alloc();
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main">
+            <div class="col-mb-12 col-tb-8" role="main">
+                <?php if ($attachment->attachment->isImage): ?>
+                    <p><img src="<?php $attachment->attachment->url(); ?>"
+                            alt="<?php $attachment->attachment->name(); ?>" class="typecho-attachment-photo"/></p>
+                <?php endif; ?>
+
+                <p>
+                    <?php $mime = \Typecho\Common::mimeIconType($attachment->attachment->mime); ?>
+                    <i class="mime-<?php echo $mime; ?>"></i>
+                    <a href=""><strong><?php $attachment->attachment->name(); ?></strong></a>
+                    <span><?php echo number_format(ceil($attachment->attachment->size / 1024)); ?> Kb</span>
+                </p>
+
+                <p>
+                    <input id="attachment-url" type="text" class="mono w-100"
+                           value="<?php $attachment->attachment->url(); ?>" readonly/>
+                </p>
+
+                <div id="upload-panel" class="p">
+                    <div class="upload-area"
+                         draggable="true"><?php _e('拖放文件到这里<br>或者 %s选择文件上传%s', '<a href="###" class="upload-file">', '</a>'); ?></div>
+                    <ul id="file-list"></ul>
+                </div>
+            </div>
+            <div class="col-mb-12 col-tb-4 edit-media" role="form">
+                <?php $attachment->form()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+?>
+<script src="<?php $options->adminStaticUrl('js', 'moxie.js'); ?>"></script>
+<script src="<?php $options->adminStaticUrl('js', 'plupload.js'); ?>"></script>
+<script type="text/javascript">
+    $(document).ready(function () {
+        $('#attachment-url').click(function () {
+            $(this).select();
+        });
+
+        $('.operate-delete').click(function () {
+            var t = $(this), href = t.attr('href');
+
+            if (confirm(t.attr('lang'))) {
+                window.location.href = href;
+            }
+
+            return false;
+        });
+
+        $('.upload-area').bind({
+            dragenter: function () {
+                $(this).parent().addClass('drag');
+            },
+
+            dragover: function (e) {
+                $(this).parent().addClass('drag');
+            },
+
+            drop: function () {
+                $(this).parent().removeClass('drag');
+            },
+
+            dragend: function () {
+                $(this).parent().removeClass('drag');
+            },
+
+            dragleave: function () {
+                $(this).parent().removeClass('drag');
+            }
+        });
+
+        function fileUploadStart(file) {
+            $('<ul id="file-list"></ul>').appendTo('#upload-panel');
+            $('<li id="' + file.id + '" class="loading">'
+                + file.name + '</li>').prependTo('#file-list');
+        }
+
+        function fileUploadError(error) {
+            var file = error.file, code = error.code, word;
+
+            switch (code) {
+                case plupload.FILE_SIZE_ERROR:
+                    word = '<?php _e('文件大小超过限制'); ?>';
+                    break;
+                case plupload.FILE_EXTENSION_ERROR:
+                    word = '<?php _e('文件扩展名不被支持'); ?>';
+                    break;
+                case plupload.FILE_DUPLICATE_ERROR:
+                    word = '<?php _e('文件已经上传过'); ?>';
+                    break;
+                case plupload.HTTP_ERROR:
+                default:
+                    word = '<?php _e('上传出现错误'); ?>';
+                    break;
+            }
+
+            var fileError = '<?php _e('%s 上传失败'); ?>'.replace('%s', file.name),
+                li, exist = $('#' + file.id);
+
+            if (exist.length > 0) {
+                li = exist.removeClass('loading').html(fileError);
+            } else {
+                $('<ul id="file-list"></ul>').appendTo('#upload-panel');
+                li = $('<li>' + fileError + '<br />' + word + '</li>').prependTo('#file-list');
+            }
+
+            li.effect('highlight', {color: '#FBC2C4'}, 2000, function () {
+                $(this).remove();
+            });
+        }
+
+        function fileUploadComplete(id, url, data) {
+            var img = $('.typecho-attachment-photo');
+
+            if (img.length > 0) {
+                img.get(0).src = '<?php $attachment->attachment->url(); ?>?' + Math.random();
+            }
+
+            $('#' + id).text('<?php _e('文件 %s 已经替换'); ?>'.replace('%s', data.title))
+                .effect('highlight', 1000, function () {
+                    $(this).remove();
+                    $('#file-list').remove();
+                });
+        }
+
+        var uploader = new plupload.Uploader({
+            browse_button: $('.upload-file').get(0),
+            url: '<?php $security->index('/action/upload?do=modify&cid=' . $attachment->cid); ?>',
+            runtimes: 'html5,flash,html4',
+            flash_swf_url: '<?php $options->adminStaticUrl('js', 'Moxie.swf'); ?>',
+            drop_element: $('.upload-area').get(0),
+            filters: {
+                max_file_size: '<?php echo $phpMaxFilesize ?>',
+                mime_types: [{
+                    'title': '<?php _e('允许上传的文件'); ?>',
+                    'extensions': '<?php $attachment->attachment->type(); ?>'
+                }],
+                prevent_duplicates: true
+            },
+            multi_selection: false,
+
+            init: {
+                FilesAdded: function (up, files) {
+                    plupload.each(files, function (file) {
+                        fileUploadStart(file);
+                    });
+
+                    uploader.start();
+                },
+
+                FileUploaded: function (up, file, result) {
+                    if (200 == result.status) {
+                        var data = $.parseJSON(result.response);
+
+                        if (data) {
+                            fileUploadComplete(file.id, data[0], data[1]);
+                            return;
+                        }
+                    }
+
+                    fileUploadError({
+                        code: plupload.HTTP_ERROR,
+                        file: file
+                    });
+                },
+
+                Error: function (up, error) {
+                    fileUploadError(error);
+                }
+            }
+        });
+
+        uploader.init();
+    });
+</script>
+<?php
+include 'footer.php';
+?>

+ 18 - 0
admin/menu.php

@@ -0,0 +1,18 @@
+<?php if (!defined('__TYPECHO_ADMIN__')) exit; ?>
+<div class="typecho-head-nav clearfix" role="navigation">
+    <button class="menu-bar"><?php _e('菜单'); ?></button>
+    <nav id="typecho-nav-list">
+        <?php $menu->output(); ?>
+    </nav>
+    <div class="operate">
+        <?php \Typecho\Plugin::factory('admin/menu.php')->navBar(); ?><a title="<?php
+        if ($user->logged > 0) {
+            $logged = new \Typecho\Date($user->logged);
+            _e('最后登录: %s', $logged->word());
+        }
+        ?>" href="<?php $options->adminUrl('profile.php'); ?>" class="author"><?php $user->screenName(); ?></a><a
+            class="exit" href="<?php $options->logoutUrl(); ?>"><?php _e('登出'); ?></a><a
+            href="<?php $options->siteUrl(); ?>"><?php _e('网站'); ?></a>
+    </div>
+</div>
+

+ 23 - 0
admin/options-discussion.php

@@ -0,0 +1,23 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="form">
+            <div class="col-mb-12 col-tb-8 col-tb-offset-2">
+                <?php \Widget\Options\Discussion::alloc()->form()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+include 'footer.php';
+?>

+ 23 - 0
admin/options-general.php

@@ -0,0 +1,23 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="form">
+            <div class="col-mb-12 col-tb-8 col-tb-offset-2">
+                <?php \Widget\Options\General::alloc()->form()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+include 'footer.php';
+?>

+ 24 - 0
admin/options-permalink.php

@@ -0,0 +1,24 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="form">
+            <div class="col-mb-12 col-tb-8 col-tb-offset-2">
+                <?php \Widget\Options\Permalink::alloc()->form()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+?>
+
+<?php include 'footer.php'; ?>

+ 23 - 0
admin/options-plugin.php

@@ -0,0 +1,23 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="form">
+            <div class="col-mb-12 col-tb-8 col-tb-offset-2">
+                <?php \Widget\Plugins\Config::alloc()->config()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+include 'footer.php';
+?>

+ 37 - 0
admin/options-reading.php

@@ -0,0 +1,37 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="form">
+            <div class="col-mb-12 col-tb-8 col-tb-offset-2">
+                <?php \Widget\Options\Reading::alloc()->form()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+?>
+<script>
+$('#frontPage-recent,#frontPage-page,#frontPage-file').change(function () {
+    var t = $(this);
+    if (t.prop('checked')) {
+        if ('frontPage-recent' == t.attr('id')) {
+            $('.front-archive').addClass('hidden');
+        } else {
+            $('.front-archive').insertAfter(t.parent()).removeClass('hidden');
+        }
+    }
+});
+</script>
+<?php
+include 'footer.php';
+?>

+ 33 - 0
admin/options-theme.php

@@ -0,0 +1,33 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12">
+                <ul class="typecho-option-tabs fix-tabs clearfix">
+                    <li><a href="<?php $options->adminUrl('themes.php'); ?>"><?php _e('可以使用的外观'); ?></a></li>
+                    <?php if (!defined('__TYPECHO_THEME_WRITEABLE__') || __TYPECHO_THEME_WRITEABLE__): ?>
+                        <li><a href="<?php $options->adminUrl('theme-editor.php'); ?>"><?php _e('编辑当前外观'); ?></a></li>
+                    <?php endif; ?>
+                    <li class="current"><a
+                            href="<?php $options->adminUrl('options-theme.php'); ?>"><?php _e('设置外观'); ?></a></li>
+                </ul>
+            </div>
+            <div class="col-mb-12 col-tb-8 col-tb-offset-2" role="form">
+                <?php \Widget\Themes\Config::alloc()->config()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+include 'footer.php';
+?>

+ 8 - 0
admin/page-title.php

@@ -0,0 +1,8 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<div class="typecho-page-title">
+    <h2><?php echo $menu->title; ?><?php 
+    if (!empty($menu->addLink)) {
+        echo "<a href=\"{$menu->addLink}\">" . _t("新增") . "</a>";
+    }
+    ?></h2>
+</div>

+ 133 - 0
admin/plugins.php

@@ -0,0 +1,133 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12 typecho-list">
+                <?php \Widget\Plugins\Rows::allocWithAlias('activated', 'activated=1')->to($activatedPlugins); ?>
+                <?php if ($activatedPlugins->have() || !empty($activatedPlugins->activatedPlugins)): ?>
+                    <h4 class="typecho-list-table-title"><?php _e('启用的插件'); ?></h4>
+                    <div class="typecho-table-wrap">
+                        <table class="typecho-list-table">
+                            <colgroup>
+                                <col width="25%"/>
+                                <col width="45%"/>
+                                <col width="8%" class="kit-hidden-mb"/>
+                                <col width="10%" class="kit-hidden-mb"/>
+                                <col width=""/>
+                            </colgroup>
+                            <thead>
+                            <tr>
+                                <th><?php _e('名称'); ?></th>
+                                <th><?php _e('描述'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('版本'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('作者'); ?></th>
+                                <th><?php _e('操作'); ?></th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <?php while ($activatedPlugins->next()): ?>
+                                <tr id="plugin-<?php $activatedPlugins->name(); ?>">
+                                    <td><?php $activatedPlugins->title(); ?>
+                                        <?php if (!$activatedPlugins->dependence): ?>
+                                            <i class="i-delete"
+                                               title="<?php _e('%s 无法在此版本的typecho下正常工作', $activatedPlugins->title); ?>"></i>
+                                        <?php endif; ?>
+                                    </td>
+                                    <td><?php $activatedPlugins->description(); ?></td>
+                                    <td class="kit-hidden-mb"><?php $activatedPlugins->version(); ?></td>
+                                    <td class="kit-hidden-mb"><?php echo empty($activatedPlugins->homepage) ? $activatedPlugins->author : '<a href="' . $activatedPlugins->homepage
+                                            . '">' . $activatedPlugins->author . '</a>'; ?></td>
+                                    <td>
+                                        <?php if ($activatedPlugins->activate || $activatedPlugins->deactivate || $activatedPlugins->config || $activatedPlugins->personalConfig): ?>
+                                            <?php if ($activatedPlugins->config): ?>
+                                                <a href="<?php $options->adminUrl('options-plugin.php?config=' . $activatedPlugins->name); ?>"><?php _e('设置'); ?></a>
+                                                &bull;
+                                            <?php endif; ?>
+                                            <a lang="<?php _e('你确认要禁用插件 %s 吗?', $activatedPlugins->name); ?>"
+                                               href="<?php $security->index('/action/plugins-edit?deactivate=' . $activatedPlugins->name); ?>"><?php _e('禁用'); ?></a>
+                                        <?php else: ?>
+                                            <span class="important"><?php _e('即插即用'); ?></span>
+                                        <?php endif; ?>
+                                    </td>
+                                </tr>
+                            <?php endwhile; ?>
+
+                            <?php if (!empty($activatedPlugins->activatedPlugins)): ?>
+                                <?php foreach ($activatedPlugins->activatedPlugins as $key => $val): ?>
+                                    <tr>
+                                        <td><?php echo $key; ?></td>
+                                        <td colspan="3"><span
+                                                class="warning"><?php _e('此插件文件已经损坏或者被不安全移除, 强烈建议你禁用它'); ?></span></td>
+                                        <td><a lang="<?php _e('你确认要禁用插件 %s 吗?', $key); ?>"
+                                               href="<?php $security->index('/action/plugins-edit?deactivate=' . $key); ?>"><?php _e('禁用'); ?></a>
+                                        </td>
+                                    </tr>
+                                <?php endforeach; ?>
+                            <?php endif; ?>
+
+                            </tbody>
+                        </table>
+                    </div>
+                <?php endif; ?>
+
+                <?php \Widget\Plugins\Rows::allocWithAlias('unactivated', 'activated=0')->to($deactivatedPlugins); ?>
+                <?php if ($deactivatedPlugins->have() || !$activatedPlugins->have()): ?>
+                    <h4 class="typecho-list-table-title"><?php _e('禁用的插件'); ?></h4>
+                    <div class="typecho-table-wrap">
+                        <table class="typecho-list-table deactivate">
+                            <colgroup>
+                                <col width="25%"/>
+                                <col width="45%"/>
+                                <col width="8%" class="kit-hidden-mb"/>
+                                <col width="10%" class="kit-hidden-mb"/>
+                                <col width=""/>
+                            </colgroup>
+                            <thead>
+                            <tr>
+                                <th><?php _e('名称'); ?></th>
+                                <th><?php _e('描述'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('版本'); ?></th>
+                                <th class="kit-hidden-mb"><?php _e('作者'); ?></th>
+                                <th class="typecho-radius-topright"><?php _e('操作'); ?></th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <?php if ($deactivatedPlugins->have()): ?>
+                                <?php while ($deactivatedPlugins->next()): ?>
+                                    <tr id="plugin-<?php $deactivatedPlugins->name(); ?>">
+                                        <td><?php $deactivatedPlugins->title(); ?></td>
+                                        <td><?php $deactivatedPlugins->description(); ?></td>
+                                        <td class="kit-hidden-mb"><?php $deactivatedPlugins->version(); ?></td>
+                                        <td class="kit-hidden-mb"><?php echo empty($deactivatedPlugins->homepage) ? $deactivatedPlugins->author : '<a href="' . $deactivatedPlugins->homepage
+                                                . '">' . $deactivatedPlugins->author . '</a>'; ?></td>
+                                        <td>
+                                            <a href="<?php $security->index('/action/plugins-edit?activate=' . $deactivatedPlugins->name); ?>"><?php _e('启用'); ?></a>
+                                        </td>
+                                    </tr>
+                                <?php endwhile; ?>
+                            <?php else: ?>
+                                <tr>
+                                    <td colspan="5"><h6 class="typecho-list-table-title"><?php _e('没有安装插件'); ?></h6>
+                                    </td>
+                                </tr>
+                            <?php endif; ?>
+                            </tbody>
+                        </table>
+                    </div>
+                <?php endif; ?>
+
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'footer.php';
+?>

+ 27 - 0
admin/preview.php

@@ -0,0 +1,27 @@
+<?php
+
+include 'common.php';
+
+/** 获取内容 Widget */
+\Widget\Archive::alloc('type=single&checkPermalink=0&preview=1')->to($content);
+
+/** 检测是否存在 */
+if (!$content->have()) {
+    $response->redirect($options->adminUrl);
+}
+
+/** 检测权限 */
+if (!$user->pass('editor', true) && $content->authorId != $user->uid) {
+    $response->redirect($options->adminUrl);
+}
+
+/** 输出内容 */
+$content->render();
+?>
+<script>
+    window.onbeforeunload = function () {
+        if (!!window.parent) {
+            window.parent.postMessage('cancelPreview', '<?php $options->rootUrl(); ?>');
+        }
+    }
+</script>

+ 62 - 0
admin/profile.php

@@ -0,0 +1,62 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+$stat = \Widget\Stat::alloc();
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main">
+            <div class="col-mb-12 col-tb-3">
+                <p><a href="https://gravatar.com/emails/"
+                      title="<?php _e('在 Gravatar 上修改头像'); ?>"><?php echo '<img class="profile-avatar" src="' . \Typecho\Common::gravatarUrl($user->mail, 220, 'X', 'mm', $request->isSecure()) . '" alt="' . $user->screenName . '" />'; ?></a>
+                </p>
+                <h2><?php $user->screenName(); ?></h2>
+                <p><?php $user->name(); ?></p>
+                <p><?php _e('目前有 <em>%s</em> 篇日志, 并有 <em>%s</em> 条关于你的评论在 <em>%s</em> 个分类中.',
+                        $stat->myPublishedPostsNum, $stat->myPublishedCommentsNum, $stat->categoriesNum); ?></p>
+                <p><?php
+                    if ($user->logged > 0) {
+                        $logged = new \Typecho\Date($user->logged);
+                        _e('最后登录: %s', $logged->word());
+                    }
+                    ?></p>
+            </div>
+
+            <div class="col-mb-12 col-tb-6 col-tb-offset-1 typecho-content-panel" role="form">
+                <section>
+                    <h3><?php _e('个人资料'); ?></h3>
+                    <?php \Widget\Users\Profile::alloc()->profileForm()->render(); ?>
+                </section>
+
+                <?php if ($user->pass('contributor', true)): ?>
+                    <br>
+                    <section id="writing-option">
+                        <h3><?php _e('撰写设置'); ?></h3>
+                        <?php \Widget\Users\Profile::alloc()->optionsForm()->render(); ?>
+                    </section>
+                <?php endif; ?>
+
+                <br>
+
+                <section id="change-password">
+                    <h3><?php _e('密码修改'); ?></h3>
+                    <?php \Widget\Users\Profile::alloc()->passwordForm()->render(); ?>
+                </section>
+
+                <?php \Widget\Users\Profile::alloc()->personalFormList(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+\Typecho\Plugin::factory('admin/profile.php')->bottom();
+include 'footer.php';
+?>

+ 50 - 0
admin/register.php

@@ -0,0 +1,50 @@
+<?php
+include 'common.php';
+
+if ($user->hasLogin() || !$options->allowRegister) {
+    $response->redirect($options->siteUrl);
+}
+$rememberName = htmlspecialchars(\Typecho\Cookie::get('__typecho_remember_name'));
+$rememberMail = htmlspecialchars(\Typecho\Cookie::get('__typecho_remember_mail'));
+\Typecho\Cookie::delete('__typecho_remember_name');
+\Typecho\Cookie::delete('__typecho_remember_mail');
+
+$bodyClass = 'body-100';
+
+include 'header.php';
+?>
+<div class="typecho-login-wrap">
+    <div class="typecho-login">
+        <h1><a href="https://typecho.org" class="i-logo">Typecho</a></h1>
+        <form action="<?php $options->registerAction(); ?>" method="post" name="register" role="form">
+            <p>
+                <label for="name" class="sr-only"><?php _e('用户名'); ?></label>
+                <input type="text" id="name" name="name" placeholder="<?php _e('用户名'); ?>" value="<?php echo $rememberName; ?>" class="text-l w-100" autofocus />
+            </p>
+            <p>
+                <label for="mail" class="sr-only"><?php _e('Email'); ?></label>
+                <input type="email" id="mail" name="mail" placeholder="<?php _e('Email'); ?>" value="<?php echo $rememberMail; ?>" class="text-l w-100" />
+            </p>
+            <p class="submit">
+                <button type="submit" class="btn btn-l w-100 primary"><?php _e('注册'); ?></button>
+            </p>
+        </form>
+        
+        <p class="more-link">
+            <a href="<?php $options->siteUrl(); ?>"><?php _e('返回首页'); ?></a>
+            &bull;
+            <a href="<?php $options->adminUrl('login.php'); ?>"><?php _e('用户登录'); ?></a>
+        </p>
+    </div>
+</div>
+<?php 
+include 'common-js.php';
+?>
+<script>
+$(document).ready(function () {
+    $('#name').focus();
+});
+</script>
+<?php
+include 'footer.php';
+?>

+ 19 - 0
admin/table-js.php

@@ -0,0 +1,19 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<script src="<?php $options->adminStaticUrl('js', 'purify.js'); ?>"></script>
+<script>
+(function () {
+    $(document).ready(function () {
+        $('.typecho-list-table').tableSelectable({
+            checkEl     :   'input[type=checkbox]',
+            rowEl       :   'tr',
+            selectAllEl :   '.typecho-table-select-all',
+            actionEl    :   '.dropdown-menu a,button.btn-operate'
+        });
+
+        $('.btn-drop').dropdownMenu({
+            btnEl       :   '.dropdown-toggle',
+            menuEl      :   '.dropdown-menu'
+        });
+    });
+})();
+</script>

+ 65 - 0
admin/theme-editor.php

@@ -0,0 +1,65 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+
+\Widget\Themes\Files::alloc()->to($files);
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12">
+                <ul class="typecho-option-tabs fix-tabs clearfix">
+                    <li><a href="<?php $options->adminUrl('themes.php'); ?>"><?php _e('可以使用的外观'); ?></a></li>
+                    <li class="current"><a href="<?php $options->adminUrl('theme-editor.php'); ?>">
+                            <?php if ($options->theme == $files->theme): ?>
+                                <?php _e('编辑当前外观'); ?>
+                            <?php else: ?>
+                                <?php _e('编辑%s外观', ' <cite>' . $files->theme . '</cite> '); ?>
+                            <?php endif; ?>
+                        </a></li>
+                    <?php if (\Widget\Themes\Config::isExists()): ?>
+                        <li><a href="<?php $options->adminUrl('options-theme.php'); ?>"><?php _e('设置外观'); ?></a></li>
+                    <?php endif; ?>
+                </ul>
+            </div>
+
+            <div class="typecho-edit-theme">
+                <div class="col-mb-12 col-tb-8 col-9 content">
+                    <form method="post" name="theme" id="theme"
+                          action="<?php $security->index('/action/themes-edit'); ?>">
+                        <label for="content" class="sr-only"><?php _e('编辑源码'); ?></label>
+                        <textarea name="content" id="content" class="w-100 mono"
+                                  <?php if (!$files->currentIsWriteable()): ?>readonly<?php endif; ?>><?php echo $files->currentContent(); ?></textarea>
+                        <p class="typecho-option typecho-option-submit">
+                            <?php if ($files->currentIsWriteable()): ?>
+                                <input type="hidden" name="theme" value="<?php echo $files->currentTheme(); ?>"/>
+                                <input type="hidden" name="edit" value="<?php echo $files->currentFile(); ?>"/>
+                                <button type="submit" class="btn primary"><?php _e('保存文件'); ?></button>
+                            <?php else: ?>
+                                <em><?php _e('此文件无法写入'); ?></em>
+                            <?php endif; ?>
+                        </p>
+                    </form>
+                </div>
+                <ul class="col-mb-12 col-tb-4 col-3">
+                    <li><strong>模板文件</strong></li>
+                    <?php while ($files->next()): ?>
+                        <li<?php if ($files->current): ?> class="current"<?php endif; ?>>
+                            <a href="<?php $options->adminUrl('theme-editor.php?theme=' . $files->currentTheme() . '&file=' . $files->file); ?>"><?php $files->file(); ?></a>
+                        </li>
+                    <?php endwhile; ?>
+                </ul>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+\Typecho\Plugin::factory('admin/theme-editor.php')->bottom($files);
+include 'footer.php';
+?>

+ 85 - 0
admin/themes.php

@@ -0,0 +1,85 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12">
+                <ul class="typecho-option-tabs fix-tabs clearfix">
+                    <li class="current"><a href="<?php $options->adminUrl('themes.php'); ?>"><?php _e('可以使用的外观'); ?></a>
+                    </li>
+                    <?php if (\Widget\Themes\Files::isWriteable()): ?>
+                        <li><a href="<?php $options->adminUrl('theme-editor.php'); ?>"><?php _e('编辑当前外观'); ?></a></li>
+                    <?php endif; ?>
+                    <?php if (\Widget\Themes\Config::isExists()): ?>
+                        <li><a href="<?php $options->adminUrl('options-theme.php'); ?>"><?php _e('设置外观'); ?></a></li>
+                    <?php endif; ?>
+                </ul>
+
+                <div class="typecho-table-wrap">
+                    <table class="typecho-list-table typecho-theme-list">
+                        <colgroup>
+                            <col width="35%"/>
+                            <col/>
+                        </colgroup>
+
+                        <thead>
+                        <th><?php _e('截图'); ?></th>
+                        <th><?php _e('详情'); ?></th>
+                        </thead>
+
+                        <tbody>
+                        <?php if ($options->missingTheme): ?>
+                            <tr id="theme-<?php $options->missingTheme; ?>" class="current">
+                                <td colspan="2" class="warning">
+                                    <p><strong><?php _e('检测到您之前使用的 "%s" 外观文件不存在,您可以重新上传此外观或者启用其他外观。', $options->missingTheme); ?></strong></p>
+                                    <ul>
+                                        <li><?php _e('重新上传此外观后刷新当前页面,此提示将会消失。'); ?></li>
+                                        <li><?php _e('启用新外观后,当前外观的设置数据将被删除。'); ?></li>
+                                    </ul>
+                                </td>
+                            </tr>
+                        <?php endif; ?>
+                        <?php \Widget\Themes\Rows::alloc()->to($themes); ?>
+                        <?php while ($themes->next()): ?>
+                            <tr id="theme-<?php $themes->name(); ?>"
+                                class="<?php if ($themes->activated && !$options->missingTheme): ?>current<?php endif; ?>">
+                                <td valign="top"><img src="<?php $themes->screen(); ?>"
+                                                      alt="<?php $themes->name(); ?>"/></td>
+                                <td valign="top">
+                                    <h3><?php '' != $themes->title ? $themes->title() : $themes->name(); ?></h3>
+                                    <cite>
+                                        <?php if ($themes->author): ?><?php _e('作者'); ?>: <?php if ($themes->homepage): ?><a href="<?php $themes->homepage() ?>"><?php endif; ?><?php $themes->author(); ?><?php if ($themes->homepage): ?></a><?php endif; ?> &nbsp;&nbsp;<?php endif; ?>
+                                        <?php if ($themes->version): ?><?php _e('版本'); ?>: <?php $themes->version() ?><?php endif; ?>
+                                    </cite>
+                                    <p><?php echo nl2br($themes->description); ?></p>
+                                    <?php if ($options->theme != $themes->name || $options->missingTheme): ?>
+                                        <p>
+                                            <?php if (\Widget\Themes\Files::isWriteable()): ?>
+                                                <a class="edit"
+                                                   href="<?php $options->adminUrl('theme-editor.php?theme=' . $themes->name); ?>"><?php _e('编辑'); ?></a> &nbsp;
+                                            <?php endif; ?>
+                                            <a class="activate"
+                                               href="<?php $security->index('/action/themes-edit?change=' . $themes->name); ?>"><?php _e('启用'); ?></a>
+                                        </p>
+                                    <?php endif; ?>
+                                </td>
+                            </tr>
+                        <?php endwhile; ?>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'footer.php';
+?>

+ 45 - 0
admin/upgrade.php

@@ -0,0 +1,45 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12">
+                <div id="typecho-welcome">
+                    <form action="<?php echo $security->getTokenUrl(
+                        \Typecho\Router::url('do', ['action' => 'upgrade', 'widget' => 'Upgrade'],
+                            \Typecho\Common::url('index.php', $options->rootUrl))); ?>" method="post">
+                        <h3><?php _e('检测到新版本!'); ?></h3>
+                        <ul>
+                            <li><?php _e('您已经更新了系统程序, 我们还需要执行一些后续步骤来完成升级'); ?></li>
+                            <li><?php _e('此程序将把您的系统从 <strong>%s</strong> 升级到 <strong>%s</strong>', $options->version, \Typecho\Common::VERSION); ?></li>
+                            <li><strong
+                                    class="warning"><?php _e('在升级之前强烈建议先<a href="%s">备份您的数据</a>', \Typecho\Common::url('backup.php', $options->adminUrl)); ?></strong>
+                            </li>
+                        </ul>
+                        <p>
+                            <button class="btn primary" type="submit"><?php _e('完成升级 &raquo;'); ?></button>
+                        </p>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+?>
+<script>
+    (function () {
+        if (window.sessionStorage) {
+            sessionStorage.removeItem('update');
+        }
+    })();
+</script>
+<?php include 'footer.php'; ?>

+ 23 - 0
admin/user.php

@@ -0,0 +1,23 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="form">
+            <div class="col-mb-12 col-tb-6 col-tb-offset-3">
+                <?php \Widget\Users\Edit::alloc()->form()->render(); ?>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+include 'footer.php';
+?>

+ 36 - 0
admin/welcome.php

@@ -0,0 +1,36 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+?>
+
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main" role="main">
+            <div class="col-mb-12">
+                <div id="typecho-welcome" class="message">
+                    <form action="<?php $options->adminUrl(); ?>" method="get">
+                    <h3><?php _e('欢迎您使用 "%s" 管理后台: ', $options->title); ?></h3>
+                    <ol>
+                        <li><a class="operate-delete" href="<?php $options->adminUrl('profile.php#change-password'); ?>"><?php _e('强烈建议更改你的默认密码'); ?></a></li>
+                        <?php if($user->pass('contributor', true)): ?>
+                        <li><a href="<?php $options->adminUrl('write-post.php'); ?>"><?php _e('撰写第一篇日志'); ?></a></li>
+                        <li><a href="<?php $options->siteUrl(); ?>"><?php $user->pass('administrator', true) ? _e('查看我的站点') : _e('查看网站'); ?></a></li>
+                        <?php else: ?>
+                        <li><a href="<?php $options->siteUrl(); ?>"><?php _e('查看网站'); ?></a></li>
+                        <?php endif; ?>
+                    </ol>
+                    <p><button type="submit" class="btn primary"><?php _e('让我直接开始使用吧 &raquo;'); ?></button></p>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'footer.php';
+?>

+ 386 - 0
admin/write-js.php

@@ -0,0 +1,386 @@
+<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
+<?php \Typecho\Plugin::factory('admin/write-js.php')->write(); ?>
+<?php \Widget\Metas\Tag\Cloud::alloc('sort=count&desc=1&limit=200')->to($tags); ?>
+
+<script src="<?php $options->adminStaticUrl('js', 'timepicker.js'); ?>"></script>
+<script src="<?php $options->adminStaticUrl('js', 'tokeninput.js'); ?>"></script>
+<script>
+$(document).ready(function() {
+    // 日期时间控件
+    $('#date').mask('9999-99-99 99:99').datetimepicker({
+        currentText     :   '<?php _e('现在'); ?>',
+        prevText        :   '<?php _e('上一月'); ?>',
+        nextText        :   '<?php _e('下一月'); ?>',
+        monthNames      :   ['<?php _e('一月'); ?>', '<?php _e('二月'); ?>', '<?php _e('三月'); ?>', '<?php _e('四月'); ?>',
+            '<?php _e('五月'); ?>', '<?php _e('六月'); ?>', '<?php _e('七月'); ?>', '<?php _e('八月'); ?>',
+            '<?php _e('九月'); ?>', '<?php _e('十月'); ?>', '<?php _e('十一月'); ?>', '<?php _e('十二月'); ?>'],
+        dayNames        :   ['<?php _e('星期日'); ?>', '<?php _e('星期一'); ?>', '<?php _e('星期二'); ?>',
+            '<?php _e('星期三'); ?>', '<?php _e('星期四'); ?>', '<?php _e('星期五'); ?>', '<?php _e('星期六'); ?>'],
+        dayNamesShort   :   ['<?php _e('周日'); ?>', '<?php _e('周一'); ?>', '<?php _e('周二'); ?>', '<?php _e('周三'); ?>',
+            '<?php _e('周四'); ?>', '<?php _e('周五'); ?>', '<?php _e('周六'); ?>'],
+        dayNamesMin     :   ['<?php _e('日'); ?>', '<?php _e('一'); ?>', '<?php _e('二'); ?>', '<?php _e('三'); ?>',
+            '<?php _e('四'); ?>', '<?php _e('五'); ?>', '<?php _e('六'); ?>'],
+        closeText       :   '<?php _e('完成'); ?>',
+        timeOnlyTitle   :   '<?php _e('选择时间'); ?>',
+        timeText        :   '<?php _e('时间'); ?>',
+        hourText        :   '<?php _e('时'); ?>',
+        amNames         :   ['<?php _e('上午'); ?>', 'A'],
+        pmNames         :   ['<?php _e('下午'); ?>', 'P'],
+        minuteText      :   '<?php _e('分'); ?>',
+        secondText      :   '<?php _e('秒'); ?>',
+
+        dateFormat      :   'yy-mm-dd',
+        timezone        :   <?php $options->timezone(); ?> / 60,
+        hour            :   (new Date()).getHours(),
+        minute          :   (new Date()).getMinutes()
+    });
+
+    // 聚焦
+    $('#title').select();
+
+    // text 自动拉伸
+    Typecho.editorResize('text', '<?php $security->index('/action/ajax?do=editorResize'); ?>');
+
+    // tag autocomplete 提示
+    var tags = $('#tags'), tagsPre = [];
+    
+    if (tags.length > 0) {
+        var items = tags.val().split(','), result = [];
+        for (var i = 0; i < items.length; i ++) {
+            var tag = items[i];
+
+            if (!tag) {
+                continue;
+            }
+
+            tagsPre.push({
+                id      :   tag,
+                tags    :   tag
+            });
+        }
+
+        tags.tokenInput(<?php 
+        $data = array();
+        while ($tags->next()) {
+            $data[] = array(
+                'id'    =>  $tags->name,
+                'tags'  =>  $tags->name
+            );
+        }
+        echo json_encode($data);
+        ?>, {
+            propertyToSearch:   'tags',
+            tokenValue      :   'tags',
+            searchDelay     :   0,
+            preventDuplicates   :   true,
+            animateDropdown :   false,
+            hintText        :   '<?php _e('请输入标签名'); ?>',
+            noResultsText   :   '<?php _e('此标签不存在, 按回车创建'); ?>',
+            prePopulate     :   tagsPre,
+
+            onResult        :   function (result, query, val) {
+                if (!query) {
+                    return result;
+                }
+
+                if (!result) {
+                    result = [];
+                }
+
+                if (!result[0] || result[0]['id'] != query) {
+                    result.unshift({
+                        id      :   val,
+                        tags    :   val
+                    });
+                }
+
+                return result.slice(0, 5);
+            }
+        });
+
+        // tag autocomplete 提示宽度设置
+        $('#token-input-tags').focus(function() {
+            var t = $('.token-input-dropdown'),
+                offset = t.outerWidth() - t.width();
+            t.width($('.token-input-list').outerWidth() - offset);
+        });
+    }
+
+    // 缩略名自适应宽度
+    var slug = $('#slug');
+
+    if (slug.length > 0) {
+        var wrap = $('<div />').css({
+            'position'  :   'relative',
+            'display'   :   'inline-block'
+        }),
+        justifySlug = $('<pre />').css({
+            'display'   :   'block',
+            'visibility':   'hidden',
+            'height'    :   slug.height(),
+            'padding'   :   '0 2px',
+            'margin'    :   0
+        }).insertAfter(slug.wrap(wrap).css({
+            'left'      :   0,
+            'top'       :   0,
+            'minWidth'  :   '5px',
+            'position'  :   'absolute',
+            'width'     :   '100%'
+        })), originalWidth = slug.width();
+
+        function justifySlugWidth() {
+            var val = slug.val();
+            justifySlug.text(val.length > 0 ? val : '     ');
+        }
+
+        slug.bind('input propertychange', justifySlugWidth);
+        justifySlugWidth();
+    }
+
+    // 原始的插入图片和文件
+    Typecho.insertFileToEditor = function (file, url, isImage) {
+        var textarea = $('#text'), sel = textarea.getSelection(),
+            html = isImage ? '<img src="' + url + '" alt="' + file + '" />'
+                : '<a href="' + url + '">' + file + '</a>',
+            offset = (sel ? sel.start : 0) + html.length;
+
+        textarea.replaceSelection(html);
+        textarea.setSelection(offset, offset);
+    };
+
+    var submitted = false, form = $('form[name=write_post],form[name=write_page]').submit(function () {
+        submitted = true;
+    }), formAction = form.attr('action'),
+        idInput = $('input[name=cid]'),
+        cid = idInput.val(),
+        draft = $('input[name=draft]'),
+        draftId = draft.length > 0 ? draft.val() : 0,
+        btnSave = $('#btn-save').removeAttr('name').removeAttr('value'),
+        btnSubmit = $('#btn-submit').removeAttr('name').removeAttr('value'),
+        btnPreview = $('#btn-preview'),
+        doAction = $('<input type="hidden" name="do" value="publish" />').appendTo(form),
+        locked = false,
+        changed = false,
+        autoSave = $('<span id="auto-save-message" class="left"></span>').prependTo('.submit'),
+        lastSaveTime = null;
+
+    $(':input', form).bind('input change', function (e) {
+        var tagName = $(this).prop('tagName');
+
+        if (tagName.match(/(input|textarea)/i) && e.type == 'change') {
+            return;
+        }
+
+        changed = true;
+    });
+
+    form.bind('field', function () {
+        changed = true;
+    });
+
+    // 发送保存请求
+    function saveData(cb) {
+        function callback(o) {
+            lastSaveTime = o.time;
+            cid = o.cid;
+            draftId = o.draftId;
+            idInput.val(cid);
+            autoSave.text('<?php _e('已保存'); ?>' + ' (' + o.time + ')').effect('highlight', 1000);
+            locked = false;
+
+            btnSave.removeAttr('disabled');
+            btnPreview.removeAttr('disabled');
+
+            if (!!cb) {
+                cb(o)
+            }
+        }
+
+        changed = false;
+        btnSave.attr('disabled', 'disabled');
+        btnPreview.attr('disabled', 'disabled');
+        autoSave.text('<?php _e('正在保存'); ?>');
+
+        if (typeof FormData !== 'undefined') {
+            var data = new FormData(form.get(0));
+            data.append('do', 'save');
+
+            $.ajax({
+                url: formAction,
+                processData: false,
+                contentType: false,
+                type: 'POST',
+                data: data,
+                success: callback
+            });
+        } else {
+            var data = form.serialize() + '&do=save';
+            $.post(formAction, data, callback, 'json');
+        }
+    }
+
+    // 计算夏令时偏移
+    var dstOffset = (function () {
+        var d = new Date(),
+            jan = new Date(d.getFullYear(), 0, 1),
+            jul = new Date(d.getFullYear(), 6, 1),
+            stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
+
+        return stdOffset - d.getTimezoneOffset();
+    })();
+    
+    if (dstOffset > 0) {
+        $('<input name="dst" type="hidden" />').appendTo(form).val(dstOffset);
+    }
+
+    // 时区
+    $('<input name="timezone" type="hidden" />').appendTo(form).val(- (new Date).getTimezoneOffset() * 60);
+
+    // 自动保存
+<?php if ($options->autoSave): ?>
+    var autoSaveOnce = !!cid;
+
+    function autoSaveListener () {
+        setInterval(function () {
+            if (changed && !locked) {
+                locked = true;
+                saveData();
+            }
+        }, 10000);
+    }
+
+    if (autoSaveOnce) {
+        autoSaveListener();
+    }
+
+    $('#text').bind('input propertychange', function () {
+        if (!locked) {
+            autoSave.text('<?php _e('尚未保存'); ?>' + (lastSaveTime ? ' (<?php _e('上次保存时间'); ?>: ' + lastSaveTime + ')' : ''));
+        }
+
+        if (!autoSaveOnce) {
+            autoSaveOnce = true;
+            autoSaveListener();
+        }
+    });
+<?php endif; ?>
+
+    // 自动检测离开页
+    $(window).bind('beforeunload', function () {
+        if (changed && !submitted) {
+            return '<?php _e('内容已经改变尚未保存, 您确认要离开此页面吗?'); ?>';
+        }
+    });
+
+    // 预览功能
+    var isFullScreen = false;
+
+    function previewData(cid) {
+        isFullScreen = $(document.body).hasClass('fullscreen');
+        $(document.body).addClass('fullscreen preview');
+
+        var frame = $('<iframe frameborder="0" class="preview-frame preview-loading"></iframe>')
+            .attr('src', './preview.php?cid=' + cid)
+            .attr('sandbox', 'allow-same-origin allow-scripts')
+            .appendTo(document.body);
+
+        frame.load(function () {
+            frame.removeClass('preview-loading');
+        });
+
+        frame.height($(window).height() - 53);
+    }
+
+    function cancelPreview() {
+        if (submitted) {
+            return;
+        }
+
+        if (!isFullScreen) {
+            $(document.body).removeClass('fullscreen');
+        }
+
+        $(document.body).removeClass('preview');
+        $('.preview-frame').remove();
+    };
+
+    $('#btn-cancel-preview').click(cancelPreview);
+
+    $(window).bind('message', function (e) {
+        if (e.originalEvent.data == 'cancelPreview') {
+            cancelPreview();
+        }
+    });
+
+    btnPreview.click(function () {
+        if (changed) {
+            locked = true;
+
+            if (confirm('<?php _e('修改后的内容需要保存后才能预览, 是否保存?'); ?>')) {
+                saveData(function (o) {
+                    previewData(o.draftId);
+                });
+            } else {
+                locked = false;
+            }
+        } else if (!!draftId) {
+            previewData(draftId);
+        } else if (!!cid) {
+            previewData(cid);
+        }
+    });
+
+    btnSave.click(function () {
+        doAction.attr('value', 'save');
+    });
+
+    btnSubmit.click(function () {
+        doAction.attr('value', 'publish');
+    });
+
+    // 控制选项和附件的切换
+    var fileUploadInit = false;
+    $('#edit-secondary .typecho-option-tabs li').click(function() {
+        $('#edit-secondary .typecho-option-tabs li').removeClass('active');
+        $(this).addClass('active');
+        $(this).parents('#edit-secondary').find('.tab-content').addClass('hidden');
+        
+        var selected_tab = $(this).find('a').attr('href'),
+            selected_el = $(selected_tab).removeClass('hidden');
+
+        if (!fileUploadInit) {
+            selected_el.trigger('init');
+            fileUploadInit = true;
+        }
+
+        return false;
+    });
+
+    // 高级选项控制
+    $('#advance-panel-btn').click(function() {
+        $('#advance-panel').toggle();
+        return false;
+    });
+
+    // 自动隐藏密码框
+    $('#visibility').change(function () {
+        var val = $(this).val(), password = $('#post-password');
+
+        if ('password' == val) {
+            password.removeClass('hidden');
+        } else {
+            password.addClass('hidden');
+        }
+    });
+    
+    // 草稿删除确认
+    $('.edit-draft-notice a').click(function () {
+        if (confirm('<?php _e('您确认要删除这份草稿吗?'); ?>')) {
+            window.location.href = $(this).attr('href');
+        }
+
+        return false;
+    });
+});
+</script>
+

+ 179 - 0
admin/write-page.php

@@ -0,0 +1,179 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+\Widget\Contents\Page\Edit::alloc()->to($page);
+?>
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main typecho-post-area" role="form">
+            <form action="<?php $security->index('/action/contents-page-edit'); ?>" method="post" name="write_page">
+                <div class="col-mb-12 col-tb-9" role="main">
+                    <?php if ($page->draft): ?>
+                        <?php if ($page->draft['cid'] != $page->cid): ?>
+                            <?php $pageModifyDate = new \Typecho\Date($page->draft['modified']); ?>
+                            <cite
+                                class="edit-draft-notice"><?php _e('当前正在编辑的是保存于%s的草稿, 你可以<a href="%s">删除它</a>', $pageModifyDate->word(),
+                                    $security->getIndex('/action/contents-page-edit?do=deleteDraft&cid=' . $page->cid)); ?></cite>
+                        <?php else: ?>
+                            <cite class="edit-draft-notice"><?php _e('当前正在编辑的是未发布的草稿'); ?></cite>
+                        <?php endif; ?>
+                        <input name="draft" type="hidden" value="<?php echo $page->draft['cid'] ?>"/>
+                    <?php endif; ?>
+
+                    <p class="title">
+                        <label for="title" class="sr-only"><?php _e('标题'); ?></label>
+                        <input type="text" id="title" name="title" autocomplete="off" value="<?php $page->title(); ?>"
+                               placeholder="<?php _e('标题'); ?>" class="w-100 text title"/>
+                    </p>
+                    <?php $permalink = \Typecho\Common::url($options->routingTable['page']['url'], $options->index);
+                    [$scheme, $permalink] = explode(':', $permalink, 2);
+                    $permalink = ltrim($permalink, '/');
+                    $permalink = preg_replace("/\[([_a-z0-9-]+)[^\]]*\]/i", "{\\1}", $permalink);
+                    if ($page->have()) {
+                        $permalink = str_replace('{cid}', $page->cid, $permalink);
+                    }
+                    $input = '<input type="text" id="slug" name="slug" autocomplete="off" value="' . htmlspecialchars($page->slug ?? '') . '" class="mono" />';
+                    ?>
+                    <p class="mono url-slug">
+                        <label for="slug" class="sr-only"><?php _e('网址缩略名'); ?></label>
+                        <?php echo preg_replace("/\{slug\}/i", $input, $permalink); ?>
+                    </p>
+                    <p>
+                        <label for="text" class="sr-only"><?php _e('页面内容'); ?></label>
+                        <textarea style="height: <?php $options->editorSize(); ?>px" autocomplete="off" id="text"
+                                  name="text" class="w-100 mono"><?php echo htmlspecialchars($page->text ?? ''); ?></textarea>
+                    </p>
+
+                    <?php include 'custom-fields.php'; ?>
+                    <p class="submit clearfix">
+                        <span class="left">
+                            <button type="button" id="btn-cancel-preview" class="btn"><i
+                                    class="i-caret-left"></i> <?php _e('取消预览'); ?></button>
+                        </span>
+                        <span class="right">
+                            <input type="hidden" name="cid" value="<?php $page->cid(); ?>"/>
+                            <button type="button" id="btn-preview" class="btn"><i
+                                    class="i-exlink"></i> <?php _e('预览页面'); ?></button>
+                            <button type="submit" name="do" value="save" id="btn-save"
+                                    class="btn"><?php _e('保存草稿'); ?></button>
+                            <button type="submit" name="do" value="publish" class="btn primary"
+                                    id="btn-submit"><?php _e('发布页面'); ?></button>
+                            <?php if ($options->markdown && (!$page->have() || $page->isMarkdown)): ?>
+                                <input type="hidden" name="markdown" value="1"/>
+                            <?php endif; ?>
+                        </span>
+                    </p>
+
+                    <?php \Typecho\Plugin::factory('admin/write-page.php')->content($page); ?>
+                </div>
+                <div id="edit-secondary" class="col-mb-12 col-tb-3" role="complementary">
+                    <ul class="typecho-option-tabs clearfix">
+                        <li class="active w-50"><a href="#tab-advance"><?php _e('选项'); ?></a></li>
+                        <li class="w-50"><a href="#tab-files" id="tab-files-btn"><?php _e('附件'); ?></a></li>
+                    </ul>
+
+                    <div id="tab-advance" class="tab-content">
+                        <section class="typecho-post-option" role="application">
+                            <label for="date" class="typecho-label"><?php _e('发布日期'); ?></label>
+                            <p><input class="typecho-date w-100" type="text" name="date" id="date" autocomplete="off"
+                                      value="<?php $page->have() && $page->created > 0 ? $page->date('Y-m-d H:i') : ''; ?>"/>
+                            </p>
+                        </section>
+
+                        <section class="typecho-post-option">
+                            <label for="order" class="typecho-label"><?php _e('页面顺序'); ?></label>
+                            <p><input type="text" id="order" name="order" value="<?php $page->order(); ?>"
+                                      class="w-100"/></p>
+                            <p class="description"><?php _e('为你的自定义页面设定一个序列值以后, 能够使得它们按此值从小到大排列'); ?></p>
+                        </section>
+
+                        <section class="typecho-post-option">
+                            <label for="template" class="typecho-label"><?php _e('自定义模板'); ?></label>
+                            <p>
+                                <select name="template" id="template">
+                                    <option value=""><?php _e('不选择'); ?></option>
+                                    <?php $templates = $page->getTemplates();
+                                    foreach ($templates as $template => $name): ?>
+                                        <option
+                                            value="<?php echo $template; ?>"<?php if ($template == $page->template): ?> selected="true"<?php endif; ?>><?php echo $name; ?></option>
+                                    <?php endforeach; ?>
+                                </select>
+                            </p>
+                            <p class="description"><?php _e('如果你为此页面选择了一个自定义模板, 系统将按照你选择的模板文件展现它'); ?></p>
+                        </section>
+
+                        <?php \Typecho\Plugin::factory('admin/write-page.php')->option($page); ?>
+
+                        <button type="button" id="advance-panel-btn" class="btn btn-xs"><?php _e('高级选项'); ?> <i
+                                class="i-caret-down"></i></button>
+                        <div id="advance-panel">
+                            <section class="typecho-post-option visibility-option">
+                                <label for="visibility" class="typecho-label"><?php _e('公开度'); ?></label>
+                                <p>
+                                    <select id="visibility" name="visibility">
+                                        <option
+                                            value="publish"<?php if ($page->status == 'publish' || !$page->status): ?> selected<?php endif; ?>><?php _e('公开'); ?></option>
+                                        <option
+                                            value="hidden"<?php if ($page->status == 'hidden'): ?> selected<?php endif; ?>><?php _e('隐藏'); ?></option>
+                                    </select>
+                                </p>
+                            </section>
+
+                            <section class="typecho-post-option allow-option">
+                                <label class="typecho-label"><?php _e('权限控制'); ?></label>
+                                <ul>
+                                    <li><input id="allowComment" name="allowComment" type="checkbox" value="1"
+                                               <?php if ($page->allow('comment')): ?>checked="true"<?php endif; ?> />
+                                        <label for="allowComment"><?php _e('允许评论'); ?></label></li>
+                                    <li><input id="allowPing" name="allowPing" type="checkbox" value="1"
+                                               <?php if ($page->allow('ping')): ?>checked="true"<?php endif; ?> />
+                                        <label for="allowPing"><?php _e('允许被引用'); ?></label></li>
+                                    <li><input id="allowFeed" name="allowFeed" type="checkbox" value="1"
+                                               <?php if ($page->allow('feed')): ?>checked="true"<?php endif; ?> />
+                                        <label for="allowFeed"><?php _e('允许在聚合中出现'); ?></label></li>
+                                </ul>
+                            </section>
+
+                            <?php \Typecho\Plugin::factory('admin/write-page.php')->advanceOption($page); ?>
+                        </div>
+                        <?php if ($page->have()): ?>
+                            <?php $modified = new \Typecho\Date($page->modified); ?>
+                            <section class="typecho-post-option">
+                                <p class="description">
+                                    <br>&mdash;<br>
+                                    <?php _e('本页面由 <a href="%s">%s</a> 创建',
+                                        \Typecho\Common::url('manage-pages.php?uid=' . $page->author->uid, $options->adminUrl), $page->author->screenName); ?>
+                                    <br>
+                                    <?php _e('最后更新于 %s', $modified->word()); ?>
+                                </p>
+                            </section>
+                        <?php endif; ?>
+                    </div><!-- end #tab-advance -->
+
+                    <div id="tab-files" class="tab-content hidden">
+                        <?php include 'file-upload.php'; ?>
+                    </div><!-- end #tab-files -->
+                </div>
+            </form>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+include 'write-js.php';
+
+\Typecho\Plugin::factory('admin/write-page.php')->trigger($plugged)->richEditor($page);
+if (!$plugged) {
+    include 'editor-js.php';
+}
+
+include 'file-upload-js.php';
+include 'custom-fields-js.php';
+\Typecho\Plugin::factory('admin/write-page.php')->bottom($page);
+include 'footer.php';
+?>

+ 216 - 0
admin/write-post.php

@@ -0,0 +1,216 @@
+<?php
+include 'common.php';
+include 'header.php';
+include 'menu.php';
+\Widget\Contents\Post\Edit::alloc()->to($post);
+?>
+<div class="main">
+    <div class="body container">
+        <?php include 'page-title.php'; ?>
+        <div class="row typecho-page-main typecho-post-area" role="form">
+            <form action="<?php $security->index('/action/contents-post-edit'); ?>" method="post" name="write_post">
+                <div class="col-mb-12 col-tb-9" role="main">
+                    <?php if ($post->draft): ?>
+                        <?php if ($post->draft['cid'] != $post->cid): ?>
+                            <?php $postModifyDate = new \Typecho\Date($post->draft['modified']); ?>
+                            <cite
+                                class="edit-draft-notice"><?php _e('你正在编辑的是保存于 %s 的草稿, 你也可以 <a href="%s">删除它</a>', $postModifyDate->word(),
+                                    $security->getIndex('/action/contents-post-edit?do=deleteDraft&cid=' . $post->cid)); ?></cite>
+                        <?php else: ?>
+                            <cite class="edit-draft-notice"><?php _e('当前正在编辑的是未发布的草稿'); ?></cite>
+                        <?php endif; ?>
+                        <input name="draft" type="hidden" value="<?php echo $post->draft['cid'] ?>"/>
+                    <?php endif; ?>
+
+                    <p class="title">
+                        <label for="title" class="sr-only"><?php _e('标题'); ?></label>
+                        <input type="text" id="title" name="title" autocomplete="off" value="<?php $post->title(); ?>"
+                               placeholder="<?php _e('标题'); ?>" class="w-100 text title"/>
+                    </p>
+                    <?php $permalink = \Typecho\Common::url($options->routingTable['post']['url'], $options->index);
+                    [$scheme, $permalink] = explode(':', $permalink, 2);
+                    $permalink = ltrim($permalink, '/');
+                    $permalink = preg_replace("/\[([_a-z0-9-]+)[^\]]*\]/i", "{\\1}", $permalink);
+                    if ($post->have()) {
+                        $permalink = str_replace([
+                            '{cid}', '{category}', '{year}', '{month}', '{day}'
+                        ], [
+                            $post->cid, $post->category, $post->year, $post->month, $post->day
+                        ], $permalink);
+                    }
+                    $input = '<input type="text" id="slug" name="slug" autocomplete="off" value="' . htmlspecialchars($post->slug ?? '') . '" class="mono" />';
+                    ?>
+                    <p class="mono url-slug">
+                        <label for="slug" class="sr-only"><?php _e('网址缩略名'); ?></label>
+                        <?php echo preg_replace("/\{slug\}/i", $input, $permalink); ?>
+                    </p>
+                    <p>
+                        <label for="text" class="sr-only"><?php _e('文章内容'); ?></label>
+                        <textarea style="height: <?php $options->editorSize(); ?>px" autocomplete="off" id="text"
+                                  name="text" class="w-100 mono"><?php echo htmlspecialchars($post->text ?? ''); ?></textarea>
+                    </p>
+
+                    <?php include 'custom-fields.php'; ?>
+
+                    <p class="submit clearfix">
+                        <span class="left">
+                            <button type="button" id="btn-cancel-preview" class="btn"><i
+                                    class="i-caret-left"></i> <?php _e('取消预览'); ?></button>
+                        </span>
+                        <span class="right">
+                            <input type="hidden" name="cid" value="<?php $post->cid(); ?>"/>
+                            <button type="button" id="btn-preview" class="btn"><i
+                                    class="i-exlink"></i> <?php _e('预览文章'); ?></button>
+                            <button type="submit" name="do" value="save" id="btn-save"
+                                    class="btn"><?php _e('保存草稿'); ?></button>
+                            <button type="submit" name="do" value="publish" class="btn primary"
+                                    id="btn-submit"><?php _e('发布文章'); ?></button>
+                            <?php if ($options->markdown && (!$post->have() || $post->isMarkdown)): ?>
+                                <input type="hidden" name="markdown" value="1"/>
+                            <?php endif; ?>
+                        </span>
+                    </p>
+
+                    <?php \Typecho\Plugin::factory('admin/write-post.php')->content($post); ?>
+                </div>
+
+                <div id="edit-secondary" class="col-mb-12 col-tb-3" role="complementary">
+                    <ul class="typecho-option-tabs clearfix">
+                        <li class="active w-50"><a href="#tab-advance"><?php _e('选项'); ?></a></li>
+                        <li class="w-50"><a href="#tab-files" id="tab-files-btn"><?php _e('附件'); ?></a></li>
+                    </ul>
+
+
+                    <div id="tab-advance" class="tab-content">
+                        <section class="typecho-post-option" role="application">
+                            <label for="date" class="typecho-label"><?php _e('发布日期'); ?></label>
+                            <p><input class="typecho-date w-100" type="text" name="date" id="date" autocomplete="off"
+                                      value="<?php $post->have() && $post->created > 0 ? $post->date('Y-m-d H:i') : ''; ?>"/>
+                            </p>
+                        </section>
+
+                        <section class="typecho-post-option category-option">
+                            <label class="typecho-label"><?php _e('分类'); ?></label>
+                            <?php \Widget\Metas\Category\Rows::alloc()->to($category); ?>
+                            <ul>
+                                <?php
+                                if ($post->have()) {
+                                    $categories = array_column($post->categories, 'mid');
+                                } else {
+                                    $categories = [];
+                                }
+                                ?>
+                                <?php while ($category->next()): ?>
+                                    <li><?php echo str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $category->levels); ?><input
+                                            type="checkbox" id="category-<?php $category->mid(); ?>"
+                                            value="<?php $category->mid(); ?>" name="category[]"
+                                            <?php if (in_array($category->mid, $categories)): ?>checked="true"<?php endif; ?>/>
+                                        <label
+                                            for="category-<?php $category->mid(); ?>"><?php $category->name(); ?></label>
+                                    </li>
+                                <?php endwhile; ?>
+                            </ul>
+                        </section>
+
+                        <section class="typecho-post-option">
+                            <label for="token-input-tags" class="typecho-label"><?php _e('标签'); ?></label>
+                            <p><input id="tags" name="tags" type="text" value="<?php $post->tags(',', false); ?>"
+                                      class="w-100 text"/></p>
+                        </section>
+
+                        <?php \Typecho\Plugin::factory('admin/write-post.php')->option($post); ?>
+
+                        <button type="button" id="advance-panel-btn" class="btn btn-xs"><?php _e('高级选项'); ?> <i
+                                class="i-caret-down"></i></button>
+                        <div id="advance-panel">
+                            <?php if ($user->pass('editor', true)): ?>
+                                <section class="typecho-post-option visibility-option">
+                                    <label for="visibility" class="typecho-label"><?php _e('公开度'); ?></label>
+                                    <p>
+                                        <select id="visibility" name="visibility">
+                                            <?php if ($user->pass('editor', true)): ?>
+                                                <option
+                                                    value="publish"<?php if (($post->status == 'publish' && !$post->password) || !$post->status): ?> selected<?php endif; ?>><?php _e('公开'); ?></option>
+                                                <option
+                                                    value="hidden"<?php if ($post->status == 'hidden'): ?> selected<?php endif; ?>><?php _e('隐藏'); ?></option>
+                                                <option
+                                                    value="password"<?php if (strlen($post->password ?? '') > 0): ?> selected<?php endif; ?>><?php _e('密码保护'); ?></option>
+                                                <option
+                                                    value="private"<?php if ($post->status == 'private'): ?> selected<?php endif; ?>><?php _e('私密'); ?></option>
+                                            <?php endif; ?>
+                                            <option
+                                                value="waiting"<?php if (!$user->pass('editor', true) || $post->status == 'waiting'): ?> selected<?php endif; ?>><?php _e('待审核'); ?></option>
+                                        </select>
+                                    </p>
+                                    <p id="post-password"<?php if (strlen($post->password ?? '') == 0): ?> class="hidden"<?php endif; ?>>
+                                        <label for="protect-pwd" class="sr-only">内容密码</label>
+                                        <input type="text" name="password" id="protect-pwd" class="text-s"
+                                               value="<?php $post->password(); ?>" size="16"
+                                               placeholder="<?php _e('内容密码'); ?>" autocomplete="off"/>
+                                    </p>
+                                </section>
+                            <?php endif; ?>
+
+                            <section class="typecho-post-option allow-option">
+                                <label class="typecho-label"><?php _e('权限控制'); ?></label>
+                                <ul>
+                                    <li><input id="allowComment" name="allowComment" type="checkbox" value="1"
+                                               <?php if ($post->allow('comment')): ?>checked="true"<?php endif; ?> />
+                                        <label for="allowComment"><?php _e('允许评论'); ?></label></li>
+                                    <li><input id="allowPing" name="allowPing" type="checkbox" value="1"
+                                               <?php if ($post->allow('ping')): ?>checked="true"<?php endif; ?> />
+                                        <label for="allowPing"><?php _e('允许被引用'); ?></label></li>
+                                    <li><input id="allowFeed" name="allowFeed" type="checkbox" value="1"
+                                               <?php if ($post->allow('feed')): ?>checked="true"<?php endif; ?> />
+                                        <label for="allowFeed"><?php _e('允许在聚合中出现'); ?></label></li>
+                                </ul>
+                            </section>
+
+                            <section class="typecho-post-option">
+                                <label for="trackback" class="typecho-label"><?php _e('引用通告'); ?></label>
+                                <p><textarea id="trackback" class="w-100 mono" name="trackback" rows="2"></textarea></p>
+                                <p class="description"><?php _e('每一行一个引用地址, 用回车隔开'); ?></p>
+                            </section>
+
+                            <?php \Typecho\Plugin::factory('admin/write-post.php')->advanceOption($post); ?>
+                        </div><!-- end #advance-panel -->
+
+                        <?php if ($post->have()): ?>
+                            <?php $modified = new \Typecho\Date($post->modified); ?>
+                            <section class="typecho-post-option">
+                                <p class="description">
+                                    <br>&mdash;<br>
+                                    <?php _e('本文由 <a href="%s">%s</a> 撰写',
+                                        \Typecho\Common::url('manage-posts.php?uid=' . $post->author->uid, $options->adminUrl), $post->author->screenName); ?>
+                                    <br>
+                                    <?php _e('最后更新于 %s', $modified->word()); ?>
+                                </p>
+                            </section>
+                        <?php endif; ?>
+                    </div><!-- end #tab-advance -->
+
+                    <div id="tab-files" class="tab-content hidden">
+                        <?php include 'file-upload.php'; ?>
+                    </div><!-- end #tab-files -->
+                </div>
+            </form>
+        </div>
+    </div>
+</div>
+
+<?php
+include 'copyright.php';
+include 'common-js.php';
+include 'form-js.php';
+include 'write-js.php';
+
+\Typecho\Plugin::factory('admin/write-post.php')->trigger($plugged)->richEditor($post);
+if (!$plugged) {
+    include 'editor-js.php';
+}
+
+include 'file-upload-js.php';
+include 'custom-fields-js.php';
+\Typecho\Plugin::factory('admin/write-post.php')->bottom($post);
+include 'footer.php';
+?>

+ 33 - 0
config.inc.php

@@ -0,0 +1,33 @@
+<?php
+// site root path
+define('__TYPECHO_ROOT_DIR__', dirname(__FILE__));
+
+// plugin directory (relative path)
+define('__TYPECHO_PLUGIN_DIR__', '/usr/plugins');
+
+// theme directory (relative path)
+define('__TYPECHO_THEME_DIR__', '/usr/themes');
+
+// admin directory (relative path)
+define('__TYPECHO_ADMIN_DIR__', '/admin/');
+
+// register autoload
+require_once __TYPECHO_ROOT_DIR__ . '/var/Typecho/Common.php';
+
+// init
+\Typecho\Common::init();
+
+// config db
+$db = new \Typecho\Db('Pdo_Mysql', 'typecho_');
+$db->addServer(array (
+  'host' => 'localhost',
+  'port' => 3306,
+  'user' => 'root',
+  'password' => '123456',
+  'charset' => 'utf8mb4',
+  'database' => 'typecho',
+  'engine' => 'InnoDB',
+  'sslCa' => '',
+  'sslVerify' => false,
+), \Typecho\Db::READ | \Typecho\Db::WRITE);
+\Typecho\Db::set($db);

+ 26 - 0
index.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Typecho Blog Platform
+ *
+ * @copyright  Copyright (c) 2008 Typecho team (http://www.typecho.org)
+ * @license    GNU General Public License 2.0
+ * @version    $Id: index.php 1153 2009-07-02 10:53:22Z magike.net $
+ */
+
+/** 载入配置支持 */
+if (!defined('__TYPECHO_ROOT_DIR__') && !@include_once 'config.inc.php') {
+    file_exists('./install.php') ? header('Location: install.php') : print('Missing Config File');
+    exit;
+}
+
+/** 初始化组件 */
+\Widget\Init::alloc();
+
+/** 注册一个初始化插件 */
+\Typecho\Plugin::factory('index.php')->begin();
+
+/** 开始路由分发 */
+\Typecho\Router::dispatch();
+
+/** 注册一个结束插件 */
+\Typecho\Plugin::factory('index.php')->end();

+ 1489 - 0
install.php

@@ -0,0 +1,1489 @@
+<?php
+
+if (!file_exists(dirname(__FILE__) . '/config.inc.php')) {
+    // site root path
+    define('__TYPECHO_ROOT_DIR__', dirname(__FILE__));
+
+    // plugin directory (relative path)
+    define('__TYPECHO_PLUGIN_DIR__', '/usr/plugins');
+
+    // theme directory (relative path)
+    define('__TYPECHO_THEME_DIR__', '/usr/themes');
+
+    // admin directory (relative path)
+    define('__TYPECHO_ADMIN_DIR__', '/admin/');
+
+    // register autoload
+    require_once __TYPECHO_ROOT_DIR__ . '/var/Typecho/Common.php';
+
+    // init
+    \Typecho\Common::init();
+} else {
+    require_once dirname(__FILE__) . '/config.inc.php';
+    $installDb = \Typecho\Db::get();
+}
+
+/**
+ * get lang
+ *
+ * @return string
+ */
+function install_get_lang(): string
+{
+    $serverLang = \Typecho\Request::getInstance()->getServer('TYPECHO_LANG');
+
+    if (!empty($serverLang)) {
+        return $serverLang;
+    } else {
+        $lang = 'zh_CN';
+        $request = \Typecho\Request::getInstance();
+
+        if ($request->is('lang')) {
+            $lang = $request->get('lang');
+            \Typecho\Cookie::set('lang', $lang);
+        }
+
+        return \Typecho\Cookie::get('lang', $lang);
+    }
+}
+
+/**
+ * get site url
+ *
+ * @return string
+ */
+function install_get_site_url(): string
+{
+    $request = \Typecho\Request::getInstance();
+    return install_is_cli() ? $request->getServer('TYPECHO_SITE_URL', 'http://localhost') : $request->getRequestRoot();
+}
+
+/**
+ * detect cli mode
+ *
+ * @return bool
+ */
+function install_is_cli(): bool
+{
+    return \Typecho\Request::getInstance()->isCli();
+}
+
+/**
+ * get default router
+ *
+ * @return string[][]
+ */
+function install_get_default_routers(): array
+{
+    return [
+        'index'              =>
+            [
+                'url'    => '/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'archive'            =>
+            [
+                'url'    => '/blog/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'do'                 =>
+            [
+                'url'    => '/action/[action:alpha]',
+                'widget' => '\Widget\Action',
+                'action' => 'action',
+            ],
+        'post'               =>
+            [
+                'url'    => '/archives/[cid:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'attachment'         =>
+            [
+                'url'    => '/attachment/[cid:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'category'           =>
+            [
+                'url'    => '/category/[slug]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'tag'                =>
+            [
+                'url'    => '/tag/[slug]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'author'             =>
+            [
+                'url'    => '/author/[uid:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'search'             =>
+            [
+                'url'    => '/search/[keywords]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'index_page'         =>
+            [
+                'url'    => '/page/[page:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'archive_page'       =>
+            [
+                'url'    => '/blog/page/[page:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'category_page'      =>
+            [
+                'url'    => '/category/[slug]/[page:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'tag_page'           =>
+            [
+                'url'    => '/tag/[slug]/[page:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'author_page'        =>
+            [
+                'url'    => '/author/[uid:digital]/[page:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'search_page'        =>
+            [
+                'url'    => '/search/[keywords]/[page:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'archive_year'       =>
+            [
+                'url'    => '/[year:digital:4]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'archive_month'      =>
+            [
+                'url'    => '/[year:digital:4]/[month:digital:2]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'archive_day'        =>
+            [
+                'url'    => '/[year:digital:4]/[month:digital:2]/[day:digital:2]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'archive_year_page'  =>
+            [
+                'url'    => '/[year:digital:4]/page/[page:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'archive_month_page' =>
+            [
+                'url'    => '/[year:digital:4]/[month:digital:2]/page/[page:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'archive_day_page'   =>
+            [
+                'url'    => '/[year:digital:4]/[month:digital:2]/[day:digital:2]/page/[page:digital]/',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'comment_page'       =>
+            [
+                'url'    => '[permalink:string]/comment-page-[commentPage:digital]',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+        'feed'               =>
+            [
+                'url'    => '/feed[feed:string:0]',
+                'widget' => '\Widget\Archive',
+                'action' => 'feed',
+            ],
+        'feedback'           =>
+            [
+                'url'    => '[permalink:string]/[type:alpha]',
+                'widget' => '\Widget\Feedback',
+                'action' => 'action',
+            ],
+        'page'               =>
+            [
+                'url'    => '/[slug].html',
+                'widget' => '\Widget\Archive',
+                'action' => 'render',
+            ],
+    ];
+}
+
+/**
+ * list all default options
+ *
+ * @return array
+ */
+function install_get_default_options(): array
+{
+    static $options;
+
+    if (empty($options)) {
+        $options = [
+            'theme' => 'default',
+            'theme:default' => 'a:2:{s:7:"logoUrl";N;s:12:"sidebarBlock";a:5:{i:0;s:15:"ShowRecentPosts";i:1;s:18:"ShowRecentComments";i:2;s:12:"ShowCategory";i:3;s:11:"ShowArchive";i:4;s:9:"ShowOther";}}',
+            'timezone' => '28800',
+            'lang' => install_get_lang(),
+            'charset' => 'UTF-8',
+            'contentType' => 'text/html',
+            'gzip' => 0,
+            'generator' => 'Typecho ' . \Typecho\Common::VERSION,
+            'title' => 'Hello World',
+            'description' => 'Your description here.',
+            'keywords' => 'typecho,php,blog',
+            'rewrite' => 0,
+            'frontPage' => 'recent',
+            'frontArchive' => 0,
+            'commentsRequireMail' => 1,
+            'commentsWhitelist' => 0,
+            'commentsRequireURL' => 0,
+            'commentsRequireModeration' => 0,
+            'plugins' => 'a:0:{}',
+            'commentDateFormat' => 'F jS, Y \a\t h:i a',
+            'siteUrl' => install_get_site_url(),
+            'defaultCategory' => 1,
+            'allowRegister' => 0,
+            'defaultAllowComment' => 1,
+            'defaultAllowPing' => 1,
+            'defaultAllowFeed' => 1,
+            'pageSize' => 5,
+            'postsListSize' => 10,
+            'commentsListSize' => 10,
+            'commentsHTMLTagAllowed' => null,
+            'postDateFormat' => 'Y-m-d',
+            'feedFullText' => 1,
+            'editorSize' => 350,
+            'autoSave' => 0,
+            'markdown' => 1,
+            'xmlrpcMarkdown' => 0,
+            'commentsMaxNestingLevels' => 5,
+            'commentsPostTimeout' => 24 * 3600 * 30,
+            'commentsUrlNofollow' => 1,
+            'commentsShowUrl' => 1,
+            'commentsMarkdown' => 0,
+            'commentsPageBreak' => 0,
+            'commentsThreaded' => 1,
+            'commentsPageSize' => 20,
+            'commentsPageDisplay' => 'last',
+            'commentsOrder' => 'ASC',
+            'commentsCheckReferer' => 1,
+            'commentsAutoClose' => 0,
+            'commentsPostIntervalEnable' => 1,
+            'commentsPostInterval' => 60,
+            'commentsShowCommentOnly' => 0,
+            'commentsAvatar' => 1,
+            'commentsAvatarRating' => 'G',
+            'commentsAntiSpam' => 1,
+            'routingTable' => serialize(install_get_default_routers()),
+            'actionTable' => 'a:0:{}',
+            'panelTable' => 'a:0:{}',
+            'attachmentTypes' => '@image@',
+            'secret' => \Typecho\Common::randString(32, true),
+            'installed' => 0,
+            'allowXmlRpc' => 2
+        ];
+    }
+
+    return $options;
+}
+
+/**
+ * get database driver type
+ *
+ * @param string $driver
+ * @return string
+ */
+function install_get_db_type(string $driver): string
+{
+    $parts = explode('_', $driver);
+    return $driver == 'Mysqli' ? 'Mysql' : array_pop($parts);
+}
+
+/**
+ * list all available database drivers
+ *
+ * @return array
+ */
+function install_get_db_drivers(): array
+{
+    $drivers = [];
+
+    if (\Typecho\Db\Adapter\Pdo\Mysql::isAvailable()) {
+        $drivers['Pdo_Mysql'] = _t('Pdo 驱动 Mysql 适配器');
+    }
+
+    if (\Typecho\Db\Adapter\Pdo\SQLite::isAvailable()) {
+        $drivers['Pdo_SQLite'] = _t('Pdo 驱动 SQLite 适配器');
+    }
+
+    if (\Typecho\Db\Adapter\Pdo\Pgsql::isAvailable()) {
+        $drivers['Pdo_Pgsql'] = _t('Pdo 驱动 PostgreSql 适配器');
+    }
+
+    if (\Typecho\Db\Adapter\Mysqli::isAvailable()) {
+        $drivers['Mysqli'] = _t('Mysql 原生函数适配器');
+    }
+
+    if (\Typecho\Db\Adapter\SQLite::isAvailable()) {
+        $drivers['SQLite'] = _t('SQLite 原生函数适配器');
+    }
+
+    if (\Typecho\Db\Adapter\Pgsql::isAvailable()) {
+        $drivers['Pgsql'] = _t('Pgsql 原生函数适配器');
+    }
+
+    return $drivers;
+}
+
+/**
+ * get current db driver
+ *
+ * @return string
+ */
+function install_get_current_db_driver(): string
+{
+    global $installDb;
+
+    if (empty($installDb)) {
+        $driver = \Typecho\Request::getInstance()->get('driver');
+        $drivers = install_get_db_drivers();
+
+        if (empty($driver) || !isset($drivers[$driver])) {
+            return key($drivers);
+        }
+
+        return $driver;
+    } else {
+        return $installDb->getAdapterName();
+    }
+}
+
+/**
+ * generate config file
+ *
+ * @param string $adapter
+ * @param string $dbPrefix
+ * @param array $dbConfig
+ * @param bool $return
+ * @return string
+ */
+function install_config_file(string $adapter, string $dbPrefix, array $dbConfig, bool $return = false): string
+{
+    global $configWritten;
+
+    $code = "<" . "?php
+// site root path
+define('__TYPECHO_ROOT_DIR__', dirname(__FILE__));
+
+// plugin directory (relative path)
+define('__TYPECHO_PLUGIN_DIR__', '/usr/plugins');
+
+// theme directory (relative path)
+define('__TYPECHO_THEME_DIR__', '/usr/themes');
+
+// admin directory (relative path)
+define('__TYPECHO_ADMIN_DIR__', '/admin/');
+
+// register autoload
+require_once __TYPECHO_ROOT_DIR__ . '/var/Typecho/Common.php';
+
+// init
+\Typecho\Common::init();
+
+// config db
+\$db = new \Typecho\Db('{$adapter}', '{$dbPrefix}');
+\$db->addServer(" . (var_export($dbConfig, true)) . ", \Typecho\Db::READ | \Typecho\Db::WRITE);
+\Typecho\Db::set(\$db);
+";
+
+    $configWritten = false;
+
+    if (!$return) {
+        $configWritten = @file_put_contents(__TYPECHO_ROOT_DIR__ . '/config.inc.php', $code) !== false;
+    }
+
+    return $code;
+}
+
+/**
+ * remove config file if written
+ */
+function install_remove_config_file()
+{
+    global $configWritten;
+
+    if ($configWritten) {
+        unlink(__TYPECHO_ROOT_DIR__ . '/config.inc.php');
+    }
+}
+
+/**
+ * check install
+ *
+ * @param string $type
+ * @return bool
+ */
+function install_check(string $type): bool
+{
+    switch ($type) {
+        case 'config':
+            return file_exists(__TYPECHO_ROOT_DIR__ . '/config.inc.php');
+        case 'db_structure':
+        case 'db_data':
+            global $installDb;
+
+            if (empty($installDb)) {
+                return false;
+            }
+
+            try {
+                // check if table exists
+                $installed = $installDb->fetchRow($installDb->select()->from('table.options')
+                    ->where('user = 0 AND name = ?', 'installed'));
+
+                if ($type == 'db_data' && empty($installed['value'])) {
+                    return false;
+                }
+            } catch (\Typecho\Db\Adapter\ConnectionException $e) {
+                return true;
+            } catch (\Typecho\Db\Adapter\SQLException $e) {
+                return false;
+            }
+
+            return true;
+        default:
+            return false;
+    }
+}
+
+/**
+ * raise install error
+ *
+ * @param mixed $error
+ * @param mixed $config
+ */
+function install_raise_error($error, $config = null)
+{
+    if (install_is_cli()) {
+        if (is_array($error)) {
+            foreach ($error as $key => $value) {
+                echo (is_int($key) ? '' : $key . ': ') . $value . "\n";
+            }
+        } else {
+            echo $error . "\n";
+        }
+
+        exit(1);
+    } else {
+        install_throw_json([
+            'success' => 0,
+            'message' => is_string($error) ? nl2br($error) : $error,
+            'config' => $config
+        ]);
+    }
+}
+
+/**
+ * @param $step
+ * @param array|null $config
+ */
+function install_success($step, ?array $config = null)
+{
+    global $installDb;
+
+    if (install_is_cli()) {
+        if ($step == 3) {
+            \Typecho\Db::set($installDb);
+        }
+
+        if ($step > 0) {
+            $method = 'install_step_' . $step . '_perform';
+            $method();
+        }
+
+        if (!empty($config)) {
+            [$userName, $userPassword] = $config;
+            echo _t('安装成功') . "\n";
+            echo _t('您的用户名是') . " {$userName}\n";
+            echo _t('您的密码是') . " {$userPassword}\n";
+        }
+
+        exit(0);
+    } else {
+        install_throw_json([
+            'success' => 1,
+            'message' => $step,
+            'config'  => $config
+        ]);
+    }
+}
+
+/**
+ * @param $data
+ */
+function install_throw_json($data)
+{
+    \Typecho\Response::getInstance()->setContentType('application/json')
+        ->addResponder(function () use ($data) {
+            echo json_encode($data);
+        })
+        ->respond();
+}
+
+/**
+ * @param string $url
+ */
+function install_redirect(string $url)
+{
+    \Typecho\Response::getInstance()->setStatus(302)
+        ->setHeader('Location', $url)
+        ->respond();
+}
+
+/**
+ * add common js support
+ */
+function install_js_support()
+{
+    ?>
+    <div id="success" class="row typecho-page-main hidden">
+        <div class="col-mb-12 col-tb-8 col-tb-offset-2">
+            <div class="typecho-page-title">
+                <h2><?php _e('安装成功'); ?></h2>
+            </div>
+            <div id="typecho-welcome">
+                <p class="keep-word">
+                    <?php _e('您选择了使用原有的数据, 您的用户名和密码和原来的一致'); ?>
+                </p>
+                <p class="fresh-word">
+                    <?php _e('您的用户名是'); ?>: <strong class="warning" id="success-user"></strong><br>
+                    <?php _e('您的密码是'); ?>: <strong class="warning" id="success-password"></strong>
+                </p>
+                <ul>
+                    <li><a id="login-url" href=""><?php _e('点击这里访问您的控制面板'); ?></a></li>
+                    <li><a id="site-url" href=""><?php _e('点击这里查看您的 Blog'); ?></a></li>
+                </ul>
+                <p><?php _e('希望您能尽情享用 Typecho 带来的乐趣!'); ?></p>
+            </div>
+        </div>
+    </div>
+    <script>
+        let form = $('form'), errorBox = $('<div></div>');
+
+        errorBox.addClass('message error')
+            .prependTo(form);
+
+        function showError(error) {
+            if (typeof error == 'string') {
+                $(window).scrollTop(0);
+
+                errorBox
+                    .html(error)
+                    .addClass('fade');
+            } else {
+                for (let k in error) {
+                    let input = $('#' + k), msg = error[k], p = $('<p></p>');
+
+                    p.addClass('message error')
+                        .html(msg)
+                        .insertAfter(input);
+
+                    input.on('input', function () {
+                        p.remove();
+                    });
+                }
+            }
+
+            return errorBox;
+        }
+
+        form.submit(function (e) {
+            e.preventDefault();
+
+            errorBox.removeClass('fade');
+            $('button', form).attr('disabled', 'disabled');
+            $('.typecho-option .error', form).remove();
+
+            $.ajax({
+                url: form.attr('action'),
+                processData: false,
+                contentType: false,
+                type: 'POST',
+                data: new FormData(this),
+                success: function (data) {
+                    $('button', form).removeAttr('disabled');
+
+                    if (data.success) {
+                        if (data.message) {
+                            location.href = '?step=' + data.message;
+                        } else {
+                            let success = $('#success').removeClass('hidden');
+
+                            form.addClass('hidden');
+
+                            if (data.config) {
+                                success.addClass('fresh');
+
+                                $('.typecho-page-main:first').addClass('hidden');
+                                $('#success-user').html(data.config[0]);
+                                $('#success-password').html(data.config[1]);
+
+                                $('#login-url').attr('href', data.config[2]);
+                                $('#site-url').attr('href', data.config[3]);
+                            } else {
+                                success.addClass('keep');
+                            }
+                        }
+                    } else {
+                        let el = showError(data.message);
+
+                        if (typeof configError == 'function' && data.config) {
+                            configError(form, data.config, el);
+                        }
+                    }
+                },
+                error: function (xhr, error) {
+                    showError(error)
+                }
+            });
+        });
+    </script>
+    <?php
+}
+
+/**
+ * @param string[] $extensions
+ * @return string|null
+ */
+function install_check_extension(array $extensions): ?string
+{
+    foreach ($extensions as $extension) {
+        if (extension_loaded($extension)) {
+            return null;
+        }
+    }
+
+    return _n('缺少PHP扩展', '请在服务器上安装以下PHP扩展中的至少一个', count($extensions))
+        . ': ' . implode(', ', $extensions);
+}
+
+function install_step_1()
+{
+    $langs = \Widget\Options\General::getLangs();
+    $lang = install_get_lang();
+    ?>
+    <div class="row typecho-page-main">
+        <div class="col-mb-12 col-tb-8 col-tb-offset-2">
+            <div class="typecho-page-title">
+                <h2><?php _e('欢迎使用 Typecho'); ?></h2>
+            </div>
+            <div id="typecho-welcome">
+                <form autocomplete="off" method="post" action="install.php">
+                    <h3><?php _e('安装说明'); ?></h3>
+                    <p class="warning">
+                        <strong><?php _e('本安装程序将自动检测服务器环境是否符合最低配置需求. 如果不符合, 将在上方出现提示信息, 请按照提示信息检查您的主机配置. 如果服务器环境符合要求, 将在下方出现 "开始下一步" 的按钮, 点击此按钮即可一步完成安装.'); ?></strong>
+                    </p>
+                    <h3><?php _e('许可及协议'); ?></h3>
+                    <ul>
+                        <li><?php _e('Typecho 基于 <a href="https://www.gnu.org/copyleft/gpl.html">GPL</a> 协议发布, 我们允许用户在 GPL 协议许可的范围内使用, 拷贝, 修改和分发此程序.'); ?>
+                            <?php _e('在GPL许可的范围内, 您可以自由地将其用于商业以及非商业用途.'); ?></li>
+                        <li><?php _e('Typecho 软件由其社区提供支持, 核心开发团队负责维护程序日常开发工作以及新特性的制定.'); ?>
+                            <?php _e('如果您遇到使用上的问题, 程序中的 BUG, 以及期许的新功能, 欢迎您在社区中交流或者直接向我们贡献代码.'); ?>
+                            <?php _e('对于贡献突出者, 他的名字将出现在贡献者名单中.'); ?></li>
+                    </ul>
+
+                    <p class="submit">
+                        <button class="btn primary" type="submit"><?php _e('我准备好了, 开始下一步 &raquo;'); ?></button>
+                        <input type="hidden" name="step" value="1">
+
+                        <?php if (count($langs) > 1) : ?>
+                            <select style="float: right" onchange="location.href='?lang=' + this.value">
+                                <?php foreach ($langs as $key => $val) : ?>
+                                    <option value="<?php echo $key; ?>"<?php if ($lang == $key) :
+                                        ?> selected<?php
+                                                   endif; ?>><?php echo $val; ?></option>
+                                <?php endforeach; ?>
+                            </select>
+                        <?php endif; ?>
+                    </p>
+                </form>
+            </div>
+        </div>
+    </div>
+    <?php
+    install_js_support();
+}
+
+/**
+ * check dependencies before install
+ */
+function install_step_1_perform()
+{
+    $errors = [];
+    $checks = [
+        'mbstring',
+        'json',
+        'Reflection',
+        ['mysqli', 'sqlite3', 'pgsql', 'pdo_mysql', 'pdo_sqlite', 'pdo_pgsql']
+    ];
+
+    foreach ($checks as $check) {
+        $error = install_check_extension(is_array($check) ? $check : [$check]);
+
+        if (!empty($error)) {
+            $errors[] = $error;
+        }
+    }
+
+    $uploadDir = '/usr/uploads';
+    $realUploadDir = \Typecho\Common::url($uploadDir, __TYPECHO_ROOT_DIR__);
+    $writeable = true;
+    if (is_dir($realUploadDir)) {
+        if (!is_writeable($realUploadDir) || !is_readable($realUploadDir)) {
+            if (!@chmod($realUploadDir, 0755)) {
+                $writeable = false;
+            }
+        }
+    } else {
+        if (!@mkdir($realUploadDir, 0755)) {
+            $writeable = false;
+        }
+    }
+
+    if (!$writeable) {
+        $errors[] = _t('上传目录无法写入, 请手动将安装目录下的 %s 目录的权限设置为可写然后继续升级', $uploadDir);
+    }
+
+    if (empty($errors)) {
+        install_success(2);
+    } else {
+        install_raise_error(implode("\n", $errors));
+    }
+}
+
+/**
+ * display step 2
+ */
+function install_step_2()
+{
+    global $installDb;
+
+    $drivers = install_get_db_drivers();
+    $adapter = install_get_current_db_driver();
+    $type = install_get_db_type($adapter);
+
+    if (!empty($installDb)) {
+        $config = $installDb->getConfig(\Typecho\Db::WRITE)->toArray();
+        $config['prefix'] = $installDb->getPrefix();
+        $config['adapter'] = $adapter;
+    }
+    ?>
+    <div class="row typecho-page-main">
+        <div class="col-mb-12 col-tb-8 col-tb-offset-2">
+            <div class="typecho-page-title">
+                <h2><?php _e('初始化配置'); ?></h2>
+            </div>
+            <form autocomplete="off" action="install.php" method="post">
+                <ul class="typecho-option">
+                    <li>
+                        <label for="dbAdapter" class="typecho-label"><?php _e('数据库适配器'); ?></label>
+                        <select name="dbAdapter" id="dbAdapter" onchange="location.href='?step=2&driver=' + this.value">
+                            <?php foreach ($drivers as $driver => $name) : ?>
+                                <option value="<?php echo $driver; ?>"<?php if ($driver == $adapter) :
+                                    ?> selected="selected"<?php
+                                               endif; ?>><?php echo $name; ?></option>
+                            <?php endforeach; ?>
+                        </select>
+                        <p class="description"><?php _e('请根据您的数据库类型选择合适的适配器'); ?></p>
+                        <input type="hidden" id="dbNext" name="dbNext" value="none">
+                    </li>
+                </ul>
+                <ul class="typecho-option">
+                    <li>
+                        <label class="typecho-label" for="dbPrefix"><?php _e('数据库前缀'); ?></label>
+                        <input type="text" class="text" name="dbPrefix" id="dbPrefix" value="typecho_" />
+                        <p class="description"><?php _e('默认前缀是 "typecho_"'); ?></p>
+                    </li>
+                </ul>
+                <?php require_once './install/' . $type . '.php'; ?>
+
+
+                <ul class="typecho-option typecho-option-submit">
+                    <li>
+                        <button id="confirm" type="submit" class="btn primary"><?php _e('确认, 开始安装 &raquo;'); ?></button>
+                        <input type="hidden" name="step" value="2">
+                    </li>
+                </ul>
+            </form>
+        </div>
+    </div>
+    <script>
+        function configError(form, config, errorBox) {
+            let next = $('#dbNext'),
+                line = $('<p></p>');
+
+            if (config.code) {
+                let text = $('<textarea></textarea>'),
+                    btn = $('<button></button>');
+
+                btn.html('<?php _e('创建完毕, 继续安装 &raquo;'); ?>')
+                    .attr('type', 'button')
+                    .addClass('btn btn-s primary');
+
+                btn.click(function () {
+                    next.val('config');
+                    form.trigger('submit');
+                });
+
+                text.val(config.code)
+                    .addClass('mono')
+                    .attr('readonly', 'readonly');
+
+                errorBox.append(text)
+                    .append(btn);
+                return;
+            }
+
+            errorBox.append(line);
+
+            for (let key in config) {
+                let word = config[key],
+                    btn = $('<button></button>');
+
+                btn.html(word)
+                    .attr('type', 'button')
+                    .addClass('btn btn-s primary')
+                    .click(function () {
+                        next.val(key);
+                        form.trigger('submit');
+                    });
+
+                line.append(btn);
+            }
+        }
+
+        $('#confirm').click(function () {
+            $('#dbNext').val('none');
+        });
+
+        <?php if (!empty($config)) : ?>
+        function fillInput(config) {
+            for (let k in config) {
+                let value = config[k],
+                    key = 'db' + k.charAt(0).toUpperCase() + k.slice(1),
+                    input = $('#' + key)
+                        .attr('readonly', 'readonly')
+                        .val(value);
+
+                $('option:not(:selected)', input).attr('disabled', 'disabled');
+            }
+        }
+
+        fillInput(<?php echo json_encode($config); ?>);
+        <?php endif; ?>
+    </script>
+    <?php
+    install_js_support();
+}
+
+/**
+ * perform install step 2
+ */
+function install_step_2_perform()
+{
+    global $installDb;
+
+    $request = \Typecho\Request::getInstance();
+    $drivers = install_get_db_drivers();
+
+    $configMap = [
+        'Mysql' => [
+            'dbHost' => 'localhost',
+            'dbPort' => 3306,
+            'dbUser' => null,
+            'dbPassword' => null,
+            'dbCharset' => 'utf8mb4',
+            'dbDatabase' => null,
+            'dbEngine' => 'InnoDB',
+            'dbSslCa' => null,
+            'dbSslVerify' => 'on',
+        ],
+        'Pgsql' => [
+            'dbHost' => 'localhost',
+            'dbPort' => 5432,
+            'dbUser' => null,
+            'dbPassword' => null,
+            'dbCharset' => 'utf8',
+            'dbDatabase' => null,
+        ],
+        'SQLite' => [
+            'dbFile' => __TYPECHO_ROOT_DIR__ . '/usr/' . uniqid() . '.db'
+        ]
+    ];
+
+    if (install_is_cli()) {
+        $config = [
+            'dbHost' => $request->getServer('TYPECHO_DB_HOST'),
+            'dbUser' => $request->getServer('TYPECHO_DB_USER'),
+            'dbPassword' => $request->getServer('TYPECHO_DB_PASSWORD'),
+            'dbCharset' => $request->getServer('TYPECHO_DB_CHARSET'),
+            'dbPort' => $request->getServer('TYPECHO_DB_PORT'),
+            'dbDatabase' => $request->getServer('TYPECHO_DB_DATABASE'),
+            'dbFile' => $request->getServer('TYPECHO_DB_FILE'),
+            'dbDsn' => $request->getServer('TYPECHO_DB_DSN'),
+            'dbEngine' => $request->getServer('TYPECHO_DB_ENGINE'),
+            'dbPrefix' => $request->getServer('TYPECHO_DB_PREFIX', 'typecho_'),
+            'dbAdapter' => $request->getServer('TYPECHO_DB_ADAPTER', install_get_current_db_driver()),
+            'dbNext' => $request->getServer('TYPECHO_DB_NEXT', 'none'),
+            'dbSslCa' => $request->getServer('TYPECHO_DB_SSL_CA'),
+            'dbSslVerify' => $request->getServer('TYPECHO_DB_SSL_VERIFY', 'on'),
+        ];
+    } else {
+        $config = $request->from([
+            'dbHost',
+            'dbUser',
+            'dbPassword',
+            'dbCharset',
+            'dbPort',
+            'dbDatabase',
+            'dbFile',
+            'dbDsn',
+            'dbEngine',
+            'dbPrefix',
+            'dbAdapter',
+            'dbNext',
+            'dbSslCa',
+            'dbSslVerify',
+        ]);
+    }
+
+    $error = (new \Typecho\Validate())
+        ->addRule('dbPrefix', 'required', _t('确认您的配置'))
+        ->addRule('dbPrefix', 'minLength', _t('确认您的配置'), 1)
+        ->addRule('dbPrefix', 'maxLength', _t('确认您的配置'), 16)
+        ->addRule('dbPrefix', 'alphaDash', _t('确认您的配置'))
+        ->addRule('dbAdapter', 'required', _t('确认您的配置'))
+        ->addRule('dbAdapter', 'enum', _t('确认您的配置'), array_keys($drivers))
+        ->addRule('dbNext', 'required', _t('确认您的配置'))
+        ->addRule('dbNext', 'enum', _t('确认您的配置'), ['none', 'delete', 'keep', 'config'])
+        ->run($config);
+
+    if (!empty($error)) {
+        install_raise_error($error);
+    }
+
+    $type = install_get_db_type($config['dbAdapter']);
+    $dbConfig = [];
+
+    foreach ($configMap[$type] as $key => $value) {
+        $config[$key] = !isset($config[$key]) ? (install_is_cli() ? $value : null) : $config[$key];
+    }
+
+    switch ($type) {
+        case 'Mysql':
+            $error = (new \Typecho\Validate())
+                ->addRule('dbHost', 'required', _t('确认您的配置'))
+                ->addRule('dbPort', 'required', _t('确认您的配置'))
+                ->addRule('dbPort', 'isInteger', _t('确认您的配置'))
+                ->addRule('dbUser', 'required', _t('确认您的配置'))
+                ->addRule('dbCharset', 'required', _t('确认您的配置'))
+                ->addRule('dbCharset', 'enum', _t('确认您的配置'), ['utf8', 'utf8mb4'])
+                ->addRule('dbDatabase', 'required', _t('确认您的配置'))
+                ->addRule('dbEngine', 'required', _t('确认您的配置'))
+                ->addRule('dbEngine', 'enum', _t('确认您的配置'), ['InnoDB', 'MyISAM'])
+                ->addRule('dbSslCa', 'file_exists', _t('确认您的配置'))
+                ->addRule('dbSslVerify', 'enum', _t('确认您的配置'), ['on', 'off'])
+                ->run($config);
+            break;
+        case 'Pgsql':
+            $error = (new \Typecho\Validate())
+                ->addRule('dbHost', 'required', _t('确认您的配置'))
+                ->addRule('dbPort', 'required', _t('确认您的配置'))
+                ->addRule('dbPort', 'isInteger', _t('确认您的配置'))
+                ->addRule('dbUser', 'required', _t('确认您的配置'))
+                ->addRule('dbCharset', 'required', _t('确认您的配置'))
+                ->addRule('dbCharset', 'enum', _t('确认您的配置'), ['utf8'])
+                ->addRule('dbDatabase', 'required', _t('确认您的配置'))
+                ->run($config);
+            break;
+        case 'SQLite':
+            $error = (new \Typecho\Validate())
+                ->addRule('dbFile', 'required', _t('确认您的配置'))
+                ->addRule('dbFile', function (string $path) {
+                    $pattern = "/^(\/[._a-z0-9-]+)*[a-z0-9]+\.[a-z0-9]{2,}$/i";
+                    if (strstr(PHP_OS, 'WIN')) {
+                        $pattern = "/(\/[._a-z0-9-]+)*[a-z0-9]+\.[a-z0-9]{2,}$/i";
+                    }
+                    return !!preg_match($pattern, $path);
+                }, _t('确认您的配置'))
+                ->run($config);
+            break;
+        default:
+            install_raise_error(_t('确认您的配置'));
+            break;
+    }
+
+    if (!empty($error)) {
+        install_raise_error($error);
+    }
+
+    foreach ($configMap[$type] as $key => $value) {
+        $dbConfig[lcfirst(substr($key, 2))] = $config[$key];
+    }
+
+    // intval port number
+    if (isset($dbConfig['port'])) {
+        $dbConfig['port'] = intval($dbConfig['port']);
+    }
+
+    // bool ssl verify
+    if (isset($dbConfig['sslVerify'])) {
+        $dbConfig['sslVerify'] = $dbConfig['sslVerify'] == 'on';
+    }
+
+    if (isset($dbConfig['file']) && preg_match("/^[a-z0-9]+\.[a-z0-9]{2,}$/i", $dbConfig['file'])) {
+        $dbConfig['file'] = __DIR__ . '/usr/' . $dbConfig['file'];
+    }
+
+    // check config file
+    if ($config['dbNext'] == 'config' && !install_check('config')) {
+        $code = install_config_file($config['dbAdapter'], $config['dbPrefix'], $dbConfig, true);
+        install_raise_error(_t('没有检测到您手动创建的配置文件, 请检查后再次创建'), ['code' => $code]);
+    } elseif (empty($installDb)) {
+        // detect db config
+        try {
+            $installDb = new \Typecho\Db($config['dbAdapter'], $config['dbPrefix']);
+            $installDb->addServer($dbConfig, \Typecho\Db::READ | \Typecho\Db::WRITE);
+            $installDb->query('SELECT 1=1');
+        } catch (\Typecho\Db\Adapter\ConnectionException $e) {
+            $code = $e->getCode();
+            if (('Mysql' == $type && 1049 == $code) || ('Pgsql' == $type && 7 == $code)) {
+                install_raise_error(_t('数据库: "%s"不存在,请手动创建后重试', $config['dbDatabase']));
+            } else {
+                install_raise_error(_t('对不起, 无法连接数据库, 请先检查数据库配置再继续进行安装: "%s"', $e->getMessage()));
+            }
+        } catch (\Typecho\Db\Exception $e) {
+            install_raise_error(_t('安装程序捕捉到以下错误: "%s". 程序被终止, 请检查您的配置信息.', $e->getMessage()));
+        }
+
+        $code = install_config_file($config['dbAdapter'], $config['dbPrefix'], $dbConfig);
+
+        if (!install_check('config')) {
+            install_raise_error(
+                _t('安装程序无法自动创建 <strong>config.inc.php</strong> 文件') . "\n" .
+                _t('您可以在网站根目录下手动创建 <strong>config.inc.php</strong> 文件, 并复制如下代码至其中'),
+                [
+                'code' => $code
+                ]
+            );
+        }
+    }
+
+    // delete exists db
+    if ($config['dbNext'] == 'delete') {
+        $tables = [
+            $config['dbPrefix'] . 'comments',
+            $config['dbPrefix'] . 'contents',
+            $config['dbPrefix'] . 'fields',
+            $config['dbPrefix'] . 'metas',
+            $config['dbPrefix'] . 'options',
+            $config['dbPrefix'] . 'relationships',
+            $config['dbPrefix'] . 'users'
+        ];
+
+        try {
+            foreach ($tables as $table) {
+                switch ($type) {
+                    case 'Mysql':
+                        $installDb->query("DROP TABLE IF EXISTS `{$table}`");
+                        break;
+                    case 'Pgsql':
+                    case 'SQLite':
+                        $installDb->query("DROP TABLE {$table}");
+                        break;
+                }
+            }
+        } catch (\Typecho\Db\Exception $e) {
+            install_raise_error(_t('安装程序捕捉到以下错误: "%s". 程序被终止, 请检查您的配置信息.', $e->getMessage()));
+        }
+    }
+
+    // init db structure
+    try {
+        $scripts = file_get_contents(__TYPECHO_ROOT_DIR__ . '/install/' . $type . '.sql');
+        $scripts = str_replace('typecho_', $config['dbPrefix'], $scripts);
+
+        if (isset($dbConfig['charset'])) {
+            $scripts = str_replace('%charset%', $dbConfig['charset'], $scripts);
+        }
+
+        if (isset($dbConfig['engine'])) {
+            $scripts = str_replace('%engine%', $dbConfig['engine'], $scripts);
+        }
+
+        $scripts = explode(';', $scripts);
+        foreach ($scripts as $script) {
+            $script = trim($script);
+            if ($script) {
+                $installDb->query($script, \Typecho\Db::WRITE);
+            }
+        }
+    } catch (\Typecho\Db\Exception $e) {
+        $code = $e->getCode();
+
+        if (
+            ('Mysql' == $type && (1050 == $code || '42S01' == $code)) ||
+            ('SQLite' == $type && ('HY000' == $code || 1 == $code)) ||
+            ('Pgsql' == $type && '42P07' == $code)
+        ) {
+            if ($config['dbNext'] == 'keep') {
+                if (install_check('db_data')) {
+                    install_success(0);
+                } else {
+                    install_success(3);
+                }
+            } elseif ($config['dbNext'] == 'none') {
+                install_remove_config_file();
+
+                install_raise_error(_t('安装程序检查到原有数据表已经存在.'), [
+                    'delete' => _t('删除原有数据'),
+                    'keep' => _t('使用原有数据')
+                ]);
+            }
+        } else {
+            install_remove_config_file();
+
+            install_raise_error(_t('安装程序捕捉到以下错误: "%s". 程序被终止, 请检查您的配置信息.', $e->getMessage()));
+        }
+    }
+
+    install_success(3);
+}
+
+/**
+ * display step 3
+ */
+function install_step_3()
+{
+    $options = \Widget\Options::alloc();
+    ?>
+    <div class="row typecho-page-main">
+        <div class="col-mb-12 col-tb-8 col-tb-offset-2">
+            <div class="typecho-page-title">
+                <h2><?php _e('创建您的管理员帐号'); ?></h2>
+            </div>
+            <form autocomplete="off" action="install.php" method="post">
+                <ul class="typecho-option">
+                    <li>
+                        <label class="typecho-label" for="userUrl"><?php _e('网站地址'); ?></label>
+                        <input autocomplete="new-password" type="text" name="userUrl" id="userUrl" class="text" value="<?php $options->rootUrl(); ?>" />
+                        <p class="description"><?php _e('这是程序自动匹配的网站路径, 如果不正确请修改它'); ?></p>
+                    </li>
+                </ul>
+                <ul class="typecho-option">
+                    <li>
+                        <label class="typecho-label" for="userName"><?php _e('用户名'); ?></label>
+                        <input autocomplete="new-password" type="text" name="userName" id="userName" class="text" />
+                        <p class="description"><?php _e('请填写您的用户名'); ?></p>
+                    </li>
+                </ul>
+                <ul class="typecho-option">
+                    <li>
+                        <label class="typecho-label" for="userPassword"><?php _e('登录密码'); ?></label>
+                        <input type="password" name="userPassword" id="userPassword" class="text" />
+                        <p class="description"><?php _e('请填写您的登录密码, 如果留空系统将为您随机生成一个'); ?></p>
+                    </li>
+                </ul>
+                <ul class="typecho-option">
+                    <li>
+                        <label class="typecho-label" for="userMail"><?php _e('邮件地址'); ?></label>
+                        <input autocomplete="new-password" type="text" name="userMail" id="userMail" class="text" />
+                        <p class="description"><?php _e('请填写一个您的常用邮箱'); ?></p>
+                    </li>
+                </ul>
+                <ul class="typecho-option typecho-option-submit">
+                    <li>
+                        <button type="submit" class="btn primary"><?php _e('继续安装 &raquo;'); ?></button>
+                        <input type="hidden" name="step" value="3">
+                    </li>
+                </ul>
+            </form>
+        </div>
+    </div>
+    <?php
+    install_js_support();
+}
+
+/**
+ * perform step 3
+ */
+function install_step_3_perform()
+{
+    global $installDb;
+
+    $request = \Typecho\Request::getInstance();
+    $defaultPassword = \Typecho\Common::randString(8);
+    $options = \Widget\Options::alloc();
+
+    if (install_is_cli()) {
+        $config = [
+            'userUrl' => $request->getServer('TYPECHO_SITE_URL'),
+            'userName' => $request->getServer('TYPECHO_USER_NAME', 'typecho'),
+            'userPassword' => $request->getServer('TYPECHO_USER_PASSWORD'),
+            'userMail' => $request->getServer('TYPECHO_USER_MAIL', 'admin@localhost.local')
+        ];
+    } else {
+        $config = $request->from([
+            'userUrl',
+            'userName',
+            'userPassword',
+            'userMail',
+        ]);
+    }
+
+    $error = (new \Typecho\Validate())
+        ->addRule('userUrl', 'required', _t('请填写站点地址'))
+        ->addRule('userUrl', 'url', _t('请填写一个合法的URL地址'))
+        ->addRule('userName', 'required', _t('必须填写用户名称'))
+        ->addRule('userName', 'xssCheck', _t('请不要在用户名中使用特殊字符'))
+        ->addRule('userName', 'maxLength', _t('用户名长度超过限制, 请不要超过 32 个字符'), 32)
+        ->addRule('userMail', 'required', _t('必须填写电子邮箱'))
+        ->addRule('userMail', 'email', _t('电子邮箱格式错误'))
+        ->addRule('userMail', 'maxLength', _t('邮箱长度超过限制, 请不要超过 200 个字符'), 200)
+        ->run($config);
+
+    if (!empty($error)) {
+        install_raise_error($error);
+    }
+
+    if (empty($config['userPassword'])) {
+        $config['userPassword'] = $defaultPassword;
+    }
+
+    try {
+        // write user
+        $hasher = new \Utils\PasswordHash(8, true);
+        $installDb->query(
+            $installDb->insert('table.users')->rows([
+                'name' => $config['userName'],
+                'password' => $hasher->hashPassword($config['userPassword']),
+                'mail' => $config['userMail'],
+                'url' => $config['userUrl'],
+                'screenName' => $config['userName'],
+                'group' => 'administrator',
+                'created' => \Typecho\Date::time()
+            ])
+        );
+
+        // write category
+        $installDb->query(
+            $installDb->insert('table.metas')
+                ->rows([
+                    'name' => _t('默认分类'),
+                    'slug' => 'default',
+                    'type' => 'category',
+                    'description' => _t('只是一个默认分类'),
+                    'count' => 1
+                ])
+        );
+
+        $installDb->query($installDb->insert('table.relationships')->rows(['cid' => 1, 'mid' => 1]));
+
+        // write first page and post
+        $installDb->query(
+            $installDb->insert('table.contents')->rows([
+                'title' => _t('欢迎使用 Typecho'),
+                'slug' => 'start', 'created' => \Typecho\Date::time(),
+                'modified' => \Typecho\Date::time(),
+                'text' => '<!--markdown-->' . _t('如果您看到这篇文章,表示您的 blog 已经安装成功.'),
+                'authorId' => 1,
+                'type' => 'post',
+                'status' => 'publish',
+                'commentsNum' => 1,
+                'allowComment' => 1,
+                'allowPing' => 1,
+                'allowFeed' => 1,
+                'parent' => 0
+            ])
+        );
+
+        $installDb->query(
+            $installDb->insert('table.contents')->rows([
+                'title' => _t('关于'),
+                'slug' => 'start-page',
+                'created' => \Typecho\Date::time(),
+                'modified' => \Typecho\Date::time(),
+                'text' => '<!--markdown-->' . _t('本页面由 Typecho 创建, 这只是个测试页面.'),
+                'authorId' => 1,
+                'order' => 0,
+                'type' => 'page',
+                'status' => 'publish',
+                'commentsNum' => 0,
+                'allowComment' => 1,
+                'allowPing' => 1,
+                'allowFeed' => 1,
+                'parent' => 0
+            ])
+        );
+
+        // write comment
+        $installDb->query(
+            $installDb->insert('table.comments')->rows([
+                'cid' => 1, 'created' => \Typecho\Date::time(),
+                'author' => 'Typecho',
+                'ownerId' => 1,
+                'url' => 'https://typecho.org',
+                'ip' => '127.0.0.1',
+                'agent' => $options->generator,
+                'text' => '欢迎加入 Typecho 大家族',
+                'type' => 'comment',
+                'status' => 'approved',
+                'parent' => 0
+            ])
+        );
+
+        // write options
+        foreach (install_get_default_options() as $key => $value) {
+            // mark installing finished
+            if ($key == 'installed') {
+                $value = 1;
+            }
+
+            $installDb->query(
+                $installDb->insert('table.options')->rows(['name' => $key, 'user' => 0, 'value' => $value])
+            );
+        }
+    } catch (\Typecho\Db\Exception $e) {
+        install_raise_error($e->getMessage());
+    }
+
+    $parts = parse_url($options->loginAction);
+    $parts['query'] = http_build_query([
+            'name'  => $config['userName'],
+            'password' => $config['userPassword'],
+            'referer' => $options->adminUrl
+        ]);
+    $loginUrl = \Typecho\Common::buildUrl($parts);
+
+    install_success(0, [
+        $config['userName'],
+        $config['userPassword'],
+        \Widget\Security::alloc()->getTokenUrl($loginUrl, $request->getReferer()),
+        $config['userUrl']
+    ]);
+}
+
+/**
+ * dispatch install action
+ *
+ */
+function install_dispatch()
+{
+    // disable root url on cli mode
+    if (install_is_cli()) {
+        define('__TYPECHO_ROOT_URL__', 'http://localhost');
+    }
+
+    // init default options
+    $options = \Widget\Options::alloc(install_get_default_options());
+    \Widget\Init::alloc();
+
+    // display version
+    if (install_is_cli()) {
+        echo $options->generator . "\n";
+        echo 'PHP ' . PHP_VERSION . "\n";
+    }
+
+    // install finished yet
+    if (
+        install_check('config')
+        && install_check('db_structure')
+        && install_check('db_data')
+    ) {
+        // redirect to siteUrl if not cli
+        if (!install_is_cli()) {
+            install_redirect($options->siteUrl);
+        }
+
+        exit(1);
+    }
+
+    if (install_is_cli()) {
+        install_step_1_perform();
+    } else {
+        $request = \Typecho\Request::getInstance();
+        $step = $request->get('step');
+
+        $action = 1;
+
+        switch (true) {
+            case $step == 2:
+                if (!install_check('db_structure')) {
+                    $action = 2;
+                } else {
+                    install_redirect('install.php?step=3');
+                }
+                break;
+            case $step == 3:
+                if (install_check('db_structure')) {
+                    $action = 3;
+                } else {
+                    install_redirect('install.php?step=2');
+                }
+                break;
+            default:
+                break;
+        }
+
+        $method = 'install_step_' . $action;
+
+        if ($request->isPost()) {
+            $method .= '_perform';
+            $method();
+            exit;
+        }
+        ?>
+<!DOCTYPE HTML>
+<html>
+<head>
+    <meta charset="<?php _e('UTF-8'); ?>" />
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <title><?php _e('Typecho 安装程序'); ?></title>
+    <link rel="stylesheet" type="text/css" href="<?php $options->adminStaticUrl('css', 'normalize.css') ?>" />
+    <link rel="stylesheet" type="text/css" href="<?php $options->adminStaticUrl('css', 'grid.css') ?>" />
+    <link rel="stylesheet" type="text/css" href="<?php $options->adminStaticUrl('css', 'style.css') ?>" />
+    <link rel="stylesheet" type="text/css" href="<?php $options->adminStaticUrl('css', 'install.css') ?>" />
+    <script src="<?php $options->adminStaticUrl('js', 'jquery.js'); ?>"></script>
+</head>
+<body>
+    <div class="body container">
+        <h1><a href="https://typecho.org" target="_blank" class="i-logo">Typecho</a></h1>
+        <?php $method(); ?>
+    </div>
+</body>
+</html>
+        <?php
+    }
+}
+
+install_dispatch();

+ 84 - 0
install/Mysql.php

@@ -0,0 +1,84 @@
+<?php if(!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
+
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbHost"><?php _e('数据库地址'); ?></label>
+        <input type="text" class="text" name="dbHost" id="dbHost" value="localhost"/>
+        <p class="description"><?php _e('您可能会使用 "%s"', 'localhost'); ?></p>
+    </li>
+</ul>
+
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbUser"><?php _e('数据库用户名'); ?></label>
+        <input type="text" class="text" name="dbUser" id="dbUser" value="" />
+        <p class="description"><?php _e('您可能会使用 "%s"', 'root'); ?></p>
+    </li>
+</ul>
+
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbPassword"><?php _e('数据库密码'); ?></label>
+        <input type="password" class="text" name="dbPassword" id="dbPassword" value="" />
+    </li>
+</ul>
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbDatabase"><?php _e('数据库名'); ?></label>
+        <input type="text" class="text" name="dbDatabase" id="dbDatabase" value="" />
+        <p class="description"><?php _e('请您指定数据库名称'); ?></p>
+    </li>
+
+</ul>
+
+<details>
+    <summary>
+        <strong><?php _e('高级选项'); ?></strong>
+    </summary>
+    <ul class="typecho-option">
+        <li>
+            <label class="typecho-label" for="dbPort"><?php _e('数据库端口'); ?></label>
+            <input type="text" class="text" name="dbPort" id="dbPort" value="3306"/>
+            <p class="description"><?php _e('如果您不知道此选项的意义, 请保留默认设置'); ?></p>
+        </li>
+    </ul>
+
+    <ul class="typecho-option">
+        <li>
+            <label class="typecho-label" for="dbCharset"><?php _e('数据库编码'); ?></label>
+            <select name="dbCharset" id="dbCharset">
+                <option value="utf8mb4">utf8mb4</option>
+                <option value="utf8">utf8</option>
+            </select>
+            <p class="description"><?php _e('选择 utf8mb4 编码至少需要 MySQL 5.5.3 版本'); ?></p>
+        </li>
+    </ul>
+
+    <ul class="typecho-option">
+        <li>
+            <label class="typecho-label" for="dbEngine"><?php _e('数据库引擎'); ?></label>
+            <select name="dbEngine" id="dbEngine">
+                <option value="InnoDB">InnoDB</option>
+                <option value="MyISAM">MyISAM</option>
+            </select>
+        </li>
+    </ul>
+
+    <ul class="typecho-option">
+        <li>
+            <label class="typecho-label" for="dbSslCa"><?php _e('数据库 SSL 证书'); ?></label>
+            <input type="text" class="text" name="dbSslCa" id="dbSslCa"/>
+            <p class="description"><?php _e('如果您的数据库启用了 SSL,请填写 CA 证书路径,否则请留空'); ?></p>
+        </li>
+    </ul>
+
+    <ul class="typecho-option">
+        <li>
+            <label class="typecho-label" for="dbSslVerify"><?php _e('启用数据库 SSL 服务端证书验证'); ?></label>
+            <select name="dbSslVerify" id="dbSslVerify">
+                <option value="on"><?php _e('启用'); ?></option>
+                <option value="off"><?php _e('不启用'); ?></option>
+            </select>
+        </li>
+    </ul>
+</details>

+ 152 - 0
install/Mysql.sql

@@ -0,0 +1,152 @@
+-- phpMyAdmin SQL Dump
+-- version 2.11.5
+-- http://www.phpmyadmin.net
+--
+-- 主机: localhost
+-- 生成日期: 2008 年 07 月 06 日 18:00
+-- 服务器版本: 5.0.51
+-- PHP 版本: 5.2.5
+
+--
+-- 数据库: `typecho`
+--
+
+-- --------------------------------------------------------
+
+--
+-- 表的结构 `typecho_comments`
+--
+
+CREATE TABLE `typecho_comments` (
+  `coid` int(10) unsigned NOT NULL auto_increment,
+  `cid` int(10) unsigned default '0',
+  `created` int(10) unsigned default '0',
+  `author` varchar(150) default NULL,
+  `authorId` int(10) unsigned default '0',
+  `ownerId` int(10) unsigned default '0',
+  `mail` varchar(150) default NULL,
+  `url` varchar(255) default NULL,
+  `ip` varchar(64) default NULL,
+  `agent` varchar(511) default NULL,
+  `text` text,
+  `type` varchar(16) default 'comment',
+  `status` varchar(16) default 'approved',
+  `parent` int(10) unsigned default '0',
+  PRIMARY KEY  (`coid`),
+  KEY `cid` (`cid`),
+  KEY `created` (`created`)
+) ENGINE=%engine%  DEFAULT CHARSET=%charset%;
+
+-- --------------------------------------------------------
+
+--
+-- 表的结构 `typecho_contents`
+--
+
+CREATE TABLE `typecho_contents` (
+  `cid` int(10) unsigned NOT NULL auto_increment,
+  `title` varchar(150) default NULL,
+  `slug` varchar(150) default NULL,
+  `created` int(10) unsigned default '0',
+  `modified` int(10) unsigned default '0',
+  `text` longtext,
+  `order` int(10) unsigned default '0',
+  `authorId` int(10) unsigned default '0',
+  `template` varchar(32) default NULL,
+  `type` varchar(16) default 'post',
+  `status` varchar(16) default 'publish',
+  `password` varchar(32) default NULL,
+  `commentsNum` int(10) unsigned default '0',
+  `allowComment` char(1) default '0',
+  `allowPing` char(1) default '0',
+  `allowFeed` char(1) default '0',
+  `parent` int(10) unsigned default '0',
+  PRIMARY KEY  (`cid`),
+  UNIQUE KEY `slug` (`slug`),
+  KEY `created` (`created`)
+) ENGINE=%engine%  DEFAULT CHARSET=%charset%;
+
+-- --------------------------------------------------------
+
+--
+-- 表的结构 `typecho_fields`
+--
+
+CREATE TABLE `typecho_fields` (
+  `cid` int(10) unsigned NOT NULL,
+  `name` varchar(150) NOT NULL,
+  `type` varchar(8) default 'str',
+  `str_value` text,
+  `int_value` int(10) default '0',
+  `float_value` float default '0',
+  PRIMARY KEY  (`cid`,`name`),
+  KEY `int_value` (`int_value`),
+  KEY `float_value` (`float_value`)
+) ENGINE=%engine%  DEFAULT CHARSET=%charset%;
+
+-- --------------------------------------------------------
+
+--
+-- 表的结构 `typecho_metas`
+--
+
+CREATE TABLE `typecho_metas` (
+  `mid` int(10) unsigned NOT NULL auto_increment,
+  `name` varchar(150) default NULL,
+  `slug` varchar(150) default NULL,
+  `type` varchar(32) NOT NULL,
+  `description` varchar(150) default NULL,
+  `count` int(10) unsigned default '0',
+  `order` int(10) unsigned default '0',
+  `parent` int(10) unsigned default '0',
+  PRIMARY KEY  (`mid`),
+  KEY `slug` (`slug`)
+) ENGINE=%engine%  DEFAULT CHARSET=%charset%;
+
+-- --------------------------------------------------------
+
+--
+-- 表的结构 `typecho_options`
+--
+
+CREATE TABLE `typecho_options` (
+  `name` varchar(32) NOT NULL,
+  `user` int(10) unsigned NOT NULL default '0',
+  `value` text,
+  PRIMARY KEY  (`name`,`user`)
+) ENGINE=%engine% DEFAULT CHARSET=%charset%;
+
+-- --------------------------------------------------------
+
+--
+-- 表的结构 `typecho_relationships`
+--
+
+CREATE TABLE `typecho_relationships` (
+  `cid` int(10) unsigned NOT NULL,
+  `mid` int(10) unsigned NOT NULL,
+  PRIMARY KEY  (`cid`,`mid`)
+) ENGINE=%engine% DEFAULT CHARSET=%charset%;
+
+-- --------------------------------------------------------
+
+--
+-- 表的结构 `typecho_users`
+--
+
+CREATE TABLE `typecho_users` (
+  `uid` int(10) unsigned NOT NULL auto_increment,
+  `name` varchar(32) default NULL,
+  `password` varchar(64) default NULL,
+  `mail` varchar(150) default NULL,
+  `url` varchar(150) default NULL,
+  `screenName` varchar(32) default NULL,
+  `created` int(10) unsigned default '0',
+  `activated` int(10) unsigned default '0',
+  `logged` int(10) unsigned default '0',
+  `group` varchar(16) default 'visitor',
+  `authCode` varchar(64) default NULL,
+  PRIMARY KEY  (`uid`),
+  UNIQUE KEY `name` (`name`),
+  UNIQUE KEY `mail` (`mail`)
+) ENGINE=%engine%  DEFAULT CHARSET=%charset%;

+ 37 - 0
install/Pgsql.php

@@ -0,0 +1,37 @@
+<?php if(!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbHost"><?php _e('数据库地址'); ?></label>
+        <input type="text" class="text" name="dbHost" id="dbHost" value="localhost"/>
+        <p class="description"><?php _e('您可能会使用 "%s"', 'localhost'); ?></p>
+    </li>
+</ul>
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbPort"><?php _e('数据库端口'); ?></label>
+        <input type="text" class="text" name="dbPort" id="dbPort" value="5432"/>
+        <p class="description"><?php _e('如果您不知道此选项的意义, 请保留默认设置'); ?></p>
+    </li>
+</ul>
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbUser"><?php _e('数据库用户名'); ?></label>
+        <input type="text" class="text" name="dbUser" id="dbUser" value="postgres" />
+        <p class="description"><?php _e('您可能会使用 "%s"', 'postgres'); ?></p>
+    </li>
+</ul>
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbPassword"><?php _e('数据库密码'); ?></label>
+        <input type="password" class="text" name="dbPassword" id="dbPassword" value="" />
+    </li
+</ul>
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbDatabase"><?php _e('数据库名'); ?></label>
+        <input type="text" class="text" name="dbDatabase" id="dbDatabase" value="" />
+        <p class="description"><?php _e('请您指定数据库名称'); ?></p>
+    </li
+</ul>
+
+<input type="hidden" name="dbCharset" value="utf8" />

+ 130 - 0
install/Pgsql.sql

@@ -0,0 +1,130 @@
+--
+-- Table structure for table "typecho_comments"
+--
+CREATE SEQUENCE "typecho_comments_seq";
+
+CREATE TABLE "typecho_comments" (  "coid" INT NOT NULL DEFAULT nextval('typecho_comments_seq'),
+  "cid" INT NULL DEFAULT '0',
+  "created" INT NULL DEFAULT '0',
+  "author" VARCHAR(150) NULL DEFAULT NULL,
+  "authorId" INT NULL DEFAULT '0',
+  "ownerId" INT NULL DEFAULT '0',
+  "mail" VARCHAR(150) NULL DEFAULT NULL,
+  "url" VARCHAR(255) NULL DEFAULT NULL,
+  "ip" VARCHAR(64) NULL DEFAULT NULL,
+  "agent" VARCHAR(511) NULL DEFAULT NULL,
+  "text" TEXT NULL DEFAULT NULL,
+  "type" VARCHAR(16) NULL DEFAULT 'comment',
+  "status" VARCHAR(16) NULL DEFAULT 'approved',
+  "parent" INT NULL DEFAULT '0',
+  PRIMARY KEY ("coid")
+);
+
+CREATE INDEX "typecho_comments_cid" ON "typecho_comments" ("cid");
+CREATE INDEX "typecho_comments_created" ON "typecho_comments" ("created");
+
+
+--
+-- Table structure for table "typecho_contents"
+--
+
+CREATE SEQUENCE "typecho_contents_seq";
+
+CREATE TABLE "typecho_contents" (  "cid" INT NOT NULL DEFAULT nextval('typecho_contents_seq'),
+  "title" VARCHAR(150) NULL DEFAULT NULL,
+  "slug" VARCHAR(150) NULL DEFAULT NULL,
+  "created" INT NULL DEFAULT '0',
+  "modified" INT NULL DEFAULT '0',
+  "text" TEXT NULL DEFAULT NULL,
+  "order" INT NULL DEFAULT '0',
+  "authorId" INT NULL DEFAULT '0',
+  "template" VARCHAR(32) NULL DEFAULT NULL,
+  "type" VARCHAR(16) NULL DEFAULT 'post',
+  "status" VARCHAR(16) NULL DEFAULT 'publish',
+  "password" VARCHAR(32) NULL DEFAULT NULL,
+  "commentsNum" INT NULL DEFAULT '0',
+  "allowComment" CHAR(1) NULL DEFAULT '0',
+  "allowPing" CHAR(1) NULL DEFAULT '0',
+  "allowFeed" CHAR(1) NULL DEFAULT '0',
+  "parent" INT NULL DEFAULT '0',
+  PRIMARY KEY ("cid"),
+  UNIQUE ("slug")
+);
+
+CREATE INDEX "typecho_contents_created" ON "typecho_contents" ("created");
+
+--
+-- Table structure for table "typecho_fields"
+--
+
+CREATE TABLE "typecho_fields" ("cid" INT NOT NULL,
+  "name" VARCHAR(150) NOT NULL,
+  "type" VARCHAR(8) NULL DEFAULT 'str',
+  "str_value" TEXT NULL DEFAULT NULL,
+  "int_value" INT NULL DEFAULT '0',
+  "float_value" REAL NULL DEFAULT '0',
+  PRIMARY KEY  ("cid","name")
+);
+
+CREATE INDEX "typecho_fields_int_value" ON "typecho_fields" ("int_value");
+CREATE INDEX "typecho_fields_float_value" ON "typecho_fields" ("float_value");
+
+--
+-- Table structure for table "typecho_metas"
+--
+
+CREATE SEQUENCE "typecho_metas_seq";
+
+CREATE TABLE "typecho_metas" (  "mid" INT NOT NULL DEFAULT nextval('typecho_metas_seq'),
+  "name" VARCHAR(150) NULL DEFAULT NULL,
+  "slug" VARCHAR(150) NULL DEFAULT NULL,
+  "type" VARCHAR(16) NOT NULL DEFAULT '',
+  "description" VARCHAR(150) NULL DEFAULT NULL,
+  "count" INT NULL DEFAULT '0',
+  "order" INT NULL DEFAULT '0',
+  "parent" INT NULL DEFAULT '0',
+  PRIMARY KEY ("mid")
+);
+
+CREATE INDEX "typecho_metas_slug" ON "typecho_metas" ("slug");
+
+
+--
+-- Table structure for table "typecho_options"
+--
+
+CREATE TABLE "typecho_options" (  "name" VARCHAR(32) NOT NULL DEFAULT '',
+  "user" INT NOT NULL DEFAULT '0',
+  "value" TEXT NULL DEFAULT NULL,
+  PRIMARY KEY ("name","user")
+);
+
+--
+-- Table structure for table "typecho_relationships"
+--
+
+CREATE TABLE "typecho_relationships" (  "cid" INT NOT NULL DEFAULT '0',
+  "mid" INT NOT NULL DEFAULT '0',
+  PRIMARY KEY ("cid","mid")
+); 
+
+--
+-- Table structure for table "typecho_users"
+--
+CREATE SEQUENCE "typecho_users_seq";
+
+CREATE TABLE "typecho_users" (  "uid" INT NOT NULL DEFAULT nextval('typecho_users_seq') ,
+  "name" VARCHAR(32) NULL DEFAULT NULL,
+  "password" VARCHAR(64) NULL DEFAULT NULL,
+  "mail" VARCHAR(150) NULL DEFAULT NULL,
+  "url" VARCHAR(150) NULL DEFAULT NULL,
+  "screenName" VARCHAR(32) NULL DEFAULT NULL,
+  "created" INT NULL DEFAULT '0',
+  "activated" INT NULL DEFAULT '0',
+  "logged" INT NULL DEFAULT '0',
+  "group" VARCHAR(16) NULL DEFAULT 'visitor',
+  "authCode" VARCHAR(64) NULL DEFAULT NULL,
+  PRIMARY KEY ("uid"),
+  UNIQUE ("name"),
+  UNIQUE ("mail")
+);

+ 9 - 0
install/SQLite.php

@@ -0,0 +1,9 @@
+<?php if(!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
+<?php $defaultDir = __TYPECHO_ROOT_DIR__ . '/usr/' . uniqid() . '.db'; ?>
+<ul class="typecho-option">
+    <li>
+        <label class="typecho-label" for="dbFile"><?php _e('数据库文件路径'); ?></label>
+        <input type="text" class="text" name="dbFile" id="dbFile" value="<?php echo $defaultDir; ?>"/>
+        <p class="description"><?php _e('"%s" 是我们为您自动生成的地址', $defaultDir); ?></p>
+    </li>
+</ul>

+ 87 - 0
install/SQLite.sql

@@ -0,0 +1,87 @@
+CREATE TABLE typecho_comments ( "coid" INTEGER NOT NULL PRIMARY KEY,
+"cid" int(10) default '0' ,
+"created" int(10) default '0' ,
+"author" varchar(150) default NULL ,
+"authorId" int(10) default '0' ,
+"ownerId" int(10) default '0' ,
+"mail" varchar(150) default NULL ,
+"url" varchar(255) default NULL ,
+"ip" varchar(64) default NULL , 
+"agent" varchar(511) default NULL ,
+"text" text , 
+"type" varchar(16) default 'comment' , 
+"status" varchar(16) default 'approved' , 
+"parent" int(10) default '0' );
+
+CREATE INDEX typecho_comments_cid ON typecho_comments ("cid");
+CREATE INDEX typecho_comments_created ON typecho_comments ("created");
+
+CREATE TABLE typecho_contents ( "cid" INTEGER NOT NULL PRIMARY KEY, 
+"title" varchar(150) default NULL ,
+"slug" varchar(150) default NULL ,
+"created" int(10) default '0' , 
+"modified" int(10) default '0' , 
+"text" text , 
+"order" int(10) default '0' , 
+"authorId" int(10) default '0' , 
+"template" varchar(32) default NULL , 
+"type" varchar(16) default 'post' , 
+"status" varchar(16) default 'publish' , 
+"password" varchar(32) default NULL , 
+"commentsNum" int(10) default '0' , 
+"allowComment" char(1) default '0' , 
+"allowPing" char(1) default '0' , 
+"allowFeed" char(1) default '0' ,
+"parent" int(10) default '0' );
+
+CREATE UNIQUE INDEX typecho_contents_slug ON typecho_contents ("slug");
+CREATE INDEX typecho_contents_created ON typecho_contents ("created");
+
+CREATE TABLE "typecho_fields" ("cid" INTEGER NOT NULL,
+  "name" varchar(150) NOT NULL,
+  "type" varchar(8) default 'str',
+  "str_value" text,
+  "int_value" int(10) default '0',
+  "float_value" real default '0'
+);
+
+CREATE UNIQUE INDEX typecho_fields_cid_name ON typecho_fields ("cid", "name");
+CREATE INDEX typecho_fields_int_value ON typecho_fields ("int_value");
+CREATE INDEX typecho_fields_float_value ON typecho_fields ("float_value");
+
+CREATE TABLE typecho_metas ( "mid" INTEGER NOT NULL PRIMARY KEY, 
+"name" varchar(150) default NULL ,
+"slug" varchar(150) default NULL ,
+"type" varchar(32) NOT NULL , 
+"description" varchar(150) default NULL ,
+"count" int(10) default '0' , 
+"order" int(10) default '0' ,
+"parent" int(10) default '0');
+
+CREATE INDEX typecho_metas_slug ON typecho_metas ("slug");
+
+CREATE TABLE typecho_options ( "name" varchar(32) NOT NULL , 
+"user" int(10) NOT NULL default '0' , 
+"value" text );
+
+CREATE UNIQUE INDEX typecho_options_name_user ON typecho_options ("name", "user");
+
+CREATE TABLE typecho_relationships ( "cid" int(10) NOT NULL , 
+"mid" int(10) NOT NULL );
+
+CREATE UNIQUE INDEX typecho_relationships_cid_mid ON typecho_relationships ("cid", "mid");
+
+CREATE TABLE typecho_users ( "uid" INTEGER NOT NULL PRIMARY KEY, 
+"name" varchar(32) default NULL ,
+"password" varchar(64) default NULL , 
+"mail" varchar(150) default NULL ,
+"url" varchar(150) default NULL ,
+"screenName" varchar(32) default NULL , 
+"created" int(10) default '0' , 
+"activated" int(10) default '0' , 
+"logged" int(10) default '0' , 
+"group" varchar(16) default 'visitor' , 
+"authCode" varchar(64) default NULL);
+
+CREATE UNIQUE INDEX typecho_users_name ON typecho_users ("name");
+CREATE UNIQUE INDEX typecho_users_mail ON typecho_users ("mail");

BIN
usr/langs/ceb.mo


BIN
usr/langs/el_GR.mo


BIN
usr/langs/en_US.mo


BIN
usr/langs/es_ES.mo


BIN
usr/langs/fil_PH.mo


BIN
usr/langs/fr_FR.mo


BIN
usr/langs/ja_JP.mo


BIN
usr/langs/pt_BR.mo


BIN
usr/langs/ru_RU.mo


BIN
usr/langs/tr_TR.mo


BIN
usr/langs/ug_CN.mo


BIN
usr/langs/zh_TW.mo


+ 29 - 0
usr/plugins/BearDocsCore/Action.php

@@ -0,0 +1,29 @@
+<?php
+namespace TypechoPlugin\BearDocsCore;
+use BearDocsCore;
+use bdOptions;
+use \Utils\Helper;
+use Typecho\{Exception, Widget, Db};
+use Typecho\Cookie;
+use Widget\Options;
+
+
+if(!class_exists('CSF')){
+    require_once Helper::options()->pluginDir('BearDocsCore').'/bdoptions-framework.php';
+}
+
+if (!class_exists('bdOptions')){
+    require_once \Utils\Helper::options()->pluginDir('BearDocsCore').'/bdOptions.php';
+}
+
+class Action extends Widget implements \Widget\ActionInterface
+{
+
+ 
+
+    /**
+     * 初始化
+     * @return $this
+     */
+    
+}

+ 229 - 0
usr/plugins/BearDocsCore/Plugin.php

@@ -0,0 +1,229 @@
+<?php
+namespace TypechoPlugin\BearDocsCore;
+error_reporting(0);
+use Utils\Helper;
+use Typecho\Plugin\PluginInterface;
+use Typecho\Widget\Helper\Form;
+use Typecho\{Plugin\Exception, Widget, Db, Widget\Request as WidgetRequest};
+use \CSF as CSF;
+use bdOptions;
+use Widget\Options;
+use Typecho\Common;
+use Typecho\Router;
+use Widget\Archive;
+use Widget\Contents\Post\Admin;
+use Widget\Contents\Post\Edit;
+use Widget\Feedback;
+use Widget\Service;
+use ReflectionClass;
+use Utils\PasswordHash;
+use Typecho\Widget\Response as WidgetResponse;
+use Utils\Markdown as Markdown;
+
+
+/**
+ * BearDocs主题核心插件
+ * <br>安装后无需进行其他设置
+ * @package BearDocsCore
+ * @author BearNotion
+ * @version v1.1.1-release
+ * @link https://www.bearnotion.ru/
+ *
+ */
+
+
+
+if (!defined('__TYPECHO_ROOT_DIR__')) exit;
+if (!defined('pluginName')) {
+  define('pluginName', 'BearDocsCore');
+}
+require_once 'bdoptions-framework.php';
+
+class Plugin implements PluginInterface
+{
+    
+     public static function getNS(): string
+    {
+        return __NAMESPACE__;
+    }
+    
+    public static function activate()
+    {
+        /* 
+         *
+         *  初始化路由和方法
+         *
+         */
+        bdRouter::initRouter();
+        \Typecho\Plugin::factory('index.php')->begin_999 = [__CLASS__, 'initevent'];
+        \Typecho\Plugin::factory('admin/header.php')->header_999 = [__CLASS__, 'enqueue_style'];
+        \Typecho\Plugin::factory('admin/footer.php')->end_999 = [__CLASS__, 'enqueue_script'];
+        \Typecho\Plugin::factory('admin/write-post.php')->bottom_999 = [__CLASS__,'shortcode_button'];
+        \Typecho\Plugin::factory('admin/write-page.php')->bottom_999 = [__CLASS__,'shortcode_button'];
+        \CSF::activateEvent();
+        
+        
+        
+         return _t('BearDocsCore核心同步完成');
+    }
+
+    public static function deactivate()
+    {
+        bdRouter::removeRouter();
+        return _t('BearDocsCore核心清除完成');
+    }
+    
+    
+   
+
+    public static function initevent()
+    {
+        if (!class_exists('bdOptions')){
+            require_once \Utils\Helper::options()->pluginDir('BearDocsCore').'/bdOptions.php';
+        }
+    }
+    public static function send_request($url, $postdata,$sendtype,$header = 'Content-type: application/x-www-form-urlencoded') {
+     if($postdata){
+         if(is_array($postdata)){
+    $data = http_build_query($postdata);
+         }
+         else{
+           $data = $postdata;
+         }
+    $options    = array(
+        'http' => array(
+            'method'  => $sendtype,
+            'header'  => $header,
+            'content' => $data,
+            'timeout' => 5
+        )
+    );
+     }
+     else{
+     $options    = array(
+        'http' => array(
+            'method'  => $sendtype,
+            'header'  => "Content-type: application/x-www-form-urlencoded",
+            'timeout' => 5
+        )
+    );    
+     }
+    $context = stream_context_create($options);
+    $result    = file_get_contents($url, false, $context);
+    if($http_response_header[0] !== 'HTTP/1.1 200 OK'){
+        $result = array(
+            "result" => "success",
+            "reason" => "request fail"
+        );
+        return json_encode($result);
+    }else{
+        return $result;
+    }
+}
+
+    public static function enqueue_style($header=null, $old=null)
+    {
+        if ($old!=null) $header = $old;
+        return CSF::get_enqueue_style($header);
+    }
+
+    public static function enqueue_script($footer=null)
+    {
+        return CSF::get_enqueue_script($footer);
+    }
+
+    public static function config(Form $form)
+    {
+
+    }
+
+    public static function personalConfig(Form $form)
+    {
+    }
+    
+    
+
+    public static function configHandle($origin_config, $is_init)
+    {
+        return true;
+    }
+    
+
+   
+    public static function shortcode_button(){
+        ?>
+        <style>.wmd-button-row {
+    height: auto;
+}
+</style>
+<link rel="stylesheet" href="//cdn.staticfile.org/remixicon/3.5.0/remixicon.min.css" />
+        <script>
+        $(document).ready(function() {
+shortCode_changelog();
+});
+
+
+
+//插入按钮
+function shortCode_changelog(){
+    	if ($("#wmd-button-row").length > 0) {
+		$('#wmd-button-row').append('<li class="wmd-button" id="wmd-log-button" style="" title="插入更新日志"><span style="background: none;margin-top:7px;font-size: 15px;text-align: center;color: #999999;font-family: serif;"><i class="ri-book-line"></i></span></li>');
+
+		
+		
+		$(document).on('click', '#wmd-log-button',
+		function() {
+			textContent = '\r\n' + '[log version="版本号" date="日期" type="版本类型"]'+'\r\n'+'该版本更新内容' + '\r\n[/log]';
+			myField = document.getElementById('text');
+			insertButton(myField, textContent);
+		});
+		
+
+	
+    	};
+}
+
+function insertButton(myField, textContent, modelId = '') {
+	if (modelId != '') {
+		$(modelId).remove();
+	}
+	if (document.selection) {
+		myField.focus();
+		var sel = document.selection.createRange();
+		sel.text = textContent;
+		myField.focus();
+	} else if (myField.selectionStart || myField.selectionStart == '0') {
+		var startPos = myField.selectionStart;
+		var endPos = myField.selectionEnd;
+		var cursorPos = startPos;
+		myField.value = myField.value.substring(0, startPos) + textContent + myField.value.substring(endPos, myField.value.length);
+		cursorPos += textContent.length;
+		myField.selectionStart = cursorPos;
+		myField.selectionEnd = cursorPos;
+		myField.focus();
+	} else { //其他环境
+		myField.value += textContent;
+		myField.focus();
+	}
+}
+
+</script>
+<?php
+}
+
+
+    public static function getSecurity($typeName,$name,$value = null){
+    if($typeName == 'get'){
+        $value = \Typecho\Cookie::get($name);
+        return $value;
+    }
+    elseif($typeName == 'set'){
+        \Typecho\Cookie::set($name, $value);
+    }
+    else{
+        \Typecho\Cookie::delete($name, $value);
+    }
+}
+
+
+}

+ 182 - 0
usr/plugins/BearDocsCore/assets/css/color-picker.css

@@ -0,0 +1,182 @@
+/* rtl:ignore */
+.wp-color-picker {
+	width: 80px;
+	direction: ltr;
+}
+
+.wp-picker-container .hidden {
+	display: none;
+}
+
+/* Needs higher specificiity. */
+.wp-picker-container .wp-color-result.button {
+	min-height: 30px;
+	margin: 0 6px 6px 0;
+	padding: 0 0 0 30px;
+	font-size: 11px;
+}
+
+.wp-color-result-text {
+	background: #f6f7f7;
+	border-radius: 0 2px 2px 0;
+	border-left: 1px solid #c3c4c7;
+	color: #50575e;
+	display: block;
+	line-height: 2.54545455; /* 28px */
+	padding: 0 6px;
+	text-align: center;
+}
+
+.wp-color-result:hover,
+.wp-color-result:focus {
+	background: #f6f7f7;
+	border-color: #8c8f94;
+	color: #1d2327;
+}
+
+.wp-color-result:hover:after,
+.wp-color-result:focus:after {
+	color: #1d2327;
+	border-color: #a7aaad;
+	border-left: 1px solid #8c8f94;
+}
+
+.wp-picker-container {
+	display: inline-block;
+}
+
+.wp-color-result:focus {
+	border-color: #4f94d4;
+	box-shadow: 0 0 3px rgba(34, 113, 177, 0.8);
+}
+
+.wp-color-result:active {
+	/* See Trac ticket #39662 */
+	transform: none !important;
+}
+
+.wp-picker-open + .wp-picker-input-wrap {
+	display: inline-block;
+	vertical-align: top;
+}
+
+.wp-picker-input-wrap label {
+	display: inline-block;
+	vertical-align: top;
+}
+
+/* For the old `custom-background` page, to override the inline-block and margins from `.form-table td fieldset label`. */
+.form-table .wp-picker-input-wrap label {
+	margin: 0 !important;
+}
+
+.wp-picker-input-wrap .button.wp-picker-default,
+.wp-picker-input-wrap .button.wp-picker-clear,
+.wp-customizer .wp-picker-input-wrap .button.wp-picker-default,
+.wp-customizer .wp-picker-input-wrap .button.wp-picker-clear {
+	margin-left: 6px;
+	padding: 0 8px;
+	line-height: 2.54545455; /* 28px */
+	min-height: 30px;
+}
+
+.wp-picker-container .iris-square-slider .ui-slider-handle:focus {
+	background-color: #50575e
+}
+
+.wp-picker-container .iris-picker {
+	border-radius: 0;
+	border-color: #dcdcde;
+	margin-top: 6px;
+}
+
+.wp-picker-container input[type="text"].wp-color-picker {
+	width: 4rem;
+	font-size: 12px;
+	font-family: monospace;
+	line-height: 2.33333333; /* 28px */
+	margin: 0;
+	padding: 0 5px;
+	vertical-align: top;
+	min-height: 30px;
+}
+
+.wp-color-picker::-webkit-input-placeholder {
+	color: #646970;
+}
+
+.wp-color-picker::-moz-placeholder {
+	color: #646970;
+	opacity: 1;
+}
+
+.wp-color-picker:-ms-input-placeholder {
+	color: #646970;
+}
+
+.wp-picker-container input[type="text"].iris-error {
+	background-color: #fcf0f1;
+	border-color: #d63638;
+	color: #000;
+}
+
+.iris-picker .ui-square-handle:focus,
+.iris-picker .iris-strip .ui-slider-handle:focus {
+	border-color: #3582c4;
+	border-style: solid;
+	box-shadow: 0 0 0 1px #3582c4;
+	outline: 2px solid transparent;
+}
+
+.iris-picker .iris-palette:focus {
+	box-shadow: 0 0 0 2px #3582c4;
+}
+
+@media screen and (max-width: 782px) {
+	.wp-picker-container input[type="text"].wp-color-picker {
+		width: 5rem;
+		font-size: 16px;
+		line-height: 1.875; /* 30px */
+		min-height: 32px;
+	}
+
+	.wp-customizer .wp-picker-container input[type="text"].wp-color-picker {
+		padding: 0 5px;
+	}
+
+	.wp-picker-input-wrap .button.wp-picker-default,
+	.wp-picker-input-wrap .button.wp-picker-clear {
+		padding: 0 8px;
+		line-height: 2.14285714; /* 30px */
+		min-height: 32px;
+	}
+
+	.wp-customizer .wp-picker-input-wrap .button.wp-picker-default,
+	.wp-customizer .wp-picker-input-wrap .button.wp-picker-clear {
+		padding: 0 8px;
+		font-size: 14px;
+		line-height: 2.14285714; /* 30px */
+		min-height: 32px;
+	}
+
+	.wp-picker-container .wp-color-result.button {
+		padding: 0 0 0 40px;
+		font-size: 14px;
+		line-height: 2.14285714; /* 30px */
+	}
+
+	.wp-customizer .wp-picker-container .wp-color-result.button {
+		font-size: 14px;
+		line-height: 2.14285714; /* 30px */
+	}
+
+	.wp-picker-container .wp-color-result-text {
+		padding: 0 14px;
+		font-size: inherit;
+		line-height: inherit;
+	}
+
+	.wp-customizer .wp-picker-container .wp-color-result-text {
+		padding: 0 10px;
+	}
+}

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 0
usr/plugins/BearDocsCore/assets/css/color-picker.min.css


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
usr/plugins/BearDocsCore/assets/css/style-rtl.min.css


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 74 - 0
usr/plugins/BearDocsCore/assets/css/style.min.css


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 74 - 0
usr/plugins/BearDocsCore/assets/css/style.scss


BIN
usr/plugins/BearDocsCore/assets/fonts/dashicons.eot


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
usr/plugins/BearDocsCore/assets/fonts/dashicons.svg


BIN
usr/plugins/BearDocsCore/assets/fonts/dashicons.ttf


Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio