diff --git a/admin/common-js.php b/admin/common-js.php index 5ee4ba1a..812bee9c 100644 --- a/admin/common-js.php +++ b/admin/common-js.php @@ -92,7 +92,7 @@ $('a').each(function () { var t = $(this), href = t.attr('href'); - if ((href.length > 1 && href[0] == '#') + if ((href && href[0] == '#') || /^adminUrl, '/'); ?>.*$/.exec(href) || /^index), '/'), 0, -1); ?>action\/[_a-zA-Z0-9\/]+.*$/.exec(href)) { return; diff --git a/admin/css/style.css b/admin/css/style.css index c568cdf7..ade4c149 100644 --- a/admin/css/style.css +++ b/admin/css/style.css @@ -1,3 +1,4 @@ +@charset "UTF-8"; /* vim: set et sw=2 ts=2 sts=2 fdm=marker ff=unix fenc=utf8 */ /** * Typecho 后台样式 @@ -1407,112 +1408,177 @@ a.operate-reply { /** * icons */ -/* line 8, ../scss/_icons.scss */ +/* line 29, ../scss/_icons.scss */ .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; - background: url('../img/icons-sba2b1299ac.png') no-repeat; - text-indent: -9999em; } - /* line 13, ../scss/_icons.scss */ + text-indent: -9999em; + background-image: url('../img/icons-sba2b1299ac.png'); + background-repeat: no-repeat; } + /* line 35, ../scss/_icons.scss */ .i-edit:hover, .i-delete:hover, .i-exlink:hover, .mime-office:hover, .mime-text:hover, .mime-image:hover, .mime-html:hover, .mime-archive:hover, .mime-application:hover, .mime-audio:hover, .mime-script:hover, .mime-video:hover, .mime-unknow:hover, .i-upload:hover, .i-upload-active:hover { filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=75); opacity: 0.75; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 29, ../scss/_icons.scss */ + .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 { + -webkit-background-size: auto 256px; + -moz-background-size: auto 256px; + -o-background-size: auto 256px; + background-size: auto 256px; + background-image: url('../img/icons-2x-se223d6d340.png'); } } -/* line 18, ../scss/_icons.scss */ +/* line 47, ../scss/_icons.scss */ .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; } -/* line 24, ../scss/_icons.scss */ +/* line 53, ../scss/_icons.scss */ .i-upload, .i-upload-active { width: 24px; height: 24px; } -/* line 30, ../scss/_icons.scss */ +/* line 59, ../scss/_icons.scss */ .i-edit { background-position: 0 -80px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 59, ../scss/_icons.scss */ + .i-edit { + background-position: 0 -80px; } } -/* line 34, ../scss/_icons.scss */ +/* line 63, ../scss/_icons.scss */ .i-delete { background-position: 0 -64px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 63, ../scss/_icons.scss */ + .i-delete { + background-position: 0 -64px; } } -/* line 42, ../scss/_icons.scss */ +/* line 71, ../scss/_icons.scss */ .i-upload { background-position: 0 -24px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 71, ../scss/_icons.scss */ + .i-upload { + background-position: 0 -24px; } } -/* line 47, ../scss/_icons.scss */ +/* line 76, ../scss/_icons.scss */ .i-upload-active { background-position: 0 0; } -/* line 53, ../scss/_icons.scss */ +/* line 82, ../scss/_icons.scss */ .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; } -/* line 59, ../scss/_icons.scss */ +/* line 88, ../scss/_icons.scss */ .i-caret-down { border-color: #BBB transparent transparent transparent; border-width: 5px 4px 3px; } -/* line 63, ../scss/_icons.scss */ +/* line 92, ../scss/_icons.scss */ .i-caret-left { border-color: transparent #BBB transparent transparent; border-width: 4px 5px 4px 3px; } -/* line 67, ../scss/_icons.scss */ +/* line 96, ../scss/_icons.scss */ .i-caret-right { border-color: transparent transparent transparent #BBB; border-width: 4px 3px 4px 5px; } -/* line 72, ../scss/_icons.scss */ +/* line 101, ../scss/_icons.scss */ .i-exlink { background-position: 0 -48px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 101, ../scss/_icons.scss */ + .i-exlink { + background-position: 0 -48px; } } /* 文件类型图标 */ -/* line 80, ../scss/_icons.scss */ +/* line 109, ../scss/_icons.scss */ .mime-office { background-position: 0 -128px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 109, ../scss/_icons.scss */ + .mime-office { + background-position: 0 -112px; } } -/* line 85, ../scss/_icons.scss */ +/* line 114, ../scss/_icons.scss */ .mime-text { background-position: 0 -208px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 114, ../scss/_icons.scss */ + .mime-text { + background-position: 0 -208px; } } -/* line 90, ../scss/_icons.scss */ +/* line 119, ../scss/_icons.scss */ .mime-image { background-position: 0 -96px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 119, ../scss/_icons.scss */ + .mime-image { + background-position: 0 -96px; } } -/* line 95, ../scss/_icons.scss */ +/* line 124, ../scss/_icons.scss */ .mime-html { background-position: 0 -144px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 124, ../scss/_icons.scss */ + .mime-html { + background-position: 0 -144px; } } -/* line 100, ../scss/_icons.scss */ +/* line 129, ../scss/_icons.scss */ .mime-archive { background-position: 0 -224px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 129, ../scss/_icons.scss */ + .mime-archive { + background-position: 0 -224px; } } -/* line 105, ../scss/_icons.scss */ +/* line 134, ../scss/_icons.scss */ .mime-application { background-position: 0 -160px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 134, ../scss/_icons.scss */ + .mime-application { + background-position: 0 -176px; } } -/* line 110, ../scss/_icons.scss */ +/* line 139, ../scss/_icons.scss */ .mime-audio { background-position: 0 -240px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 139, ../scss/_icons.scss */ + .mime-audio { + background-position: 0 -240px; } } -/* line 115, ../scss/_icons.scss */ +/* line 144, ../scss/_icons.scss */ .mime-script { background-position: 0 -176px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 144, ../scss/_icons.scss */ + .mime-script { + background-position: 0 -160px; } } -/* line 120, ../scss/_icons.scss */ +/* line 149, ../scss/_icons.scss */ .mime-video { background-position: 0 -112px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 149, ../scss/_icons.scss */ + .mime-video { + background-position: 0 -128px; } } -/* line 125, ../scss/_icons.scss */ +/* line 154, ../scss/_icons.scss */ .mime-unknow { background-position: 0 -192px; } + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 154, ../scss/_icons.scss */ + .mime-unknow { + background-position: 0 -192px; } } /* Logo 图标 */ -/* line 132, ../scss/_icons.scss */ +/* line 161, ../scss/_icons.scss */ .i-logo, .i-logo-s { width: 169px; height: 40px; @@ -1525,12 +1591,12 @@ a.operate-reply { background-size: auto 40px; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=15); opacity: 0.15; } - /* line 140, ../scss/_icons.scss */ + /* line 169, ../scss/_icons.scss */ .i-logo:hover, .i-logo-s:hover { filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=20); opacity: 0.2; } -/* line 144, ../scss/_icons.scss */ +/* line 173, ../scss/_icons.scss */ .i-logo-s { width: 26px; height: 26px; @@ -1584,40 +1650,48 @@ a.operate-reply { height: 20px; background: transparent url(../img/editor.png) no-repeat; } -/* line 44, ../scss/components/_editor.scss */ +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + /* line 46, ../scss/components/_editor.scss */ + #wmd-button-row span { + background-image: url(../img/editor@2x.png); + -webkit-background-size: 320px auto; + -moz-background-size: 320px auto; + -o-background-size: 320px auto; + background-size: 320px auto; } } +/* line 53, ../scss/components/_editor.scss */ .wmd-edittab { float: right; margin-top: 3px; font-size: .92857em; } - /* line 48, ../scss/components/_editor.scss */ + /* line 57, ../scss/components/_editor.scss */ .wmd-edittab a { display: inline-block; padding: 0 8px; margin-left: 5px; height: 20px; line-height: 20px; } - /* line 54, ../scss/components/_editor.scss */ + /* line 63, ../scss/components/_editor.scss */ .wmd-edittab a:hover { text-decoration: none; } - /* line 57, ../scss/components/_editor.scss */ + /* line 66, ../scss/components/_editor.scss */ .wmd-edittab a.active { background: #E9E9E6; color: #999; } -/* line 65, ../scss/components/_editor.scss */ +/* line 74, ../scss/components/_editor.scss */ .wmd-hidetab { display: none; } -/* line 69, ../scss/components/_editor.scss */ +/* line 78, ../scss/components/_editor.scss */ .wmd-visualhide { visibility: hidden; } /* 对话框 */ -/* line 74, ../scss/components/_editor.scss */ +/* line 83, ../scss/components/_editor.scss */ .wmd-prompt-background { background-color: #000; } -/* line 77, ../scss/components/_editor.scss */ +/* line 86, ../scss/components/_editor.scss */ .wmd-prompt-dialog { position: fixed; z-index: 1001; @@ -1628,22 +1702,22 @@ a.operate-reply { padding: 20px; width: 360px; background: #F6F6F3; } - /* line 88, ../scss/components/_editor.scss */ + /* line 97, ../scss/components/_editor.scss */ .wmd-prompt-dialog p { margin: 0 0 5px; } - /* line 89, ../scss/components/_editor.scss */ + /* line 98, ../scss/components/_editor.scss */ .wmd-prompt-dialog form { margin-top: 10px; } - /* line 90, ../scss/components/_editor.scss */ + /* line 99, ../scss/components/_editor.scss */ .wmd-prompt-dialog input[type="text"] { margin-bottom: 10px; width: 100%; } - /* line 94, ../scss/components/_editor.scss */ + /* line 103, ../scss/components/_editor.scss */ .wmd-prompt-dialog button { margin-right: 10px; } /* 预览 */ -/* line 98, ../scss/components/_editor.scss */ +/* line 107, ../scss/components/_editor.scss */ #wmd-preview { background: #FFF; margin: 1em 0; @@ -1655,37 +1729,37 @@ a.operate-reply { -ms-border-radius: 2px; -o-border-radius: 2px; border-radius: 2px; } - /* line 105, ../scss/components/_editor.scss */ + /* line 114, ../scss/components/_editor.scss */ #wmd-preview img { max-width: 100%; } - /* line 106, ../scss/components/_editor.scss */ + /* line 115, ../scss/components/_editor.scss */ #wmd-preview code, #wmd-preview pre { padding: 2px 4px; background: #F3F3F0; font-size: .92857em; } - /* line 111, ../scss/components/_editor.scss */ + /* line 120, ../scss/components/_editor.scss */ #wmd-preview code { color: #C13; } - /* line 112, ../scss/components/_editor.scss */ + /* line 121, ../scss/components/_editor.scss */ #wmd-preview pre { padding: 1em; } - /* line 114, ../scss/components/_editor.scss */ + /* line 123, ../scss/components/_editor.scss */ #wmd-preview pre code { padding: 0; color: #444; } - /* line 119, ../scss/components/_editor.scss */ + /* line 128, ../scss/components/_editor.scss */ #wmd-preview blockquote { margin: 1em 1.5em; padding-left: 1.5em; border-left: 4px solid #E9E9E6; color: #777; } - /* line 125, ../scss/components/_editor.scss */ + /* line 134, ../scss/components/_editor.scss */ #wmd-preview hr { margin: 2em auto; width: 100px; border: 1px solid #E9E9E6; border-width: 2px 0 0 0; } - /* line 131, ../scss/components/_editor.scss */ + /* line 140, ../scss/components/_editor.scss */ #wmd-preview .summary:after { display: block; margin: 2em 0; @@ -1696,7 +1770,7 @@ a.operate-reply { content: "- more -"; } /* 编辑器全屏 */ -/* line 144, ../scss/components/_editor.scss */ +/* line 153, ../scss/components/_editor.scss */ .fullscreen #wmd-button-bar, .fullscreen #text, .fullscreen #wmd-preview, .fullscreen .submit { position: absolute; top: 0; @@ -1711,20 +1785,20 @@ a.operate-reply { -ms-border-radius: 0; -o-border-radius: 0; border-radius: 0; } -/* line 154, ../scss/components/_editor.scss */ +/* line 163, ../scss/components/_editor.scss */ .fullscreen #wmd-button-bar { left: 0; padding: 13px 20px; border-bottom: 1px solid #F3F3F0; z-index: 1000; } -/* line 160, ../scss/components/_editor.scss */ +/* line 169, ../scss/components/_editor.scss */ .fullscreen #text { top: 53px; left: 0; padding: 20px; border: none; outline: none; } -/* line 167, ../scss/components/_editor.scss */ +/* line 176, ../scss/components/_editor.scss */ .fullscreen #wmd-preview { top: 53px; right: 0; @@ -1734,23 +1808,23 @@ a.operate-reply { border-left: 1px solid #F3F3F0; background: #F6F6F3; overflow: auto; } - /* line 176, ../scss/components/_editor.scss */ + /* line 185, ../scss/components/_editor.scss */ .fullscreen #wmd-preview code, .fullscreen #wmd-preview pre { background: #F0F0EC; } -/* line 180, ../scss/components/_editor.scss */ +/* line 189, ../scss/components/_editor.scss */ .fullscreen .submit { right: 0; margin: 0; padding: 10px 20px; border-bottom: 1px solid #F3F3F0; } -/* line 188, ../scss/components/_editor.scss */ +/* line 197, ../scss/components/_editor.scss */ .fullscreen #tab-files { position: absolute; top: 52px; right: 20px; width: 280px; z-index: 1001; } -/* line 202, ../scss/components/_editor.scss */ +/* line 211, ../scss/components/_editor.scss */ .fullscreen .wmd-edittab, .fullscreen .typecho-post-option, .fullscreen .title, @@ -1759,10 +1833,10 @@ a.operate-reply { .fullscreen .typecho-head-nav, .fullscreen .message { display: none; } -/* line 203, ../scss/components/_editor.scss */ +/* line 212, ../scss/components/_editor.scss */ .fullscreen .wmd-hidetab { display: block; } -/* line 205, ../scss/components/_editor.scss */ +/* line 214, ../scss/components/_editor.scss */ .fullscreen .wmd-visualhide, .fullscreen #btn-fullscreen-upload { visibility: visible; } diff --git a/admin/img/editor.png b/admin/img/editor.png index d905db6d..9bc092de 100644 Binary files a/admin/img/editor.png and b/admin/img/editor.png differ diff --git a/admin/img/editor@2x.png b/admin/img/editor@2x.png new file mode 100644 index 00000000..06d061c1 Binary files /dev/null and b/admin/img/editor@2x.png differ diff --git a/admin/img/icons-2x-se223d6d340.png b/admin/img/icons-2x-se223d6d340.png new file mode 100644 index 00000000..71b1f879 Binary files /dev/null and b/admin/img/icons-2x-se223d6d340.png differ diff --git a/admin/img/icons-2x/icon-delete-2x.png b/admin/img/icons-2x/icon-delete.png similarity index 100% rename from admin/img/icons-2x/icon-delete-2x.png rename to admin/img/icons-2x/icon-delete.png diff --git a/admin/img/icons-2x/icon-edit-2x.png b/admin/img/icons-2x/icon-edit.png similarity index 100% rename from admin/img/icons-2x/icon-edit-2x.png rename to admin/img/icons-2x/icon-edit.png diff --git a/admin/img/icons-2x/icon-exlink-2x.png b/admin/img/icons-2x/icon-exlink.png similarity index 100% rename from admin/img/icons-2x/icon-exlink-2x.png rename to admin/img/icons-2x/icon-exlink.png diff --git a/admin/img/icons-2x/icon-upload-active-2x.png b/admin/img/icons-2x/icon-upload-active.png similarity index 100% rename from admin/img/icons-2x/icon-upload-active-2x.png rename to admin/img/icons-2x/icon-upload-active.png diff --git a/admin/img/icons-2x/icon-upload-2x.png b/admin/img/icons-2x/icon-upload.png similarity index 100% rename from admin/img/icons-2x/icon-upload-2x.png rename to admin/img/icons-2x/icon-upload.png diff --git a/admin/img/icons-2x/mime-application-2x.png b/admin/img/icons-2x/mime-application.png similarity index 100% rename from admin/img/icons-2x/mime-application-2x.png rename to admin/img/icons-2x/mime-application.png diff --git a/admin/img/icons-2x/mime-archive-2x.png b/admin/img/icons-2x/mime-archive.png similarity index 100% rename from admin/img/icons-2x/mime-archive-2x.png rename to admin/img/icons-2x/mime-archive.png diff --git a/admin/img/icons-2x/mime-audio-2x.png b/admin/img/icons-2x/mime-audio.png similarity index 100% rename from admin/img/icons-2x/mime-audio-2x.png rename to admin/img/icons-2x/mime-audio.png diff --git a/admin/img/icons-2x/mime-html-2x.png b/admin/img/icons-2x/mime-html.png similarity index 100% rename from admin/img/icons-2x/mime-html-2x.png rename to admin/img/icons-2x/mime-html.png diff --git a/admin/img/icons-2x/mime-image-2x.png b/admin/img/icons-2x/mime-image.png similarity index 100% rename from admin/img/icons-2x/mime-image-2x.png rename to admin/img/icons-2x/mime-image.png diff --git a/admin/img/icons-2x/mime-office-2x.png b/admin/img/icons-2x/mime-office.png similarity index 100% rename from admin/img/icons-2x/mime-office-2x.png rename to admin/img/icons-2x/mime-office.png diff --git a/admin/img/icons-2x/mime-script-2x.png b/admin/img/icons-2x/mime-script.png similarity index 100% rename from admin/img/icons-2x/mime-script-2x.png rename to admin/img/icons-2x/mime-script.png diff --git a/admin/img/icons-2x/mime-text-2x.png b/admin/img/icons-2x/mime-text.png similarity index 100% rename from admin/img/icons-2x/mime-text-2x.png rename to admin/img/icons-2x/mime-text.png diff --git a/admin/img/icons-2x/mime-unknow-2x.png b/admin/img/icons-2x/mime-unknow.png similarity index 100% rename from admin/img/icons-2x/mime-unknow-2x.png rename to admin/img/icons-2x/mime-unknow.png diff --git a/admin/img/icons-2x/mime-video-2x.png b/admin/img/icons-2x/mime-video.png similarity index 100% rename from admin/img/icons-2x/mime-video-2x.png rename to admin/img/icons-2x/mime-video.png diff --git a/admin/js/pagedown.js b/admin/js/pagedown.js index 2c96211b..ecd60010 100644 --- a/admin/js/pagedown.js +++ b/admin/js/pagedown.js @@ -1296,7 +1296,10 @@ else // autolink anything like - var replacer = function (wholematch, m1) { return "" + pluginHooks.plainLinkText(m1) + ""; } + var replacer = function (wholematch, m1) { + var html = "" + pluginHooks.plainLinkText(m1) + ""; + return "~K" + (g_html_blocks.push(html) - 1) + "K"; + } text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer); // Email addresses: diff --git a/admin/scss/_icons.scss b/admin/scss/_icons.scss index 83beeff8..1992149d 100644 --- a/admin/scss/_icons.scss +++ b/admin/scss/_icons.scss @@ -1,18 +1,47 @@ /** * icons */ +$sprites: sprite-map("icons/*.png"); +$sprites-retina: sprite-map("icons-2x/*.png"); + +@mixin sprite-background($name) { + // background-image: sprite-url($sprites); + background-position: sprite-position($sprites, $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) { + // 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); + } +} -$icons: sprite-map("icons/*.png"); -$icons-2x: sprite-map("icons-2x/*.png"); %i-base { display: inline-block; vertical-align: text-bottom; - background: $icons no-repeat; text-indent: -9999em; + background-image: sprite-url($sprites); + background-repeat: no-repeat; &:hover { @include opacity(0.75); } + @media + (-webkit-min-device-pixel-ratio: 2), + (min-resolution: 192dpi) { + // 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-16 { @@ -29,11 +58,11 @@ $icons-2x: sprite-map("icons-2x/*.png"); .i-edit { @extend %i-16; - background-position: sprite-position($icons, icon-edit); + @include sprite-background(icon-edit); } .i-delete { @extend %i-16; - background-position: sprite-position($icons, icon-delete); + @include sprite-background(icon-delete); } @@ -41,12 +70,12 @@ $icons-2x: sprite-map("icons-2x/*.png"); .i-upload { @extend %i-24; - background-position: sprite-position($icons, icon-upload); + @include sprite-background(icon-upload); } .i-upload-active { @extend %i-24; - background-position: sprite-position($icons, icon-upload-active); + @include sprite-background(icon-upload-active); } // 小箭头 @@ -71,7 +100,7 @@ $icons-2x: sprite-map("icons-2x/*.png"); .i-exlink { @extend %i-16; - background-position: sprite-position($icons, icon-exlink); + @include sprite-background(icon-exlink); } @@ -79,52 +108,52 @@ $icons-2x: sprite-map("icons-2x/*.png"); .mime-office { @extend %i-16; - background-position: sprite-position($icons, mime-office); + @include sprite-background(mime-office); } .mime-text { @extend %i-16; - background-position: sprite-position($icons, mime-text); + @include sprite-background(mime-text); } .mime-image { @extend %i-16; - background-position: sprite-position($icons, mime-image); + @include sprite-background(mime-image); } .mime-html { @extend %i-16; - background-position: sprite-position($icons, mime-html); + @include sprite-background(mime-html); } .mime-archive { @extend %i-16; - background-position: sprite-position($icons, mime-archive); + @include sprite-background(mime-archive); } .mime-application { @extend %i-16; - background-position: sprite-position($icons, mime-application); + @include sprite-background(mime-application); } .mime-audio { @extend %i-16; - background-position: sprite-position($icons, mime-audio); + @include sprite-background(mime-audio); } .mime-script { @extend %i-16; - background-position: sprite-position($icons, mime-script); + @include sprite-background(mime-script); } .mime-video { @extend %i-16; - background-position: sprite-position($icons, mime-video); + @include sprite-background(mime-video); } .mime-unknow { @extend %i-16; - background-position: sprite-position($icons, mime-unknow); + @include sprite-background(mime-unknow); } diff --git a/admin/scss/components/_editor.scss b/admin/scss/components/_editor.scss index 01f775b4..2d9b68f4 100644 --- a/admin/scss/components/_editor.scss +++ b/admin/scss/components/_editor.scss @@ -40,6 +40,15 @@ background: transparent url(../img/editor.png) no-repeat; } +@media +(-webkit-min-device-pixel-ratio: 2), +(min-resolution: 192dpi) { + #wmd-button-row span { + background-image: url(../img/editor@2x.png); + @include background-size(320px auto); + } +} + // 撰写预览切换 tab .wmd-edittab { float: right; diff --git a/install.php b/install.php index 6d426266..a666b176 100644 --- a/install.php +++ b/install.php @@ -60,6 +60,18 @@ if (!isset($_GET['finish']) && file_exists(__TYPECHO_ROOT_DIR__ . '/config.inc.p exit; } +// 挡掉可能的跨站请求 +if (!empty($_GET) || !empty($_POST)) { + if (empty($_SERVER['HTTP_REFERER')) { + exit; + } + + $parts = parse_url($_SERVER); + if (empty($parts['host']) || $_SERVER['HTTP_HOST'] != $parts['host']) { + exit; + } +} + /** * 获取传递参数 * @@ -68,7 +80,8 @@ if (!isset($_GET['finish']) && file_exists(__TYPECHO_ROOT_DIR__ . '/config.inc.p * @return string */ function _r($name, $default = NULL) { - return isset($_REQUEST[$name]) ? $_REQUEST[$name] : $default; + return isset($_REQUEST[$name]) ? + (is_array($_REQUEST[$name]) ? $default : $_REQUEST[$name]) : $default; } /** @@ -81,7 +94,8 @@ function _rFrom() { $params = func_get_args(); foreach ($params as $param) { - $result[$param] = isset($_REQUEST[$param]) ? $_REQUEST[$param] : NULL; + $result[$param] = isset($_REQUEST[$param]) ? + (is_array($_REQUEST[$param]) ? NULL : $_REQUEST[$param]) : NULL; } return $result; @@ -203,6 +217,7 @@ list($prefixVersion, $suffixVersion) = explode('/', $currentVersion); if (isset($_REQUEST['user']) && isset($_REQUEST['password'])) { $loginUrl = _u() . '/index.php/action/login?name=' . urlencode(_r('user')) . '&password=' . urlencode(_r('password')) . '&referer=' . _u() . '/admin/index.php'; + $loginUrl = Typecho_Widget::widget('Widget_Security')->getTokenUrl($loginUrl); } else { $loginUrl = _u() . '/admin/index.php'; } @@ -305,6 +320,7 @@ list($prefixVersion, $suffixVersion) = explode('/', $currentVersion); $installDb->query($installDb->insert('table.options')->rows(array('name' => 'actionTable', 'user' => 0, 'value' => 'a:0:{}'))); $installDb->query($installDb->insert('table.options')->rows(array('name' => 'panelTable', 'user' => 0, 'value' => 'a:0:{}'))); $installDb->query($installDb->insert('table.options')->rows(array('name' => 'attachmentTypes', 'user' => 0, 'value' => '@image@'))); + $installDb->query($installDb->insert('table.options')->rows(array('name' => 'secret', 'user' => 0, 'value' => Typecho_Common::randString(32, true)))); /** 初始分类 */ $installDb->query($installDb->insert('table.metas')->rows(array('name' => _t('默认分类'), 'slug' => 'default', 'type' => 'category', 'description' => _t('只是一个默认分类'), diff --git a/usr/themes/default/img/icon-search.png b/usr/themes/default/img/icon-search.png index c437e363..c0b7599c 100644 Binary files a/usr/themes/default/img/icon-search.png and b/usr/themes/default/img/icon-search.png differ diff --git a/usr/themes/default/img/icon-search@2x.png b/usr/themes/default/img/icon-search@2x.png new file mode 100644 index 00000000..5f9ea169 Binary files /dev/null and b/usr/themes/default/img/icon-search@2x.png differ diff --git a/usr/themes/default/style.css b/usr/themes/default/style.css index 01b84bfc..020c8ede 100644 --- a/usr/themes/default/style.css +++ b/usr/themes/default/style.css @@ -158,6 +158,18 @@ textarea { text-indent: -9999em; } +@media +(-webkit-min-device-pixel-ratio: 2), +(min-resolution: 192dpi) { + #search button { + background-image: url(img/icon-search@2x.png); + -webkit-background-size: 24px 24px; + -moz-background-size: 24px 24px; + -o-background-size: 24px 24px; + background-size: 24px 24px; + } +} + /* ------------------ * Main diff --git a/var/Helper.php b/var/Helper.php index f9073d6a..d1079bc5 100644 --- a/var/Helper.php +++ b/var/Helper.php @@ -21,6 +21,16 @@ class Helper return Typecho_Widget::widget('Widget_Options'); } + /** + * 获取Widget_Security对象 + * + * @return Widget_Security + */ + public static function security() + { + return Typecho_Widget::widget('Widget_Security'); + } + /** * 强行删除某个插件 * @@ -95,7 +105,7 @@ class Helper * @param string $widget 组件名称 * @param string $action 组件动作 * @param string $after 在某个路由后面 - * @return void + * @return integer */ public static function addRoute($name, $url, $widget, $action = NULL, $after = NULL) { @@ -173,8 +183,8 @@ class Helper * 删除action扩展 * * @access public - * @param unknown $actionName - * @return unknown + * @param string $actionName + * @return Typecho_Widget */ public static function removeAction($actionName) { @@ -245,7 +255,7 @@ class Helper * @param string $subTitle 面板副标题 * @param string $level 进入权限 * @param boolean $hidden 是否隐藏 - * @param boolean $addLink 新增项目链接, 会显示在页面标题之后 + * @param string $addLink 新增项目链接, 会显示在页面标题之后 * @return integer */ public static function addPanel($index, $fileName, $title, $subTitle, $level, $hidden = false, $addLink = '') @@ -306,7 +316,8 @@ class Helper * 获取面板url * * @access public - * @return unknown + * @param string $fileName + * @return string */ public static function url($fileName) { @@ -319,7 +330,7 @@ class Helper * @access public * @static * @param mixed $pluginName 插件名称 - * @param mixed array $settings 变量键值对 + * @param array $settings 变量键值对 * @param bool $isPersonal. (default: false) 是否为私人变量 * @return void */ diff --git a/var/Typecho/Common.php b/var/Typecho/Common.php index 76706401..3642f327 100644 --- a/var/Typecho/Common.php +++ b/var/Typecho/Common.php @@ -9,6 +9,8 @@ * @version $Id$ */ +define('__TYPECHO_MB_SUPPORTED__', function_exists('mb_get_info')); + /** * Typecho公用方法 * @@ -20,7 +22,7 @@ class Typecho_Common { /** 程序版本 */ - const VERSION = '0.9/14.2.24'; + const VERSION = '0.9/14.3.14'; /** * 锁定的代码块 @@ -103,7 +105,7 @@ class Typecho_Common * @param mixed $matches * @static * @access public - * @return void + * @return bool */ public static function __filterAttrs($matches) { @@ -242,20 +244,9 @@ class Typecho_Common */ public static function exceptionHandle(Exception $exception) { - //$obHandles = ob_list_handlers(); - @ob_end_clean(); - /* - if (in_array('ob_gzhandler', $obHandles)) { - ob_start('ob_gzhandler'); - } else { - ob_start(); - } - */ - if (defined('__TYPECHO_DEBUG__')) { - //@ob_clean(); echo nl2br($exception->__toString()); } else { if (404 == $exception->getCode() && !empty(self::$exceptionHandle)) { @@ -279,6 +270,7 @@ class Typecho_Common public static function error($exception) { $isException = is_object($exception); + $message = ''; if ($isException) { $code = $exception->getCode(); @@ -643,12 +635,12 @@ EOF; { //~ 针对location的xss过滤, 因为其特殊性无法使用removeXSS函数 //~ fix issue 66 - $params = parse_url(str_replace(array("\r", "\n"), '', $url)); + $params = parse_url(str_replace(array("\r", "\n", "\t", ' '), '', $url)); /** 禁止非法的协议跳转 */ if (isset($params['scheme'])) { if (!in_array($params['scheme'], array('http', 'https'))) { - return; + return '/'; } } @@ -743,7 +735,7 @@ EOF; $iLength = self::strLen($str) - $start; $tLength = $length < $iLength ? ($length - self::strLen($trim)) : $length; - if (function_exists('mb_get_info')) { + if (__TYPECHO_MB_SUPPORTED__) { $str = mb_substr($str, $start, $tLength, self::$charset); } else { if ('UTF-8' == strtoupper(self::$charset)) { @@ -767,7 +759,7 @@ EOF; */ public static function strLen($str) { - if (function_exists('mb_get_info')) { + if (__TYPECHO_MB_SUPPORTED__) { return mb_strlen($str, self::$charset); } else { return 'UTF-8' == strtoupper(self::$charset) @@ -775,6 +767,26 @@ EOF; } } + /** + * 检查是否为合法的编码数据 + * + * @param string|array $str + * @return boolean + */ + public static function checkStrEncoding($str) + { + if (is_array($str)) { + return array_map(array('Typecho_Common', 'checkStrEncoding'), $str); + } + + if (__TYPECHO_MB_SUPPORTED__) { + return mb_check_encoding($str, self::$charset); + } else { + // just support utf-8 + return preg_match('//u', $str); + } + } + /** * 生成缩略名 * @@ -792,7 +804,7 @@ EOF; return $default; } - if (function_exists('mb_regex_encoding')) { + if (__TYPECHO_MB_SUPPORTED__) { mb_regex_encoding(self::$charset); mb_ereg_search_init($str, "[\w" . preg_quote('_-') . "]+"); $result = mb_ereg_search(); @@ -892,7 +904,7 @@ EOF; * * @access public * @param integer $length 字符串长度 - * @param string $specialChars 是否有特殊字符 + * @param boolean $specialChars 是否有特殊字符 * @return string */ public static function randString($length, $specialChars = false) @@ -955,9 +967,9 @@ EOF; { if ('$T$' == substr($to, 0, 3)) { $salt = substr($to, 3, 9); - return self::hash($from, $salt) == $to; + return self::hash($from, $salt) === $to; } else { - return md5($from) == $to; + return md5($from) === $to; } } diff --git a/var/Typecho/Cookie.php b/var/Typecho/Cookie.php index d6284120..78aaf849 100644 --- a/var/Typecho/Cookie.php +++ b/var/Typecho/Cookie.php @@ -54,7 +54,7 @@ class Typecho_Cookie * 获取前缀 * * @access public - * @return void + * @return string */ public static function getPrefix() { @@ -73,7 +73,7 @@ class Typecho_Cookie { $key = self::$_prefix . $key; $value = isset($_COOKIE[$key]) ? $_COOKIE[$key] : (isset($_POST[$key]) ? $_POST[$key] : $default); - return $value; + return is_array($value) ? $default : $value; } /** @@ -88,16 +88,7 @@ class Typecho_Cookie public static function set($key, $value, $expire = 0) { $key = self::$_prefix . $key; - - /** 对数组型COOKIE的写入支持 */ - if (is_array($value)) { - foreach ($value as $name => $val) { - setrawcookie("{$key}[{$name}]", rawurlencode($val), $expire, self::$_path); - } - } else { - setrawcookie($key, rawurlencode($value), $expire, self::$_path); - } - + setrawcookie($key, rawurlencode($value), $expire, self::$_path); $_COOKIE[$key] = $value; } @@ -115,15 +106,7 @@ class Typecho_Cookie return; } - /** 对数组型COOKIE的删除支持 */ - if (is_array($_COOKIE[$key])) { - foreach ($_COOKIE[$key] as $name => $val) { - setcookie("{$key}[{$name}]", '', time() - 2592000, self::$_path); - } - } else { - setcookie($key, '', time() - 2592000, self::$_path); - } - + setcookie($key, '', time() - 2592000, self::$_path); unset($_COOKIE[$key]); } } diff --git a/var/Typecho/I18n/GetText.php b/var/Typecho/I18n/GetText.php index 36aa5498..2b45503a 100644 --- a/var/Typecho/I18n/GetText.php +++ b/var/Typecho/I18n/GetText.php @@ -162,7 +162,7 @@ class Typecho_I18n_GetText $this->table_translations = $this->readintarray($this->total * 2); if ($this->enable_cache) { - $this->cache_translations = array (); + $this->cache_translations = array ('' => NULL); /* read all strings in the cache */ for ($i = 0; $i < $this->total; $i++) { if ($this->table_originals[$i * 2 + 1] > 0) { @@ -303,7 +303,7 @@ class Typecho_I18n_GetText } else { $header = $this->get_translation_string(0); } - if (eregi("plural-forms: ([^\n]*)\n", $header, $regs)) + if (preg_match("/plural\-forms: ([^\n]*)\n/i", $header, $regs)) $expr = $regs[1]; else $expr = "nplurals=2; plural=n == 1 ? 0 : 1;"; diff --git a/var/Typecho/Request.php b/var/Typecho/Request.php index 0d702489..ecd66743 100644 --- a/var/Typecho/Request.php +++ b/var/Typecho/Request.php @@ -7,10 +7,11 @@ * @version $Id$ */ +define('__TYPECHO_FILTER_SUPPORTED__', function_exists('filter_var')); + /** * 服务器请求处理类 * - * TODO getSiteUrl * @package Request */ class Typecho_Request @@ -95,6 +96,13 @@ class Typecho_Request */ private static $_instance = NULL; + /** + * 全部的http数据 + * + * @var bool|array + */ + private static $_httpParams = false; + /** * 当前过滤器 * @@ -146,9 +154,10 @@ class Typecho_Request $value = is_array($value) ? array_map($filter, $value) : call_user_func($filter, $value); } + + $this->_filter = array(); } - $this->_filter = array(); return $value; } @@ -160,9 +169,9 @@ class Typecho_Request */ private function _checkIp($ip) { - if (function_exists('filter_var')) { - return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) - || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); + if (__TYPECHO_FILTER_SUPPORTED__) { + return false !== (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) + || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); } return preg_match("/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $ip) @@ -180,6 +189,17 @@ class Typecho_Request return preg_match("/^[_a-z0-9- ,:;=#@\.\(\)\/\+\*\?]+$/i", $agent); } + /** + * 初始化变量 + */ + public function __construct() + { + if (false === self::$_httpParams) { + self::$_httpParams = array_filter(array_merge($_POST, $_GET), + array('Typecho_Common', 'checkStrEncoding')); + } + } + /** * 设置过滤器 * @@ -219,9 +239,8 @@ class Typecho_Request */ public function __isset($key) { - return isset($_GET[$key]) - || isset($_POST[$key]) - || $this->isSetParam($key); + return isset(self::$_httpParams[$key]) + || isset($this->_params[$key]); } /** @@ -238,19 +257,31 @@ class Typecho_Request case isset($this->_params[$key]): $value = $this->_params[$key]; break; - case isset($_GET[$key]): - $value = $_GET[$key]; - break; - case isset($_POST[$key]): - $value = $_POST[$key]; + case isset(self::$_httpParams[$key]): + $value = self::$_httpParams[$key]; break; default: $value = $default; break; } - $value = is_array($value) || strlen($value) > 0 ? $value : $default; - return $this->_filter ? $this->_applyFilter($value) : $value; + $value = !is_array($value) && strlen($value) > 0 ? $value : $default; + return $this->_applyFilter($value); + } + + /** + * 获取一个数组 + * + * @param $key + * @return array + */ + public function getArray($key) + { + $result = isset(self::$_httpParams[$key]) ? self::$_httpParams[$key] : array(); + + $result = is_array($result) ? $result + : (strlen($result) > 0 ? array($result) : array()); + return $this->_applyFilter($result); } /** @@ -272,21 +303,6 @@ class Typecho_Request return $result; } - /** - * 获取指定的http传递参数 - * - * @access public - * @param string $key 指定的参数 - * @param mixed $default 默认的参数 - * @return mixed - */ - public function getParam($key, $default = NULL) - { - $value = isset($this->_params[$key]) ? $this->_params[$key] : $default; - $value = is_array($value) || strlen($value) > 0 ? $value : $default; - return $this->_filter ? $this->_applyFilter($value) : $value; - } - /** * 设置http传递参数 * @@ -297,31 +313,9 @@ class Typecho_Request */ public function setParam($name, $value) { - $this->_params[$name] = $value; - } - - /** - * 删除参数 - * - * @access public - * @param string $name 指定的参数 - * @return void - */ - public function unSetParam($name) - { - unset($this->_params[$name]); - } - - /** - * 参数是否存在 - * - * @access public - * @param string $key 指定的参数 - * @return boolean - */ - public function isSetParam($key) - { - return isset($this->_params[$key]); + if (Typecho_Common::checkStrEncoding($value)) { + $this->_params[$name] = $value; + } } /** @@ -339,7 +333,8 @@ class Typecho_Request $params = $out; } - $this->_params = array_merge($this->_params, $params); + $this->_params = array_merge($this->_params, + array_filter($params, array('Typecho_Common', 'checkStrEncoding'))); } /** diff --git a/var/Typecho/Response.php b/var/Typecho/Response.php index 540f9391..d84ec613 100644 --- a/var/Typecho/Response.php +++ b/var/Typecho/Response.php @@ -142,7 +142,7 @@ class Typecho_Response * 获取字符集 * * @access public - * @return void + * @return string */ public function getCharset() { @@ -258,9 +258,8 @@ class Typecho_Response * 返回来路 * * @access public - * @param string $anchor 附加地址 + * @param string $suffix 附加地址 * @param string $default 默认来路 - * @return void */ public function goBack($suffix = NULL, $default = NULL) { diff --git a/var/Typecho/Validate.php b/var/Typecho/Validate.php index 8bf2dc02..e8620ffe 100644 --- a/var/Typecho/Validate.php +++ b/var/Typecho/Validate.php @@ -144,7 +144,7 @@ class Typecho_Validate * @param integer $length 最小长度 * @return boolean */ - public function minLength($str, $length) + public static function minLength($str, $length) { return (Typecho_Common::strLen($str) >= $length); } @@ -182,7 +182,7 @@ class Typecho_Validate * @param array $params 枚举值 * @return unknown */ - public function enum($str, array $params) + public static function enum($str, array $params) { $keys = array_flip($params); return isset($keys[$str]); @@ -191,11 +191,11 @@ class Typecho_Validate /** * Max Length * - * @access public - * @param string - * @return boolean + * @param $str + * @param $length + * @return bool */ - public function maxLength($str, $length) + public static function maxLength($str, $length) { return (Typecho_Common::strLen($str) < $length); } @@ -207,9 +207,9 @@ class Typecho_Validate * @param string * @return boolean */ - public function email($str) + public static function email($str) { - return preg_match("/^[^@\s<&>]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $str); + return preg_match("/^[_a-z0-9-\.]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $str); } /** @@ -219,7 +219,7 @@ class Typecho_Validate * @param string $str * @return boolean */ - public function url($str) + public static function url($str) { $parts = @parse_url($str); if (!$parts) { @@ -238,7 +238,7 @@ class Typecho_Validate * @param string * @return boolean */ - public function alpha($str) + public static function alpha($str) { return preg_match("/^([a-z])+$/i", $str) ? true : false; } @@ -250,7 +250,7 @@ class Typecho_Validate * @param string * @return boolean */ - public function alphaNumeric($str) + public static function alphaNumeric($str) { return preg_match("/^([a-z0-9])+$/i", $str); } @@ -262,7 +262,7 @@ class Typecho_Validate * @param string * @return boolean */ - public function alphaDash($str) + public static function alphaDash($str) { return preg_match("/^([_a-z0-9-])+$/i", $str) ? true : false; } @@ -274,7 +274,7 @@ class Typecho_Validate * @param string $str * @return boolean */ - public function xssCheck($str) + public static function xssCheck($str) { $search = 'abcdefghijklmnopqrstuvwxyz'; $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; @@ -291,7 +291,7 @@ class Typecho_Validate $str = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $str); // with a ; } - return !preg_match('/(\(|\)|\\\|"|<|>|[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19])/', $str); + return !preg_match('/(\(|\)|\\\|"|<|>|[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19]|' . "\r|\n|\t" . ')/', $str); } /** @@ -301,9 +301,9 @@ class Typecho_Validate * @param integer * @return boolean */ - public function isFloat($str) + public static function isFloat($str) { - return ereg("^[0-9\.]+$", $str); + return preg_match("/^[0-9\.]+$/", $str); } /** @@ -313,7 +313,7 @@ class Typecho_Validate * @param string * @return boolean */ - public function isInteger($str) + public static function isInteger($str) { return is_numeric($str); } diff --git a/var/Typecho/Widget/Helper/Form/Element/Hidden.php b/var/Typecho/Widget/Helper/Form/Element/Hidden.php index 382d0bb1..c3c92149 100644 --- a/var/Typecho/Widget/Helper/Form/Element/Hidden.php +++ b/var/Typecho/Widget/Helper/Form/Element/Hidden.php @@ -57,6 +57,6 @@ class Typecho_Widget_Helper_Form_Element_Hidden extends Typecho_Widget_Helper_Fo */ protected function _value($value) { - $this->input->setAttribute('value', $value); + $this->input->setAttribute('value', htmlspecialchars($value)); } } diff --git a/var/Typecho/Widget/Helper/Form/Element/Password.php b/var/Typecho/Widget/Helper/Form/Element/Password.php index 21481b78..347a540e 100644 --- a/var/Typecho/Widget/Helper/Form/Element/Password.php +++ b/var/Typecho/Widget/Helper/Form/Element/Password.php @@ -47,6 +47,6 @@ class Typecho_Widget_Helper_Form_Element_Password extends Typecho_Widget_Helper_ */ protected function _value($value) { - $this->input->setAttribute('value', $value); + $this->input->setAttribute('value', htmlspecialchars($value)); } } diff --git a/var/Typecho/Widget/Helper/Form/Element/Text.php b/var/Typecho/Widget/Helper/Form/Element/Text.php index 390d3efc..78d1939c 100644 --- a/var/Typecho/Widget/Helper/Form/Element/Text.php +++ b/var/Typecho/Widget/Helper/Form/Element/Text.php @@ -48,6 +48,6 @@ class Typecho_Widget_Helper_Form_Element_Text extends Typecho_Widget_Helper_Form */ protected function _value($value) { - $this->input->setAttribute('value', $value); + $this->input->setAttribute('value', htmlspecialchars($value)); } } diff --git a/var/Typecho/Widget/Helper/Form/Element/Textarea.php b/var/Typecho/Widget/Helper/Form/Element/Textarea.php index b984bdc1..ec5adacd 100644 --- a/var/Typecho/Widget/Helper/Form/Element/Textarea.php +++ b/var/Typecho/Widget/Helper/Form/Element/Textarea.php @@ -47,6 +47,6 @@ class Typecho_Widget_Helper_Form_Element_Textarea extends Typecho_Widget_Helper_ */ protected function _value($value) { - $this->input->html($value); + $this->input->html(htmlspecialchars($value)); } } diff --git a/var/Upgrade.php b/var/Upgrade.php index a09dc4e2..aea5d788 100644 --- a/var/Upgrade.php +++ b/var/Upgrade.php @@ -1161,5 +1161,19 @@ Typecho_Date::setTimezoneOffset($options->timezone); break; } } -} + + /** + * v0_9r14_3_14 + * + * @param mixed $db + * @param mixed $options + * @access public + * @return void + */ + public function v0_9r14_3_14($db, $options) + { + $db->query($db->insert('table.options') + ->rows(array('name' => 'secret', 'user' => 0, 'value' => Typecho_Common::randString(32, true)))); + } +} diff --git a/var/Widget/Abstract/Contents.php b/var/Widget/Abstract/Contents.php index cd962dbb..6a377725 100644 --- a/var/Widget/Abstract/Contents.php +++ b/var/Widget/Abstract/Contents.php @@ -734,7 +734,7 @@ class Widget_Abstract_Contents extends Widget_Abstract /** 处理密码保护流程 */ if (!empty($value['password']) && - $value['password'] != $this->request->protectPassword && + $value['password'] !== Typecho_Cookie::get('protectPassword') && $value['authorId'] != $this->user->uid && !$this->user->pass('editor', true)) { $value['hidden'] = true; @@ -749,7 +749,8 @@ class Widget_Abstract_Contents extends Widget_Abstract /** 如果访问权限被禁止 */ if ($value['hidden']) { - $value['text'] = '
' . + $value['text'] = '' . '

' . _t('请输入密码访问') . '

' . '

' . diff --git a/var/Widget/Archive.php b/var/Widget/Archive.php index 0de43aae..8f85be79 100644 --- a/var/Widget/Archive.php +++ b/var/Widget/Archive.php @@ -292,7 +292,7 @@ class Widget_Archive extends Widget_Abstract_Contents * 评论地址 * * @access protected - * @return void + * @return string */ protected function ___commentUrl() { @@ -306,7 +306,7 @@ class Widget_Archive extends Widget_Abstract_Contents $commentUrl .= '?parent=' . $reply; } - return $commentUrl; + return $this->security->getTokenUrl($commentUrl); } /** @@ -320,7 +320,7 @@ class Widget_Archive extends Widget_Abstract_Contents } /** - * @param $_archiveSlug the $_archiveSlug to set + * @param string $archiveSlug the $_archiveSlug to set */ public function setArchiveSlug($archiveSlug) { @@ -328,7 +328,7 @@ class Widget_Archive extends Widget_Abstract_Contents } /** - * @param $_archiveSingle the $_archiveSingle to set + * @param string $archiveSingle the $_archiveSingle to set */ public function setArchiveSingle($archiveSingle) { @@ -795,6 +795,7 @@ class Widget_Archive extends Widget_Abstract_Contents /** 保存密码至cookie */ if ($this->request->isPost() && isset($this->request->protectPassword)) { + $this->security->protect(); Typecho_Cookie::set('protectPassword', $this->request->protectPassword, 0); } @@ -1475,7 +1476,7 @@ class Widget_Archive extends Widget_Abstract_Contents * 获取回响归档对象 * * @access public - * @return void + * @return Widget_Comments_Ping */ public function pings() { diff --git a/var/Widget/Comments/Edit.php b/var/Widget/Comments/Edit.php index c98869c2..39eeedf4 100644 --- a/var/Widget/Comments/Edit.php +++ b/var/Widget/Comments/Edit.php @@ -60,18 +60,6 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In return false; } - /** - * 以数组形式获取coid - * - * @access private - * @return array - */ - private function getCoidAsArray() - { - $coid = $this->request->filter('int')->coid; - return $coid ? (is_array($coid) ? $coid : array($coid)) : array(); - } - /** * 标记为待审核 * @@ -80,7 +68,7 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In */ public function waitingComment() { - $comments = $this->getCoidAsArray(); + $comments = $this->request->filter('int')->getArray('coid'); $updateRows = 0; foreach ($comments as $comment) { @@ -105,7 +93,7 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In */ public function spamComment() { - $comments = $this->getCoidAsArray(); + $comments = $this->request->filter('int')->getArray('coid'); $updateRows = 0; foreach ($comments as $comment) { @@ -130,7 +118,7 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In */ public function approvedComment() { - $comments = $this->getCoidAsArray(); + $comments = $this->request->filter('int')->getArray('coid'); $updateRows = 0; foreach ($comments as $comment) { @@ -155,7 +143,7 @@ class Widget_Comments_Edit extends Widget_Abstract_Comments implements Widget_In */ public function deleteComment() { - $comments = $this->getCoidAsArray(); + $comments = $this->request->filter('int')->getArray('coid'); $deleteRows = 0; foreach ($comments as $coid) { diff --git a/var/Widget/Contents/Attachment/Edit.php b/var/Widget/Contents/Attachment/Edit.php index b4b706c5..c0019704 100644 --- a/var/Widget/Contents/Attachment/Edit.php +++ b/var/Widget/Contents/Attachment/Edit.php @@ -214,41 +214,34 @@ class Widget_Contents_Attachment_Edit extends Widget_Contents_Post_Edit implemen */ public function deleteAttachment() { - $cid = $this->request->filter('int')->cid; + $posts = $this->request->filter('int')->getArray('cid'); $deleteCount = 0; - $status = 'publish'; - if ($cid) { - /** 格式化文章主键 */ - $posts = is_array($cid) ? $cid : array($cid); - foreach ($posts as $post) { - // 删除插件接口 - $this->pluginHandle()->delete($post, $this); + foreach ($posts as $post) { + // 删除插件接口 + $this->pluginHandle()->delete($post, $this); - $condition = $this->db->sql()->where('cid = ?', $post); - $row = $this->db->fetchRow($this->select() + $condition = $this->db->sql()->where('cid = ?', $post); + $row = $this->db->fetchRow($this->select() ->where('table.contents.type = ?', 'attachment') ->where('table.contents.cid = ?', $post) ->limit(1), array($this, 'push')); - if ($this->isWriteable($condition) && $this->delete($condition)) { - /** 删除文件 */ - Widget_Upload::deleteHandle($row); + if ($this->isWriteable($condition) && $this->delete($condition)) { + /** 删除文件 */ + Widget_Upload::deleteHandle($row); - /** 删除评论 */ - $this->db->query($this->db->delete('table.comments') + /** 删除评论 */ + $this->db->query($this->db->delete('table.comments') ->where('cid = ?', $post)); - $status = $this->status; + // 完成删除插件接口 + $this->pluginHandle()->finishDelete($post, $this); - // 完成删除插件接口 - $this->pluginHandle()->finishDelete($post, $this); - - $deleteCount ++; - } - - unset($condition); + $deleteCount ++; } + + unset($condition); } if ($this->request->isAjax()) { @@ -273,7 +266,8 @@ class Widget_Contents_Attachment_Edit extends Widget_Contents_Post_Edit implemen public function clearAttachment() { $page = 1; - + $deleteCount = 0; + do { $posts = Typecho_Common::arrayFlatten($this->db->fetchAll($this->select('cid') ->from('table.contents') diff --git a/var/Widget/Contents/Page/Edit.php b/var/Widget/Contents/Page/Edit.php index ba6f6588..9b03d2ae 100644 --- a/var/Widget/Contents/Page/Edit.php +++ b/var/Widget/Contents/Page/Edit.php @@ -133,51 +133,47 @@ class Widget_Contents_Page_Edit extends Widget_Contents_Post_Edit implements Wid */ public function deletePage() { - $cid = $this->request->filter('int')->cid; + $pages = $this->request->filter('int')->getArray('cid'); $deleteCount = 0; - if ($cid) { - /** 格式化页面主键 */ - $pages = is_array($cid) ? $cid : array($cid); - foreach ($pages as $page) { - // 删除插件接口 - $this->pluginHandle()->delete($page, $this); + foreach ($pages as $page) { + // 删除插件接口 + $this->pluginHandle()->delete($page, $this); - if ($this->delete($this->db->sql()->where('cid = ?', $page))) { - /** 删除评论 */ - $this->db->query($this->db->delete('table.comments') + if ($this->delete($this->db->sql()->where('cid = ?', $page))) { + /** 删除评论 */ + $this->db->query($this->db->delete('table.comments') ->where('cid = ?', $page)); - /** 解除附件关联 */ - $this->unAttach($page); + /** 解除附件关联 */ + $this->unAttach($page); - /** 解除首页关联 */ - if ($this->options->frontPage == 'page:' . $page) { - $this->db->query($this->db->update('table.options') + /** 解除首页关联 */ + if ($this->options->frontPage == 'page:' . $page) { + $this->db->query($this->db->update('table.options') ->rows(array('value' => 'recent')) ->where('name = ?', 'frontPage')); - } + } - /** 删除草稿 */ - $draft = $this->db->fetchRow($this->db->select('cid') + /** 删除草稿 */ + $draft = $this->db->fetchRow($this->db->select('cid') ->from('table.contents') ->where('table.contents.parent = ? AND table.contents.type = ?', $page, 'page_draft') ->limit(1)); - /** 删除自定义字段 */ - $this->deleteFields($page); + /** 删除自定义字段 */ + $this->deleteFields($page); - if ($draft) { - $this->deleteDraft($draft['cid']); - $this->deleteFields($draft['cid']); - } - - // 完成删除插件接口 - $this->pluginHandle()->finishDelete($page, $this); - - $deleteCount ++; + if ($draft) { + $this->deleteDraft($draft['cid']); + $this->deleteFields($draft['cid']); } + + // 完成删除插件接口 + $this->pluginHandle()->finishDelete($page, $this); + + $deleteCount ++; } } @@ -197,29 +193,24 @@ class Widget_Contents_Page_Edit extends Widget_Contents_Post_Edit implements Wid */ public function deletePageDraft() { - $cid = $this->request->filter('int')->cid; + $pages = $this->request->filter('int')->getArray('cid'); $deleteCount = 0; - - if ($cid) { - /** 格式化文章主键 */ - $pages = is_array($cid) ? $cid : array($cid); - - foreach ($pages as $page) { - /** 删除草稿 */ - $draft = $this->db->fetchRow($this->db->select('cid') + + foreach ($pages as $page) { + /** 删除草稿 */ + $draft = $this->db->fetchRow($this->db->select('cid') ->from('table.contents') ->where('table.contents.parent = ? AND table.contents.type = ?', $page, 'page_draft') ->limit(1)); - if ($draft) { - $this->deleteDraft($draft['cid']); - $this->deleteFields($draft['cid']); - $deleteCount ++; - } + if ($draft) { + $this->deleteDraft($draft['cid']); + $this->deleteFields($draft['cid']); + $deleteCount ++; } } - + /** 设置提示信息 */ $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('草稿已经被删除') : _t('没有草稿被删除'), $deleteCount > 0 ? 'success' : 'notice'); @@ -236,9 +227,9 @@ class Widget_Contents_Page_Edit extends Widget_Contents_Post_Edit implements Wid */ public function sortPage() { - $pages = $this->request->filter('int')->cid; + $pages = $this->request->filter('int')->getArray('cid'); - if ($pages && is_array($pages)) { + if ($pages) { foreach ($pages as $sort => $cid) { $this->db->query($this->db->update('table.contents')->rows(array('order' => $sort + 1)) ->where('cid = ?', $cid)); diff --git a/var/Widget/Contents/Post/Edit.php b/var/Widget/Contents/Post/Edit.php index 7d021264..861157a6 100644 --- a/var/Widget/Contents/Post/Edit.php +++ b/var/Widget/Contents/Post/Edit.php @@ -90,9 +90,14 @@ class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widg protected function getFields() { $fields = array(); + $fieldNames = $this->request->getArray('fieldNames'); - if (!empty($this->request->fieldNames)) { - $data = $this->request->from('fieldNames', 'fieldTypes', 'fieldValues'); + if (!empty($fieldNames)) { + $data = array( + 'fieldNames' => $this->request->getArray('fieldNames'), + 'fieldTypes' => $this->request->getArray('fieldTypes'), + 'fieldValues' => $this->request->getArray('fieldValues') + ); foreach ($data['fieldNames'] as $key => $val) { if (empty($val)) { continue; @@ -585,6 +590,7 @@ class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widg { $tags = str_replace(',', ',', $tags); $tags = array_unique(array_map('trim', explode(',', $tags))); + $tags = array_filter($tags, array('Typecho_Validate', 'xssCheck')); /** 取出已有tag */ $existTags = Typecho_Common::arrayFlatten($this->db->fetchAll( @@ -597,6 +603,10 @@ class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widg /** 删除已有tag */ if ($existTags) { foreach ($existTags as $tag) { + if (0 == strlen($tag)) { + continue; + } + $this->db->query($this->db->delete('table.relationships') ->where('cid = ?', $cid) ->where('mid = ?', $tag)); @@ -615,6 +625,10 @@ class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widg /** 插入tag */ if ($insertTags) { foreach ($insertTags as $tag) { + if (0 == strlen($tag)) { + continue; + } + $this->db->query($this->db->insert('table.relationships') ->rows(array( 'mid' => $tag, @@ -701,8 +715,9 @@ class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widg public function writePost() { $contents = $this->request->from('password', 'allowComment', - 'allowPing', 'allowFeed', 'slug', 'category', 'tags', 'text', 'visibility'); + 'allowPing', 'allowFeed', 'slug', 'tags', 'text', 'visibility'); + $contents['category'] = $this->request->getArray('category'); $contents['title'] = $this->request->get('title', _t('未命名文档')); $contents['created'] = $this->getCreated(); @@ -770,67 +785,63 @@ class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widg */ public function deletePost() { - $cid = $this->request->filter('int')->cid; + $posts = $this->request->filter('int')->getArray('cid'); $deleteCount = 0; - if ($cid) { - /** 格式化文章主键 */ - $posts = is_array($cid) ? $cid : array($cid); - foreach ($posts as $post) { - // 删除插件接口 - $this->pluginHandle()->delete($post, $this); + foreach ($posts as $post) { + // 删除插件接口 + $this->pluginHandle()->delete($post, $this); - $condition = $this->db->sql()->where('cid = ?', $post); - $postObject = $this->db->fetchObject($this->db->select('status', 'type') - ->from('table.contents')->where('cid = ? AND type = ?', $post, 'post')); + $condition = $this->db->sql()->where('cid = ?', $post); + $postObject = $this->db->fetchObject($this->db->select('status', 'type') + ->from('table.contents')->where('cid = ? AND type = ?', $post, 'post')); - if ($this->isWriteable($condition) && + if ($this->isWriteable($condition) && $postObject && $this->delete($condition)) { - /** 删除分类 */ - $this->setCategories($post, array(), 'publish' == $postObject->status - && 'post' == $postObject->type); + /** 删除分类 */ + $this->setCategories($post, array(), 'publish' == $postObject->status + && 'post' == $postObject->type); - /** 删除标签 */ - $this->setTags($post, NULL, 'publish' == $postObject->status - && 'post' == $postObject->type); + /** 删除标签 */ + $this->setTags($post, NULL, 'publish' == $postObject->status + && 'post' == $postObject->type); - /** 删除评论 */ - $this->db->query($this->db->delete('table.comments') + /** 删除评论 */ + $this->db->query($this->db->delete('table.comments') ->where('cid = ?', $post)); - /** 解除附件关联 */ - $this->unAttach($post); + /** 解除附件关联 */ + $this->unAttach($post); - /** 删除草稿 */ - $draft = $this->db->fetchRow($this->db->select('cid') + /** 删除草稿 */ + $draft = $this->db->fetchRow($this->db->select('cid') ->from('table.contents') ->where('table.contents.parent = ? AND table.contents.type = ?', $post, 'post_draft') ->limit(1)); - /** 删除自定义字段 */ - $this->deleteFields($post); + /** 删除自定义字段 */ + $this->deleteFields($post); - if ($draft) { - $this->deleteDraft($draft['cid']); - $this->deleteFields($draft['cid']); - } - - // 完成删除插件接口 - $this->pluginHandle()->finishDelete($post, $this); - - $deleteCount ++; + if ($draft) { + $this->deleteDraft($draft['cid']); + $this->deleteFields($draft['cid']); } - unset($condition); + // 完成删除插件接口 + $this->pluginHandle()->finishDelete($post, $this); + + $deleteCount ++; } - // 清理标签 - if ($deleteCount > 0) { - $this->widget('Widget_Abstract_Metas')->clearTags(); - } + unset($condition); + } + + // 清理标签 + if ($deleteCount > 0) { + $this->widget('Widget_Abstract_Metas')->clearTags(); } /** 设置提示信息 */ @@ -849,29 +860,24 @@ class Widget_Contents_Post_Edit extends Widget_Abstract_Contents implements Widg */ public function deletePostDraft() { - $cid = $this->request->filter('int')->cid; + $posts = $this->request->filter('int')->getArray('cid'); $deleteCount = 0; - - if ($cid) { - /** 格式化文章主键 */ - $posts = is_array($cid) ? $cid : array($cid); - - foreach ($posts as $post) { - /** 删除草稿 */ - $draft = $this->db->fetchRow($this->db->select('cid') + + foreach ($posts as $post) { + /** 删除草稿 */ + $draft = $this->db->fetchRow($this->db->select('cid') ->from('table.contents') ->where('table.contents.parent = ? AND table.contents.type = ?', $post, 'post_draft') ->limit(1)); - if ($draft) { - $this->deleteDraft($draft['cid']); - $this->deleteFields($draft['cid']); - $deleteCount ++; - } + if ($draft) { + $this->deleteDraft($draft['cid']); + $this->deleteFields($draft['cid']); + $deleteCount ++; } } - + /** 设置提示信息 */ $this->widget('Widget_Notice')->set($deleteCount > 0 ? _t('草稿已经被删除') : _t('没有草稿被删除'), $deleteCount > 0 ? 'success' : 'notice'); diff --git a/var/Widget/Feedback.php b/var/Widget/Feedback.php index 0489ef7c..a0c2c9b6 100644 --- a/var/Widget/Feedback.php +++ b/var/Widget/Feedback.php @@ -37,6 +37,9 @@ class Widget_Feedback extends Widget_Abstract_Comments implements Widget_Interfa */ private function comment() { + // 使用安全模块保护 + $this->security->protect(); + $comment = array( 'cid' => $this->_content->cid, 'created' => $this->options->gmtTime, diff --git a/var/Widget/Login.php b/var/Widget/Login.php index d3de316c..597a559b 100644 --- a/var/Widget/Login.php +++ b/var/Widget/Login.php @@ -28,6 +28,9 @@ class Widget_Login extends Widget_Abstract_Users implements Widget_Interface_Do */ public function action() { + // protect + $this->security->protect(); + /** 如果已经登录 */ if ($this->user->hasLogin()) { /** 直接返回 */ @@ -73,7 +76,7 @@ class Widget_Login extends Widget_Abstract_Users implements Widget_Interface_Do $this->response->redirect($this->request->referer); } else if (!$this->user->pass('contributor', true)) { /** 不允许普通用户直接跳转后台 */ - $this->response->redirect($this->options->siteUrl); + $this->response->redirect($this->options->profileUrl); } else { $this->response->redirect($this->options->adminUrl); } diff --git a/var/Widget/Metas/Category/Edit.php b/var/Widget/Metas/Category/Edit.php index a63fa6f4..6ff55b3c 100644 --- a/var/Widget/Metas/Category/Edit.php +++ b/var/Widget/Metas/Category/Edit.php @@ -305,18 +305,16 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget */ public function deleteCategory() { - $categories = $this->request->filter('int')->mid; + $categories = $this->request->filter('int')->getArray('mid'); $deleteCount = 0; - if ($categories && is_array($categories)) { - foreach ($categories as $category) { - $parent = $this->db->fetchObject($this->select()->where('mid = ?', $category))->parent; + foreach ($categories as $category) { + $parent = $this->db->fetchObject($this->select()->where('mid = ?', $category))->parent; - if ($this->delete($this->db->sql()->where('mid = ?', $category))) { - $this->db->query($this->db->delete('table.relationships')->where('mid = ?', $category)); - $this->update(array('parent' => $parent), $this->db->sql()->where('parent = ?', $category)); - $deleteCount ++; - } + if ($this->delete($this->db->sql()->where('mid = ?', $category))) { + $this->db->query($this->db->delete('table.relationships')->where('mid = ?', $category)); + $this->update(array('parent' => $parent), $this->db->sql()->where('parent = ?', $category)); + $deleteCount ++; } } @@ -347,9 +345,9 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget } $merge = $this->request->merge; - $categories = $this->request->filter('int')->mid; + $categories = $this->request->filter('int')->getArray('mid'); - if ($categories && is_array($categories)) { + if ($categories) { $this->merge($merge, 'category', $categories); /** 提示信息 */ @@ -370,8 +368,8 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget */ public function sortCategory() { - $categories = $this->request->filter('int')->mid; - if ($categories && is_array($categories)) { + $categories = $this->request->filter('int')->getArray('mid'); + if ($categories) { $this->sort($categories, 'category'); } @@ -391,8 +389,8 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget */ public function refreshCategory() { - $categories = $this->request->filter('int')->mid; - if ($categories && is_array($categories)) { + $categories = $this->request->filter('int')->getArray('mid'); + if ($categories) { foreach ($categories as $category) { $this->refreshCountByTypeAndStatus($category, 'post', 'publish'); } @@ -445,8 +443,8 @@ class Widget_Metas_Category_Edit extends Widget_Abstract_Metas implements Widget /** * 获取菜单标题 * - * @access public * @return string + * @throws Typecho_Widget_Exception */ public function getMenuTitle() { diff --git a/var/Widget/Metas/Tag/Edit.php b/var/Widget/Metas/Tag/Edit.php index d8beb1d5..ec0f9081 100644 --- a/var/Widget/Metas/Tag/Edit.php +++ b/var/Widget/Metas/Tag/Edit.php @@ -267,7 +267,7 @@ class Widget_Metas_Tag_Edit extends Widget_Abstract_Metas implements Widget_Inte */ public function deleteTag() { - $tags = $this->request->filter('int')->mid; + $tags = $this->request->filter('int')->getArray('mid'); $deleteCount = 0; if ($tags && is_array($tags)) { @@ -306,9 +306,9 @@ class Widget_Metas_Tag_Edit extends Widget_Abstract_Metas implements Widget_Inte $this->response->goBack(); } - $tags = $this->request->filter('int')->mid; + $tags = $this->request->filter('int')->getArray('mid'); - if ($tags && is_array($tags)) { + if ($tags) { $this->merge($merge, 'tag', $tags); /** 提示信息 */ @@ -329,8 +329,8 @@ class Widget_Metas_Tag_Edit extends Widget_Abstract_Metas implements Widget_Inte */ public function refreshTag() { - $tags = $this->request->filter('int')->mid; - if ($tags && is_array($tags)) { + $tags = $this->request->filter('int')->getArray('mid'); + if ($tags) { foreach ($tags as $tag) { $this->refreshCountByTypeAndStatus($tag, 'post', 'publish'); } diff --git a/var/Widget/Options.php b/var/Widget/Options.php index a893f019..c569447a 100644 --- a/var/Widget/Options.php +++ b/var/Widget/Options.php @@ -201,8 +201,9 @@ class Widget_Options extends Typecho_Widget */ protected function ___loginAction() { - return Typecho_Router::url('do', array('action' => 'login', 'widget' => 'Login'), - Typecho_Common::url('index.php', $this->rootUrl)); + return $this->widget('Widget_Security')->getTokenUrl( + Typecho_Router::url('do', array('action' => 'login', 'widget' => 'Login'), + Typecho_Common::url('index.php', $this->rootUrl))); } /** @@ -224,7 +225,8 @@ class Widget_Options extends Typecho_Widget */ protected function ___registerAction() { - return Typecho_Router::url('do', array('action' => 'register', 'widget' => 'Register'), $this->index); + return $this->widget('Widget_Security')->getTokenUrl( + Typecho_Router::url('do', array('action' => 'register', 'widget' => 'Register'), $this->index)); } /** diff --git a/var/Widget/Options/Discussion.php b/var/Widget/Options/Discussion.php index 74bdfacc..ca330a34 100644 --- a/var/Widget/Options/Discussion.php +++ b/var/Widget/Options/Discussion.php @@ -183,9 +183,11 @@ class Widget_Options_Discussion extends Widget_Abstract_Options implements Widge $this->response->goBack(); } - $settings = $this->request->from('commentDateFormat', 'commentsListSize', 'commentsShow', 'commentsPost', 'commentsPageSize', 'commentsPageDisplay', 'commentsAvatar', + $settings = $this->request->from('commentDateFormat', 'commentsListSize', 'commentsPageSize', 'commentsPageDisplay', 'commentsAvatar', 'commentsOrder', 'commentsMaxNestingLevels', 'commentsUrlNofollow', 'commentsPostTimeout', 'commentsUniqueIpInterval', 'commentsWhitelist', 'commentsRequireMail', 'commentsAvatarRating', 'commentsPostTimeout', 'commentsPostInterval', 'commentsRequireModeration', 'commentsRequireURL', 'commentsHTMLTagAllowed', 'commentsStopWords', 'commentsIpBlackList'); + $settings['commentsShow'] = $this->request->getArray('commentsShow'); + $settings['commentsPost'] = $this->request->getArray('commentsPost'); $settings['commentsShowCommentOnly'] = $this->isEnableByCheckbox($settings['commentsShow'], 'commentsShowCommentOnly'); $settings['commentsMarkdown'] = $this->isEnableByCheckbox($settings['commentsShow'], 'commentsMarkdown'); diff --git a/var/Widget/Options/General.php b/var/Widget/Options/General.php index f8e89269..c3c777dc 100644 --- a/var/Widget/Options/General.php +++ b/var/Widget/Options/General.php @@ -36,7 +36,8 @@ class Widget_Options_General extends Widget_Abstract_Options implements Widget_I /** 站点名称 */ $title = new Typecho_Widget_Helper_Form_Element_Text('title', NULL, $this->options->title, _t('站点名称'), _t('站点的名称将显示在网页的标题处.')); $title->input->setAttribute('class', 'w-100'); - $form->addInput($title->addRule('required', _t('请填写站点名称'))); + $form->addInput($title->addRule('required', _t('请填写站点名称')) + ->addRule('xssCheck', _t('请不要在站点名称中使用特殊字符'))); /** 站点地址 */ $siteUrl = new Typecho_Widget_Helper_Form_Element_Text('siteUrl', NULL, $this->options->originalSiteUrl, _t('站点地址'), _t('站点地址主要用于生成内容的永久链接.') @@ -49,11 +50,11 @@ class Widget_Options_General extends Widget_Abstract_Options implements Widget_I /** 站点描述 */ $description = new Typecho_Widget_Helper_Form_Element_Text('description', NULL, $this->options->description, _t('站点描述'), _t('站点描述将显示在网页代码的头部.')); - $form->addInput($description); + $form->addInput($description->addRule('xssCheck', _t('请不要在站点描述中使用特殊字符'))); /** 关键词 */ $keywords = new Typecho_Widget_Helper_Form_Element_Text('keywords', NULL, $this->options->keywords, _t('关键词'), _t('请以半角逗号 "," 分割多个关键字.')); - $form->addInput($keywords); + $form->addInput($keywords->addRule('xssCheck', _t('请不要在关键词中使用特殊字符'))); /** 注册 */ $allowRegister = new Typecho_Widget_Helper_Form_Element_Radio('allowRegister', array('0' => _t('不允许'), '1' => _t('允许')), $this->options->allowRegister, _t('是否允许注册'), @@ -135,6 +136,17 @@ class Widget_Options_General extends Widget_Abstract_Options implements Widget_I return $form; } + /** + * 过滤掉可执行的后缀名 + * + * @param string $ext + * @return boolean + */ + public function removeShell($ext) + { + return !preg_match("/^(php|php4|php5|sh|asp|jsp|rb|py|pl|dll|exe|bat)$/i", $ext); + } + /** * 执行更新动作 * @@ -148,7 +160,8 @@ class Widget_Options_General extends Widget_Abstract_Options implements Widget_I $this->response->goBack(); } - $settings = $this->request->from('title', 'siteUrl', 'description', 'keywords', 'allowRegister', 'timezone', 'attachmentTypes'); + $settings = $this->request->from('title', 'siteUrl', 'description', 'keywords', 'allowRegister', 'timezone'); + $settings['attachmentTypes'] = $this->request->getArray('attachmentTypes'); $settings['siteUrl'] = rtrim($settings['siteUrl'], '/'); $attachmentTypes = array(); @@ -164,9 +177,14 @@ class Widget_Options_General extends Widget_Abstract_Options implements Widget_I $attachmentTypes[] = '@doc@'; } - $attachmentTypesOther = $this->request->filter('trim')->attachmentTypesOther; + $attachmentTypesOther = $this->request->filter('trim', 'strtolower')->attachmentTypesOther; if ($this->isEnableByCheckbox($settings['attachmentTypes'], '@other@') && !empty($attachmentTypesOther)) { - $attachmentTypes[] = implode(',', array_map('trim', explode(',', $attachmentTypesOther))); + $types = implode(',', array_filter(array_map('trim', + explode(',', $attachmentTypesOther)), array($this, 'removeShell'))); + + if (!empty($types)) { + $attachmentTypes[] = $types; + } } $settings['attachmentTypes'] = implode(',', $attachmentTypes); diff --git a/var/Widget/Options/Permalink.php b/var/Widget/Options/Permalink.php index e545b074..ef3ebd8e 100644 --- a/var/Widget/Options/Permalink.php +++ b/var/Widget/Options/Permalink.php @@ -222,7 +222,8 @@ RewriteRule . {$basePath}index.php [L] . _t('请调整你的目录权限, 或者手动创建一个.htaccess文件.') . ''; } - $errorStr .= '
' . _t('如果你仍然想启用此功能, 请点击这里', Typecho_Common::url('index.php/action/options-permalink?do=enableRewriteAnyway', $this->options->siteUrl)); + $errorStr .= '
' . _t('如果你仍然想启用此功能, 请点击这里', + $this->security->getTokenUrl(Typecho_Common::url('index.php/action/options-permalink?do=enableRewriteAnyway', $this->options->siteUrl))); $form->addInput($rewrite->addRule(array($this, 'checkRewrite'), $errorStr)); $patterns = array('/archives/[cid:digital]/' => _t('默认风格') . ' /archives/{cid}/', diff --git a/var/Widget/Options/Reading.php b/var/Widget/Options/Reading.php index 20ef9b68..84262a82 100644 --- a/var/Widget/Options/Reading.php +++ b/var/Widget/Options/Reading.php @@ -39,7 +39,7 @@ class Widget_Options_Reading extends Widget_Options_Permalink . _t('在某些主题中这个格式可能不会生效, 因为主题作者可以自定义日期格式.') . '
' . _t('请参考 PHP 日期格式写法.')); $postDateFormat->input->setAttribute('class', 'w-40 mono'); - $form->addInput($postDateFormat); + $form->addInput($postDateFormat->addRule('xssCheck', _t('请不要在日期格式中使用特殊字符'))); //首页显示 $frontPageParts = explode(':', $this->options->frontPage); diff --git a/var/Widget/Register.php b/var/Widget/Register.php index a9480b84..abd3af2d 100644 --- a/var/Widget/Register.php +++ b/var/Widget/Register.php @@ -17,6 +17,9 @@ class Widget_Register extends Widget_Abstract_Users implements Widget_Interface_ */ public function action() { + // protect + $this->security->protect(); + /** 如果已经登录 */ if ($this->user->hasLogin() || !$this->options->allowRegister) { /** 直接返回 */ @@ -79,6 +82,6 @@ class Widget_Register extends Widget_Abstract_Users implements Widget_Interface_ Typecho_Cookie::delete('__typecho_remember_mail'); $this->widget('Widget_Notice')->set(_t('用户 %s 已经成功注册, 密码为 %s', $this->screenName, $generatedPassword), 'success'); - $this->response->goBack(); + $this->response->redirect($this->options->adminUrl); } } diff --git a/var/Widget/Security.php b/var/Widget/Security.php index a3aacc4b..e1562e8d 100644 --- a/var/Widget/Security.php +++ b/var/Widget/Security.php @@ -30,13 +30,21 @@ class Widget_Security extends Typecho_Widget $this->_options = $this->widget('Widget_Options'); $user = $this->widget('Widget_User'); - $token = uniqid(); + $this->_token = $this->_options->secret; if ($user->hasLogin()) { - $token = $user->authCode . '&' . $user->uid - . '&' . $this->request->getRequestUrl(); + $this->_token .= '&' . $user->authCode . '&' . $user->uid; } + } - $this->_token = md5($token); + /** + * 获取token + * + * @param string $suffix 后缀 + * @return string + */ + public function getToken($suffix) + { + return md5($this->_token . '&' . $suffix); } /** @@ -54,7 +62,7 @@ class Widget_Security extends Typecho_Widget parse_str($parts['query'], $params); } - $params['_'] = $this->_token; + $params['_'] = $this->getToken($this->request->getRequestUrl()); $parts['query'] = http_build_query($params); return Typecho_Common::buildUrl($parts); @@ -66,16 +74,8 @@ class Widget_Security extends Typecho_Widget */ public function protect() { - $user = $this->widget('Widget_User'); - $token = uniqid(); - if ($user->hasLogin()) { - $token = $user->authCode . '&' . $user->uid - . '&' . $this->request->getReferer(); - } - - if ($this->request->get('_') != md5($token)) { - $this->widget('Widget_Notice')->set(_t('一次不安全的跳转已经被阻止')); - $this->response->redirect($this->_options->adminUrl); + if ($this->request->get('_') != $this->getToken($this->request->getReferer())) { + $this->response->goBack(); } } diff --git a/var/Widget/Users/Edit.php b/var/Widget/Users/Edit.php index 16a2c638..e2e04d16 100644 --- a/var/Widget/Users/Edit.php +++ b/var/Widget/Users/Edit.php @@ -78,7 +78,7 @@ class Widget_Users_Edit extends Widget_Abstract_Users implements Widget_Interfac ->from('table.users') ->where('uid = ?', $uid)->limit(1)); - return $user ? true : false; + return !empty($user); } /** @@ -125,9 +125,9 @@ class Widget_Users_Edit extends Widget_Abstract_Users implements Widget_Interfac $form->addInput($url); /** 用户组 */ - $group = new Typecho_Widget_Helper_Form_Element_Select('group', array('visitor' => _t('访问者'), - 'subscriber' => _t('关注者'), 'contributor' => _t('贡献者'), 'editor' => _t('编辑'), 'administrator' => _t('管理员')), - NULL, _t('用户组'), _t('不同的用户组拥有不同的权限.') + $group = new Typecho_Widget_Helper_Form_Element_Select('group', array('subscriber' => _t('关注者'), + 'contributor' => _t('贡献者'), 'editor' => _t('编辑'), 'administrator' => _t('管理员')), + NULL, _t('用户组'), _t('不同的用户组拥有不同的权限.') . '
' . _t('具体的权限分配表请参考这里.')); $form->addInput($group); @@ -167,6 +167,7 @@ class Widget_Users_Edit extends Widget_Abstract_Users implements Widget_Interfac /** 给表单增加规则 */ if ('insert' == $action || 'update' == $action) { $screenName->addRule(array($this, 'screenNameExists'), _t('昵称已经存在')); + $screenName->addRule('xssCheck', _t('请不要在昵称中使用特殊字符')); $url->addRule('url', _t('个人主页地址格式错误')); $mail->addRule('required', _t('必须填写电子邮箱')); $mail->addRule(array($this, 'mailExists'), _t('电子邮箱地址已经存在')); @@ -267,18 +268,16 @@ class Widget_Users_Edit extends Widget_Abstract_Users implements Widget_Interfac */ public function deleteUser() { - $users = $this->request->uid; + $users = $this->request->filter('int')->getArray('uid'); $deleteCount = 0; - if ($users && is_array($users)) { - foreach ($users as $user) { - if (1 == $user) { - continue; - } + foreach ($users as $user) { + if (1 == $user || $user == $this->user->id) { + continue; + } - if ($this->delete($this->db->sql()->where('uid = ?', $user))) { - $deleteCount ++; - } + if ($this->delete($this->db->sql()->where('uid = ?', $user))) { + $deleteCount ++; } } diff --git a/var/Widget/Users/Profile.php b/var/Widget/Users/Profile.php index 80b6b8ec..963f0203 100644 --- a/var/Widget/Users/Profile.php +++ b/var/Widget/Users/Profile.php @@ -74,6 +74,7 @@ class Widget_Users_Profile extends Widget_Users_Edit implements Widget_Interface /** 给表单增加规则 */ $screenName->addRule(array($this, 'screenNameExists'), _t('昵称已经存在')); + $screenName->addRule('xssCheck', _t('请不要在昵称中使用特殊字符')); $url->addRule('url', _t('个人主页地址格式错误')); $mail->addRule('required', _t('必须填写电子邮箱')); $mail->addRule(array($this, 'mailExists'), _t('电子邮箱地址已经存在')); @@ -94,7 +95,7 @@ class Widget_Users_Profile extends Widget_Users_Edit implements Widget_Interface $form = new Typecho_Widget_Helper_Form($this->security->getIndex('/action/users-profile'), Typecho_Widget_Helper_Form::POST_METHOD); - /** 自动保存 */ + /** 撰写设置 */ $markdown = new Typecho_Widget_Helper_Form_Element_Radio('markdown', array('0' => _t('关闭'), '1' => _t('打开')), $this->options->markdown, _t('使用 Markdown 语法编辑和解析内容'), @@ -276,15 +277,11 @@ class Widget_Users_Profile extends Widget_Users_Edit implements Widget_Interface { $settings['autoSave'] = $this->request->autoSave ? 1 : 0; $settings['markdown'] = $this->request->markdown ? 1 : 0; + $defaultAllow = $this->request->getArray('defaultAllow'); - $settings['defaultAllowComment'] = is_array($this->request->defaultAllow) - && in_array('comment', $this->request->defaultAllow) ? 1 : 0; - - $settings['defaultAllowPing'] = is_array($this->request->defaultAllow) - && in_array('ping', $this->request->defaultAllow) ? 1 : 0; - - $settings['defaultAllowFeed'] = is_array($this->request->defaultAllow) - && in_array('feed', $this->request->defaultAllow) ? 1 : 0; + $settings['defaultAllowComment'] = in_array('comment', $defaultAllow) ? 1 : 0; + $settings['defaultAllowPing'] = in_array('ping', $defaultAllow) ? 1 : 0; + $settings['defaultAllowFeed'] = in_array('feed', $defaultAllow) ? 1 : 0; foreach ($settings as $name => $value) { if ($this->db->fetchObject($this->db->select(array('COUNT(*)' => 'num'))