fix install

This commit is contained in:
joyqi 2021-08-17 18:36:54 +08:00
parent 0d4299d99e
commit 2731e34db0
23 changed files with 1262 additions and 6858 deletions

18
admin/css/install.css Normal file
View File

@ -0,0 +1,18 @@
h1 { text-align: center; }
details summary { cursor: pointer; }
@keyframes fadein { from { opacity: 0; }
to { opacity: 1; } }
form > .message { display: none; }
.message.fade { display: block; animation: fadein .5s linear; }
.message *:last-child { margin-bottom: 0; }
.message p { margin-top: 8px; }
.message p button { margin-left: 5px; }
.message p button:first-child { margin-left: 0; }

View File

@ -237,27 +237,6 @@ select { border: 1px solid #CCC; height: 28px; }
.profile-avatar { width: 220px; height: 220px; border-radius: 10px; }
/** 增加配置面板内部的错误样式 by 70 */
/** 安装样式 @author mingcheng @date 2008-09-06 */
/** 安装向导 */
.typecho-install { padding-bottom: 2em; }
.typecho-install-patch { margin-bottom: 2em; padding: 2em 0; background-color: #292D33; color: #FFF; text-align: center; }
.typecho-install-patch ol { list-style: none; margin: 3em 0 1em; padding: 0; color: #999; }
.typecho-install-patch li { display: inline-block; margin: 0 .8em; }
.typecho-install-patch span { display: inline-block; margin-right: 5px; width: 20px; height: 20px; line-height: 20px; border: 2px solid #999; text-align: center; border-radius: 2em; }
.typecho-install-patch li.current { color: #FFF; font-weight: bold; }
.typecho-install-patch li.current span { border-color: #FFF; }
/** 安装主体内容 */
.typecho-install .typecho-install-body input { width: 100%; }
.typecho-install-body .typecho-option li { margin: 1em 0; }
/** 欢迎界面 */
#typecho-welcome { margin: 1em 0; padding: 1em 2em; background-color: #E9E9E6; }

View File

@ -5,17 +5,13 @@ if (!defined('__TYPECHO_ADMIN__')) {
$header = '<link rel="stylesheet" href="' . Typecho_Common::url('normalize.css?v=' . $suffixVersion, $options->adminStaticUrl('css')) . '">
<link rel="stylesheet" href="' . Typecho_Common::url('grid.css?v=' . $suffixVersion, $options->adminStaticUrl('css')) . '">
<link rel="stylesheet" href="' . Typecho_Common::url('style.css?v=' . $suffixVersion, $options->adminStaticUrl('css')) . '">
<!--[if lt IE 9]>
<script src="' . Typecho_Common::url('html5shiv.js?v=' . $suffixVersion, $options->adminStaticUrl('js')) . '"></script>
<script src="' . Typecho_Common::url('respond.js?v=' . $suffixVersion, $options->adminStaticUrl('js')) . '"></script>
<![endif]-->';
<link rel="stylesheet" href="' . Typecho_Common::url('style.css?v=' . $suffixVersion, $options->adminStaticUrl('css')) . '">';
/** 注册一个初始化插件 */
$header = Typecho_Plugin::factory('admin/header.php')->header($header);
?><!DOCTYPE HTML>
<html class="no-js">
<html>
<head>
<meta charset="<?php $options->charset(); ?>">
<meta name="renderer" content="webkit">

View File

@ -1 +0,0 @@
!function(e,i){var l,m,t=e.html5||{},a=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,n="_html5shiv",c=0,o={};function s(){var e=f.elements;return"string"==typeof e?e.split(" "):e}function h(e){var t=o[e[n]];return t||(t={},c++,e[n]=c,o[c]=t),t}function u(e,t,n){return t=t||i,m?t.createElement(e):(t=(n=n||h(t)).cache[e]?n.cache[e].cloneNode():r.test(e)?(n.cache[e]=n.createElem(e)).cloneNode():n.createElem(e)).canHaveChildren&&!a.test(e)?n.frag.appendChild(t):t}function d(e){var t,n,a,r,c,o=h(e=e||i);return!f.shivCSS||l||o.hasCSS||(o.hasCSS=(n="article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}",a=(t=e).createElement("p"),t=t.getElementsByTagName("head")[0]||t.documentElement,a.innerHTML="x<style>"+n+"</style>",!!t.insertBefore(a.lastChild,t.firstChild))),m||(r=e,(c=o).cache||(c.cache={},c.createElem=r.createElement,c.createFrag=r.createDocumentFragment,c.frag=c.createFrag()),r.createElement=function(e){return f.shivMethods?u(e,r,c):c.createElem(e)},r.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+s().join().replace(/[\w\-]+/g,function(e){return c.createElem(e),c.frag.createElement(e),'c("'+e+'")'})+");return n}")(f,c.frag)),e}!function(){try{var e=i.createElement("a");e.innerHTML="<xyz></xyz>",l="hidden"in e,m=1==e.childNodes.length||function(){i.createElement("a");var e=i.createDocumentFragment();return void 0===e.cloneNode||void 0===e.createDocumentFragment||void 0===e.createElement}()}catch(e){m=l=!0}}();var f={elements:t.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==t.shivCSS,supportsUnknownElements:m,shivMethods:!1!==t.shivMethods,type:"default",shivDocument:d,createElement:u,createDocumentFragment:function(e,t){if(e=e||i,m)return e.createDocumentFragment();for(var n=(t=t||h(e)).frag.cloneNode(),a=0,r=s(),c=r.length;a<c;a++)n.createElement(r[a]);return n}};e.html5=f,d(i)}(this,document);

View File

@ -1 +0,0 @@
!function(e){"use strict";var t,n,s,a,i;e.matchMedia=e.matchMedia||(e=e.document,n=e.documentElement,s=n.firstElementChild||n.firstChild,a=e.createElement("body"),(i=e.createElement("div")).id="mq-test-1",i.style.cssText="position:absolute;top:-100em",a.style.background="none",a.appendChild(i),function(e){return i.innerHTML='&shy;<style media="'+e+'"> #mq-test-1 { width: 42px; }</style>',n.insertBefore(a,s),t=42===i.offsetWidth,n.removeChild(a),{matches:t,media:e}})}(this),function(g){"use strict";var c={};(g.respond=c).update=function(){};var y,x,E,v,w,i,S,T,r,C,b,$,z,M,R,o,l,e,m=[],s=function(){var t=!1;try{t=new g.XMLHttpRequest}catch(e){t=new g.ActiveXObject("Microsoft.XMLHTTP")}return function(){return t}}(),n=function(e,t){var n=s();n&&(n.open("GET",e,!0),n.onreadystatechange=function(){4!==n.readyState||200!==n.status&&304!==n.status||t(n.responseText)},4!==n.readyState&&n.send(null))},p=function(e){return e.replace(c.regex.minmaxwh,"").match(c.regex.other)};function t(){R(!0)}c.ajax=n,c.queue=m,c.unsupportedmq=p,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=g.matchMedia&&null!==g.matchMedia("only all")&&g.matchMedia("only all").matches,c.mediaQueriesSupported||(y=g.document,x=y.documentElement,E=[],v=[],w=[],i={},S=30,T=y.getElementsByTagName("head")[0]||x,r=y.getElementsByTagName("base")[0],C=T.getElementsByTagName("link"),M=function(){var e,t=y.createElement("div"),n=y.body,s=x.style.fontSize,a=n&&n.style.fontSize,i=!1;return t.style.cssText="position:absolute;font-size:1em;width:1em",n||((n=i=y.createElement("body")).style.background="none"),x.style.fontSize="100%",n.style.fontSize="100%",n.appendChild(t),i&&x.insertBefore(n,x.firstChild),e=t.offsetWidth,i?x.removeChild(n):n.removeChild(t),x.style.fontSize=s,a&&(n.style.fontSize=a),z=parseFloat(e),z},R=function(e){var t,n,s,a,i,r,o,l,m,d,h="clientWidth",u=x[h],c="CSS1Compat"===y.compatMode&&u||y.body[h]||u,p={},f=C[C.length-1],u=(new Date).getTime();if(e&&b&&u-b<S)return g.clearTimeout($),void($=g.setTimeout(R,S));for(t in b=u,E)E.hasOwnProperty(t)&&(s=null===(i=(n=E[t]).minw),a=null===(r=n.maxw),i=i&&parseFloat(i)*(-1<i.indexOf("em")?z||M():1),r=r&&parseFloat(r)*(-1<r.indexOf("em")?z||M():1),n.hasquery&&(s&&a||!(s||i<=c)||!(a||c<=r))||(p[n.media]||(p[n.media]=[]),p[n.media].push(v[n.rules])));for(o in w)w.hasOwnProperty(o)&&w[o]&&w[o].parentNode===T&&T.removeChild(w[o]);for(l in w.length=0,p)p.hasOwnProperty(l)&&(m=y.createElement("style"),d=p[l].join("\n"),m.type="text/css",m.media=l,T.insertBefore(m,f.nextSibling),m.styleSheet?m.styleSheet.cssText=d:m.appendChild(y.createTextNode(d)),w.push(m))},o=function(e,t,n){function s(e){return e.replace(c.regex.urls,"$1"+t+"$2$3")}var a=e.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),i=a&&a.length||0,r=!i&&n;(t=t.substring(0,t.lastIndexOf("/"))).length&&(t+="/"),r&&(i=1);for(var o,l,m=0;m<i;m++){r?(o=n,v.push(s(e))):(o=a[m].match(c.regex.findStyles)&&RegExp.$1,v.push(RegExp.$2&&s(RegExp.$2)));for(var d,h=(d=o.split(",")).length,u=0;u<h;u++)l=d[u],p(l)||E.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:v.length-1,hasquery:-1<l.indexOf("("),minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}R()},l=function(){var t;m.length&&(t=m.shift(),n(t.href,function(e){o(e,t.href,t.media),i[t.href]=!0,g.setTimeout(function(){l()},0)}))},(e=function(){for(var e=0;e<C.length;e++){var t=C[e],n=t.href,s=t.media,a=t.rel&&"stylesheet"===t.rel.toLowerCase();n&&a&&!i[n]&&(t.styleSheet&&t.styleSheet.rawCssText?(o(t.styleSheet.rawCssText,n,s),i[n]=!0):(/^([a-zA-Z:]*\/\/)/.test(n)||r)&&n.replace(RegExp.$1,"").split("/")[0]!==g.location.host||("//"===n.substring(0,2)&&(n=g.location.protocol+n),m.push({href:n,media:s})))}l()})(),c.update=e,c.getEmValue=M,g.addEventListener?g.addEventListener("resize",t,!1):g.attachEvent&&g.attachEvent("onresize",t))}(this);

View File

@ -1,301 +0,0 @@
/**
* @preserve HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
;(function(window, document) {
/*jshint evil:true */
/** version */
var version = '3.7.0';
/** Preset options */
var options = window.html5 || {};
/** Used to skip problem elements */
var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
/** Not all elements can be cloned in IE **/
var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
/** Detect whether the browser supports default html5 styles */
var supportsHtml5Styles;
/** Name of the expando, to work with multiple documents or to re-shiv one document */
var expando = '_html5shiv';
/** The id for the the documents expando */
var expanID = 0;
/** Cached data for each document */
var expandoData = {};
/** Detect whether the browser supports unknown elements */
var supportsUnknownElements;
(function() {
try {
var a = document.createElement('a');
a.innerHTML = '<xyz></xyz>';
//if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
supportsHtml5Styles = ('hidden' in a);
supportsUnknownElements = a.childNodes.length == 1 || (function() {
// assign a false positive if unable to shiv
(document.createElement)('a');
var frag = document.createDocumentFragment();
return (
typeof frag.cloneNode == 'undefined' ||
typeof frag.createDocumentFragment == 'undefined' ||
typeof frag.createElement == 'undefined'
);
}());
} catch(e) {
// assign a false positive if detection fails => unable to shiv
supportsHtml5Styles = true;
supportsUnknownElements = true;
}
}());
/*--------------------------------------------------------------------------*/
/**
* Creates a style sheet with the given CSS text and adds it to the document.
* @private
* @param {Document} ownerDocument The document.
* @param {String} cssText The CSS text.
* @returns {StyleSheet} The style element.
*/
function addStyleSheet(ownerDocument, cssText) {
var p = ownerDocument.createElement('p'),
parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
p.innerHTML = 'x<style>' + cssText + '</style>';
return parent.insertBefore(p.lastChild, parent.firstChild);
}
/**
* Returns the value of `html5.elements` as an array.
* @private
* @returns {Array} An array of shived element node names.
*/
function getElements() {
var elements = html5.elements;
return typeof elements == 'string' ? elements.split(' ') : elements;
}
/**
* Returns the data associated to the given document
* @private
* @param {Document} ownerDocument The document.
* @returns {Object} An object of data.
*/
function getExpandoData(ownerDocument) {
var data = expandoData[ownerDocument[expando]];
if (!data) {
data = {};
expanID++;
ownerDocument[expando] = expanID;
expandoData[expanID] = data;
}
return data;
}
/**
* returns a shived element for the given nodeName and document
* @memberOf html5
* @param {String} nodeName name of the element
* @param {Document} ownerDocument The context document.
* @returns {Object} The shived element.
*/
function createElement(nodeName, ownerDocument, data){
if (!ownerDocument) {
ownerDocument = document;
}
if(supportsUnknownElements){
return ownerDocument.createElement(nodeName);
}
if (!data) {
data = getExpandoData(ownerDocument);
}
var node;
if (data.cache[nodeName]) {
node = data.cache[nodeName].cloneNode();
} else if (saveClones.test(nodeName)) {
node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
} else {
node = data.createElem(nodeName);
}
// Avoid adding some elements to fragments in IE < 9 because
// * Attributes like `name` or `type` cannot be set/changed once an element
// is inserted into a document/fragment
// * Link elements with `src` attributes that are inaccessible, as with
// a 403 response, will cause the tab/window to crash
// * Script elements appended to fragments will execute when their `src`
// or `text` property is set
return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node;
}
/**
* returns a shived DocumentFragment for the given document
* @memberOf html5
* @param {Document} ownerDocument The context document.
* @returns {Object} The shived DocumentFragment.
*/
function createDocumentFragment(ownerDocument, data){
if (!ownerDocument) {
ownerDocument = document;
}
if(supportsUnknownElements){
return ownerDocument.createDocumentFragment();
}
data = data || getExpandoData(ownerDocument);
var clone = data.frag.cloneNode(),
i = 0,
elems = getElements(),
l = elems.length;
for(;i<l;i++){
clone.createElement(elems[i]);
}
return clone;
}
/**
* Shivs the `createElement` and `createDocumentFragment` methods of the document.
* @private
* @param {Document|DocumentFragment} ownerDocument The document.
* @param {Object} data of the document.
*/
function shivMethods(ownerDocument, data) {
if (!data.cache) {
data.cache = {};
data.createElem = ownerDocument.createElement;
data.createFrag = ownerDocument.createDocumentFragment;
data.frag = data.createFrag();
}
ownerDocument.createElement = function(nodeName) {
//abort shiv
if (!html5.shivMethods) {
return data.createElem(nodeName);
}
return createElement(nodeName, ownerDocument, data);
};
ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
'var n=f.cloneNode(),c=n.createElement;' +
'h.shivMethods&&(' +
// unroll the `createElement` calls
getElements().join().replace(/[\w\-]+/g, function(nodeName) {
data.createElem(nodeName);
data.frag.createElement(nodeName);
return 'c("' + nodeName + '")';
}) +
');return n}'
)(html5, data.frag);
}
/*--------------------------------------------------------------------------*/
/**
* Shivs the given document.
* @memberOf html5
* @param {Document} ownerDocument The document to shiv.
* @returns {Document} The shived document.
*/
function shivDocument(ownerDocument) {
if (!ownerDocument) {
ownerDocument = document;
}
var data = getExpandoData(ownerDocument);
if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
data.hasCSS = !!addStyleSheet(ownerDocument,
// corrects block display not defined in IE6/7/8/9
'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}' +
// adds styling not present in IE6/7/8/9
'mark{background:#FF0;color:#000}' +
// hides non-rendered elements
'template{display:none}'
);
}
if (!supportsUnknownElements) {
shivMethods(ownerDocument, data);
}
return ownerDocument;
}
/*--------------------------------------------------------------------------*/
/**
* The `html5` object is exposed so that more elements can be shived and
* existing shiving can be detected on iframes.
* @type Object
* @example
*
* // options can be changed before the script is included
* html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
*/
var html5 = {
/**
* An array or space separated string of node names of the elements to shiv.
* @memberOf html5
* @type Array|String
*/
'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video',
/**
* current version of html5shiv
*/
'version': version,
/**
* A flag to indicate that the HTML5 style sheet should be inserted.
* @memberOf html5
* @type Boolean
*/
'shivCSS': (options.shivCSS !== false),
/**
* Is equal to true if a browser supports creating unknown/HTML5 elements
* @memberOf html5
* @type boolean
*/
'supportsUnknownElements': supportsUnknownElements,
/**
* A flag to indicate that the document's `createElement` and `createDocumentFragment`
* methods should be overwritten.
* @memberOf html5
* @type Boolean
*/
'shivMethods': (options.shivMethods !== false),
/**
* A string to describe the type of `html5` object ("default" or "default print").
* @memberOf html5
* @type String
*/
'type': 'default',
// shivs the document according to the specified `html5` object options
'shivDocument': shivDocument,
//creates a shived element
createElement: createElement,
//creates a shived documentFragment
createDocumentFragment: createDocumentFragment
};
/*--------------------------------------------------------------------------*/
// expose html5
window.html5 = html5;
// shiv the document
shivDocument(document);
}(this, document));

View File

@ -1,237 +0,0 @@
/*! Respond.js v1.4.2: min/max-width media query polyfill
* Copyright 2013 Scott Jehl
* Licensed under MIT
* http://j.mp/respondjs */
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
(function(w) {
"use strict";
w.matchMedia = w.matchMedia || function(doc, undefined) {
var bool, docElem = doc.documentElement, refNode = docElem.firstElementChild || docElem.firstChild, fakeBody = doc.createElement("body"), div = doc.createElement("div");
div.id = "mq-test-1";
div.style.cssText = "position:absolute;top:-100em";
fakeBody.style.background = "none";
fakeBody.appendChild(div);
return function(q) {
div.innerHTML = '&shy;<style media="' + q + '"> #mq-test-1 { width: 42px; }</style>';
docElem.insertBefore(fakeBody, refNode);
bool = div.offsetWidth === 42;
docElem.removeChild(fakeBody);
return {
matches: bool,
media: q
};
};
}(w.document);
})(this);
(function(w) {
"use strict";
var respond = {};
w.respond = respond;
respond.update = function() {};
var requestQueue = [], xmlHttp = function() {
var xmlhttpmethod = false;
try {
xmlhttpmethod = new w.XMLHttpRequest();
} catch (e) {
xmlhttpmethod = new w.ActiveXObject("Microsoft.XMLHTTP");
}
return function() {
return xmlhttpmethod;
};
}(), ajax = function(url, callback) {
var req = xmlHttp();
if (!req) {
return;
}
req.open("GET", url, true);
req.onreadystatechange = function() {
if (req.readyState !== 4 || req.status !== 200 && req.status !== 304) {
return;
}
callback(req.responseText);
};
if (req.readyState === 4) {
return;
}
req.send(null);
}, isUnsupportedMediaQuery = function(query) {
return query.replace(respond.regex.minmaxwh, "").match(respond.regex.other);
};
respond.ajax = ajax;
respond.queue = requestQueue;
respond.unsupportedmq = isUnsupportedMediaQuery;
respond.regex = {
media: /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,
keyframes: /@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,
comments: /\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,
urls: /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,
findStyles: /@media *([^\{]+)\{([\S\s]+?)$/,
only: /(only\s+)?([a-zA-Z]+)\s?/,
minw: /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,
maxw: /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,
minmaxwh: /\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,
other: /\([^\)]*\)/g
};
respond.mediaQueriesSupported = w.matchMedia && w.matchMedia("only all") !== null && w.matchMedia("only all").matches;
if (respond.mediaQueriesSupported) {
return;
}
var doc = w.document, docElem = doc.documentElement, mediastyles = [], rules = [], appendedEls = [], parsedSheets = {}, resizeThrottle = 30, head = doc.getElementsByTagName("head")[0] || docElem, base = doc.getElementsByTagName("base")[0], links = head.getElementsByTagName("link"), lastCall, resizeDefer, eminpx, getEmValue = function() {
var ret, div = doc.createElement("div"), body = doc.body, originalHTMLFontSize = docElem.style.fontSize, originalBodyFontSize = body && body.style.fontSize, fakeUsed = false;
div.style.cssText = "position:absolute;font-size:1em;width:1em";
if (!body) {
body = fakeUsed = doc.createElement("body");
body.style.background = "none";
}
docElem.style.fontSize = "100%";
body.style.fontSize = "100%";
body.appendChild(div);
if (fakeUsed) {
docElem.insertBefore(body, docElem.firstChild);
}
ret = div.offsetWidth;
if (fakeUsed) {
docElem.removeChild(body);
} else {
body.removeChild(div);
}
docElem.style.fontSize = originalHTMLFontSize;
if (originalBodyFontSize) {
body.style.fontSize = originalBodyFontSize;
}
ret = eminpx = parseFloat(ret);
return ret;
}, applyMedia = function(fromResize) {
var name = "clientWidth", docElemProp = docElem[name], currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[name] || docElemProp, styleBlocks = {}, lastLink = links[links.length - 1], now = new Date().getTime();
if (fromResize && lastCall && now - lastCall < resizeThrottle) {
w.clearTimeout(resizeDefer);
resizeDefer = w.setTimeout(applyMedia, resizeThrottle);
return;
} else {
lastCall = now;
}
for (var i in mediastyles) {
if (mediastyles.hasOwnProperty(i)) {
var thisstyle = mediastyles[i], min = thisstyle.minw, max = thisstyle.maxw, minnull = min === null, maxnull = max === null, em = "em";
if (!!min) {
min = parseFloat(min) * (min.indexOf(em) > -1 ? eminpx || getEmValue() : 1);
}
if (!!max) {
max = parseFloat(max) * (max.indexOf(em) > -1 ? eminpx || getEmValue() : 1);
}
if (!thisstyle.hasquery || (!minnull || !maxnull) && (minnull || currWidth >= min) && (maxnull || currWidth <= max)) {
if (!styleBlocks[thisstyle.media]) {
styleBlocks[thisstyle.media] = [];
}
styleBlocks[thisstyle.media].push(rules[thisstyle.rules]);
}
}
}
for (var j in appendedEls) {
if (appendedEls.hasOwnProperty(j)) {
if (appendedEls[j] && appendedEls[j].parentNode === head) {
head.removeChild(appendedEls[j]);
}
}
}
appendedEls.length = 0;
for (var k in styleBlocks) {
if (styleBlocks.hasOwnProperty(k)) {
var ss = doc.createElement("style"), css = styleBlocks[k].join("\n");
ss.type = "text/css";
ss.media = k;
head.insertBefore(ss, lastLink.nextSibling);
if (ss.styleSheet) {
ss.styleSheet.cssText = css;
} else {
ss.appendChild(doc.createTextNode(css));
}
appendedEls.push(ss);
}
}
}, translate = function(styles, href, media) {
var qs = styles.replace(respond.regex.comments, "").replace(respond.regex.keyframes, "").match(respond.regex.media), ql = qs && qs.length || 0;
href = href.substring(0, href.lastIndexOf("/"));
var repUrls = function(css) {
return css.replace(respond.regex.urls, "$1" + href + "$2$3");
}, useMedia = !ql && media;
if (href.length) {
href += "/";
}
if (useMedia) {
ql = 1;
}
for (var i = 0; i < ql; i++) {
var fullq, thisq, eachq, eql;
if (useMedia) {
fullq = media;
rules.push(repUrls(styles));
} else {
fullq = qs[i].match(respond.regex.findStyles) && RegExp.$1;
rules.push(RegExp.$2 && repUrls(RegExp.$2));
}
eachq = fullq.split(",");
eql = eachq.length;
for (var j = 0; j < eql; j++) {
thisq = eachq[j];
if (isUnsupportedMediaQuery(thisq)) {
continue;
}
mediastyles.push({
media: thisq.split("(")[0].match(respond.regex.only) && RegExp.$2 || "all",
rules: rules.length - 1,
hasquery: thisq.indexOf("(") > -1,
minw: thisq.match(respond.regex.minw) && parseFloat(RegExp.$1) + (RegExp.$2 || ""),
maxw: thisq.match(respond.regex.maxw) && parseFloat(RegExp.$1) + (RegExp.$2 || "")
});
}
}
applyMedia();
}, makeRequests = function() {
if (requestQueue.length) {
var thisRequest = requestQueue.shift();
ajax(thisRequest.href, function(styles) {
translate(styles, thisRequest.href, thisRequest.media);
parsedSheets[thisRequest.href] = true;
w.setTimeout(function() {
makeRequests();
}, 0);
});
}
}, ripCSS = function() {
for (var i = 0; i < links.length; i++) {
var sheet = links[i], href = sheet.href, media = sheet.media, isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet";
if (!!href && isCSS && !parsedSheets[href]) {
if (sheet.styleSheet && sheet.styleSheet.rawCssText) {
translate(sheet.styleSheet.rawCssText, href, media);
parsedSheets[href] = true;
} else {
if (!/^([a-zA-Z:]*\/\/)/.test(href) && !base || href.replace(RegExp.$1, "").split("/")[0] === w.location.host) {
if (href.substring(0, 2) === "//") {
href = w.location.protocol + href;
}
requestQueue.push({
href: href,
media: media
});
}
}
}
}
makeRequests();
};
ripCSS();
respond.update = ripCSS;
respond.getEmValue = getEmValue;
function callMedia() {
applyMedia(true);
}
if (w.addEventListener) {
w.addEventListener("resize", callMedia, false);
} else if (w.attachEvent) {
w.attachEvent("onresize", callMedia);
}
})(this);

View File

@ -0,0 +1,41 @@
h1 {
text-align: center;
}
details {
summary {
cursor: pointer;
}
}
@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }
}
.message {
form > & {
display: none;
}
&.fade {
display: block;
animation: fadein .5s linear;
}
*:last-child {
margin-bottom: 0;
}
p {
margin-top: 8px;
button {
margin-left: 5px;
}
button:first-child {
margin-left: 0;
}
}
}

View File

@ -179,68 +179,6 @@ a.button:hover, a.balloon-button:hover {
/** 增加配置面板内部的错误样式 by 70 */
/**
* 安装样式
*
* @author mingcheng
* @date 2008-09-06
*/
/**
* 安装向导
*/
.typecho-install {
padding-bottom: 2em;
}
.typecho-install-patch {
margin-bottom: 2em;
padding: 2em 0;
background-color: #292D33;
color: #FFF;
text-align: center;
}
.typecho-install-patch ol {
list-style: none;
margin: 3em 0 1em;
padding: 0;
color: #999;
}
.typecho-install-patch li {
display: inline-block;
margin: 0 .8em;
}
.typecho-install-patch span {
display: inline-block;
margin-right: 5px;
width: 20px;
height: 20px;
line-height: 20px;
border: 2px solid #999;
text-align: center;
border-radius: 2em;
}
.typecho-install-patch li.current {
color: #FFF;
font-weight: bold;
}
.typecho-install-patch li.current span {
border-color: #FFF;
}
/**
* 安装主体内容
*/
.typecho-install .typecho-install-body input {
width: 100%;
}
.typecho-install-body .typecho-option li {
margin: 1em 0;
}
/**
* 欢迎界面
*/

View File

@ -29,17 +29,7 @@ Typecho_Common::init();
else:
require_once dirname(__FILE__) . '/config.inc.php';
//判断是否已经安装
$db = Typecho_Db::get();
try {
$installed = $db->fetchRow($db->select()->from('table.options')->where('name = ?', 'installed'));
if (empty($installed) || $installed['value'] == 1) {
exit(1);
}
} catch (Exception $e) {
// do nothing
}
$installDb = Typecho_Db::get();
endif;
@ -210,6 +200,28 @@ function install_get_db_drivers(): array {
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
*
@ -219,6 +231,8 @@ function install_get_db_drivers(): array {
* @return string
*/
function install_config_file(string $adapter, string $dbPrefix, array $dbConfig): string {
global $configWritten;
$lines = array_slice(file(__FILE__), 1, 26);
$lines[] = "
/** 定义数据库参数 */
@ -226,7 +240,57 @@ function install_config_file(string $adapter, string $dbPrefix, array $dbConfig)
\$db->addServer(" . (var_export($dbConfig, true)) . ", Typecho_Db::READ | Typecho_Db::WRITE);
Typecho_Db::set(\$db);
";
return implode('', $lines);
$code = implode('', $lines);
$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
$values = $installDb->fetchAll($installDb->select()->from('table.options')
->where('user = 0'));
if ($type == 'db_data' && empty($values)) {
return false;
}
} catch (Typecho_Db_Exception $e) {
return false;
}
return true;
default:
return false;
}
}
/**
@ -236,23 +300,20 @@ Typecho_Db::set(\$db);
* @param mixed $config
*/
function install_raise_error($error, $config = null) {
$lines = [];
if (is_array($error)) {
foreach ($error as $key => $value) {
$lines[] = $key . ': ' . $value;
}
} else {
$lines[] = $error;
}
if (install_is_cli()) {
echo implode("\n", $lines);
if (is_array($error)) {
foreach ($error as $key => $value) {
echo (is_int($key) ? '' : $key . ': ') . $value;
}
} else {
echo $error;
}
exit(1);
} else {
Typecho_Response::getInstance()->throwJson([
'success' => 0,
'message' => implode("<br>", $lines),
'message' => is_string($error) ? nl2br($error) : $error,
'config' => $config
]);
}
@ -260,60 +321,124 @@ function install_raise_error($error, $config = null) {
/**
* @param $step
* @param array|null $config
*/
function install_success($step) {
function install_success($step, ?array $config = null) {
if (install_is_cli()) {
$method = 'install_step_' . $step . '_perform';
$method();
if ($step > 0) {
$method = 'install_step_' . $step . '_perform';
$method();
}
if (!empty($config)) {
echo implode("\n", $config);
}
} else {
Typecho_Response::getInstance()->throwJson([
'success' => $step
'success' => 1,
'message' => $step,
'config' => $config
]);
}
}
function install_ajax_support() {
?>
<script>
let form = document.querySelector('form'), errorBox = document.createElement('div');
form.insertBefore(errorBox, form.firstChild);
$options = Typecho_Widget::widget('Widget_Options');
errorBox.classList.add('error', 'hidden');
?>
<div id="success" class="hidden">
<h2><?php _e('安装成功'); ?></h2>
<p class="keep-word">
<?php _e('您选择了使用原有的数据, 您的用户名和密码和原来的一致'); ?>
</p>
<p class="fresh-word">
<?php _e('您的用户名是'); ?>: <strong id="success-user"></strong><br>
<?php _e('您的密码是'); ?>: <strong id="success-password"></strong>
</p>
<ul>
<li><a href="<?php $options->loginUrl(); ?>"><?php _e('点击这里访问您的控制面板'); ?></a></li>
<li><a href="<?php $options->siteUrl(); ?>"><?php _e('点击这里查看您的 Blog'); ?></a></li>
</ul>
<p><?php _e('希望您能尽情享用 Typecho 带来的乐趣!'); ?></p>
</div>
<script>
let form = document.querySelector('form'), errorBox = document.createElement('div');
function showError(error) {
let str = '<?php _t('安装程序捕捉到以下错误: " %s ". 程序被终止, 请检查您的配置信息.', '%') ?>';
errorBox.innerHTML = str.replace('%', error);
form.insertBefore(errorBox, form.firstChild);
errorBox.classList.add('message', 'error');
errorBox.classList.remove('hidden');
return errorBox;
}
form.addEventListener('submit', function (e) {
e.preventDefault();
errorBox.classList.add('hidden');
fetch(this.getAttribute('action'), {
method: 'POST',
body: new FormData(this)
}).then(function (response) {
return response.json();
}).then(function (data) {
if (data.success) {
location.href = '?step=' + data.message;
function showError(error) {
if (typeof error == 'string') {
window.scrollTo({ top: 0 });
errorBox.innerHTML = error;
errorBox.classList.add('fade');
} else {
showError(data.message);
}
}).catch(function (error) {
let el = showError(error.message);
for (let k in error) {
let input = document.querySelector('#' + k), msg = error[k], p = document.createElement('p');
if (typeof configError == 'function' && error.config) {
configError(error.config, el);
p.classList.add('message', 'error');
p.innerHTML = msg;
input.parentNode.insertBefore(p, input.nextSibling);
input.addEventListener('input', function () {
p.remove();
});
}
}
});
}, true);
</script>
return errorBox;
}
form.addEventListener('submit', function (e) {
e.preventDefault();
errorBox.classList.remove('fade');
form.querySelectorAll('button').forEach(function (btn) {
btn.setAttribute('disabled', 'disabled');
});
form.querySelectorAll('.typecho-option .error').forEach(function (el) {
el.remove();
});
fetch(this.getAttribute('action'), {
method: 'POST',
body: new FormData(this)
}).then(function (response) {
return response.json();
}).then(function (data) {
form.querySelectorAll('button').forEach(function (btn) {
btn.removeAttribute('disabled');
});
if (data.success) {
if (data.message) {
location.href = '?step=' + data.message;
} else {
let success = document.querySelector('#success');
form.classList.add('hidden');
success.classList.remove('hidden');
if (data.config) {
success.classList.add('fresh');
} else {
success.classList.add('keep');
}
}
} else {
let el = showError(data.message);
if (typeof configError == 'function' && data.config) {
configError(data.config, el);
}
}
}).catch(function (error) {
showError(error.message);
});
}, true);
</script>
<?php
}
@ -321,92 +446,168 @@ function install_step_1() {
$langs = Widget_Options_General::getLangs();
$lang = install_get_lang();
?>
<form method="get" action="?step=2">
<h1><?php _e('欢迎使用 Typecho'); ?></h1>
<h2><?php _e('安装说明'); ?></h2>
<p><strong><?php _e('本安装程序将自动检测服务器环境是否符合最低配置需求. 如果不符合, 将在上方出现提示信息, 请按照提示信息检查您的主机配置. 如果服务器环境符合要求, 将在下方出现 "开始下一步" 的按钮, 点击此按钮即可一步完成安装.'); ?></strong></p>
<h2><?php _e('许可及协议'); ?></h2>
<p><?php _e('Typecho 基于 <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> 协议发布, 我们允许用户在 GPL 协议许可的范围内使用, 拷贝, 修改和分发此程序.'); ?>
<?php _e('在GPL许可的范围内, 您可以自由地将其用于商业以及非商业用途.'); ?></p>
<p><?php _e('Typecho 软件由其社区提供支持, 核心开发团队负责维护程序日常开发工作以及新特性的制定.'); ?>
<?php _e('如果您遇到使用上的问题, 程序中的 BUG, 以及期许的新功能, 欢迎您在社区中交流或者直接向我们贡献代码.'); ?>
<?php _e('对于贡献突出者, 他的名字将出现在贡献者名单中.'); ?></p>
<p class="submit">
<button type="submit"><?php _e('我准备好了, 开始下一步 &raquo;'); ?></button>
<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 method="get" action="install.php">
<h3><?php _e('安装说明'); ?></h3>
<ul>
<li class="warning"><strong><?php _e('本安装程序将自动检测服务器环境是否符合最低配置需求. 如果不符合, 将在上方出现提示信息, 请按照提示信息检查您的主机配置. 如果服务器环境符合要求, 将在下方出现 "开始下一步" 的按钮, 点击此按钮即可一步完成安装.'); ?></strong></li>
</ul>
<h3><?php _e('许可及协议'); ?></h3>
<ul>
<li><?php _e('Typecho 基于 <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> 协议发布, 我们允许用户在 GPL 协议许可的范围内使用, 拷贝, 修改和分发此程序.'); ?>
<?php _e('在GPL许可的范围内, 您可以自由地将其用于商业以及非商业用途.'); ?></li>
<li><?php _e('Typecho 软件由其社区提供支持, 核心开发团队负责维护程序日常开发工作以及新特性的制定.'); ?>
<?php _e('如果您遇到使用上的问题, 程序中的 BUG, 以及期许的新功能, 欢迎您在社区中交流或者直接向我们贡献代码.'); ?>
<?php _e('对于贡献突出者, 他的名字将出现在贡献者名单中.'); ?></li>
</ul>
<?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>
<p class="submit">
<button class="btn primary" type="submit"><?php _e('我准备好了, 开始下一步 &raquo;'); ?></button>
<input type="hidden" name="step" value="2">
<?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
}
/**
* check step 2
*/
function install_step_2_check(): bool {
return !file_exists(__TYPECHO_ROOT_DIR__ . '/config.inc.php');
}
/**
* display step 2
*/
function install_step_2() {
global $installDb;
$drivers = install_get_db_drivers();
$adapter = Typecho_Request::getInstance()->get('driver', key($drivers));
$adapter = install_get_current_db_driver();
$type = install_get_db_type($adapter);
?>
<form action="?step=2" 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>
<?php require_once './install/' . $type . '.php'; ?>
<li>
<label class="typecho-label" for="dbPrefix"><?php _e('数据库前缀'); ?></label>
<input type="text" class="text" name="dbPrefix" id="dbPrefix" value="<?php _v('dbPrefix', 'typecho_'); ?>" />
<p class="description"><?php _e('默认前缀是 "typecho_"'); ?></p>
</li>
</ul>
</form>
<script>
function configError(config, errorBox) {
let next = document.querySelector('#dbNext'),
line = document.createElement('p'),
form = document.querySelector('form');
errorBox.appendChild(line);
config.forEach(function (word, key) {
let btn = document.createElement('button');
btn.innerHTML = word;
btn.addEventListener('click', function () {
next.setAttribute('value', key);
});
line.appendChild(btn);
form.submit();
});
if (!empty($installDb)) {
$config = $installDb->getConfig(Typecho_Db::WRITE)->toArray();
$config['prefix'] = $installDb->getPrefix();
$config['adapter'] = $adapter;
}
</script>
?>
<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 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="<?php _v('dbPrefix', '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 type="submit" class="btn primary"><?php _e('确认, 开始安装 &raquo;'); ?></button>
<input type="hidden" name="step" value="2">
</li>
</ul>
</form>
</div>
</div>
<script>
function configError(config, errorBox) {
let next = document.querySelector('#dbNext'),
line = document.createElement('p'),
form = document.querySelector('form');
if (config.code) {
let text = document.createElement('textarea');
text.value = config.code;
text.setAttribute('readonly', 'readonly');
errorBox.appendChild(text);
return;
}
errorBox.appendChild(line);
for (let key in config) {
let word = config[key],
btn = document.createElement('button');
btn.innerHTML = word;
btn.setAttribute('type', 'button');
btn.classList.add('btn', 'btn-s', 'primary');
btn.addEventListener('click', function () {
next.value = key;
form.dispatchEvent(new Event('submit'));
});
line.appendChild(btn);
}
}
<?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 = document.querySelector('#' + key);
input.value = value;
if (input.children.length > 0) {
for (let i = 0; i < input.children.length; i ++) {
let option = input.children[i];
if (!option.selected) {
option.setAttribute('disabled', 'disabled');
}
}
}
input.setAttribute('readonly', 'readonly');
}
}
fillInput(<?php echo Json::encode($config); ?>);
<?php endif; ?>
</script>
<?php
install_ajax_support();
}
/**
* perform install step 2
*/
function install_step_2_perform() {
global $installDb;
$request = Typecho_Request::getInstance();
$drivers = install_get_db_drivers();
@ -484,38 +685,37 @@ function install_step_2_perform() {
$dbConfig = [];
foreach ($configMap[$type] as $key => $value) {
$dbConfig[strtolower(substr($key, 2))]
= $config[$key] === null ? (install_is_cli() ? $value : null) : $config[$key];
$dbConfig[$key] = $config[$key] === null ? (install_is_cli() ? $value : null) : $config[$key];
}
switch ($type) {
case 'Mysql':
$error = (new Typecho_Validate())
->addRule('host', 'required', _t('确认您的配置'))
->addRule('port', 'required', _t('确认您的配置'))
->addRule('port', 'isInteger', _t('确认您的配置'))
->addRule('user', 'required', _t('确认您的配置'))
->addRule('charset', 'required', _t('确认您的配置'))
->addRule('charset', 'enum', _t('确认您的配置'), ['utf8', 'utf8mb4'])
->addRule('database', 'required', _t('确认您的配置'))
->addRule('engine', 'required', _t('确认您的配置'))
->addRule('engine', 'enum', _t('确认您的配置'), ['InnoDB', 'MyISAM'])
->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'])
->run($dbConfig);
break;
case 'Pgsql':
$error = (new Typecho_Validate())
->addRule('host', 'required', _t('确认您的配置'))
->addRule('port', 'required', _t('确认您的配置'))
->addRule('port', 'isInteger', _t('确认您的配置'))
->addRule('user', 'required', _t('确认您的配置'))
->addRule('charset', 'required', _t('确认您的配置'))
->addRule('charset', 'enum', _t('确认您的配置'), ['utf8'])
->addRule('database', 'required', _t('确认您的配置'))
->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($dbConfig);
break;
case 'SQLite':
$error = (new Typecho_Validate())
->addRule('file', 'required', _t('确认您的配置'))
->addRule('dbFile', 'required', _t('确认您的配置'))
->run($dbConfig);
break;
default:
@ -527,19 +727,32 @@ function install_step_2_perform() {
install_raise_error($error);
}
// 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_Exception $e) {
install_raise_error(_t('对不起, 无法连接数据库, 请先检查数据库配置再继续进行安装'));
} catch (Typecho_Db_Exception $e) {
install_raise_error(_t('安装程序捕捉到以下错误: " %s ". 程序被终止, 请检查您的配置信息.', $e->getMessage()));
foreach ($dbConfig as $key => $value) {
$dbConfig[strtolower(substr($key, 2))] = $value;
}
if (!isset($installDb)) {
return;
if (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_Exception $e) {
install_raise_error(_t('对不起, 无法连接数据库, 请先检查数据库配置再继续进行安装'));
} 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
@ -597,14 +810,22 @@ function install_step_2_perform() {
('Pgsql' == $type && '42P07' == $code)) {
if ($config['dbNext'] == 'keep') {
install_success(3);
} elseif ($config['dbNext' == 'none']) {
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()));
}
}
@ -612,8 +833,279 @@ function install_step_2_perform() {
install_success(3);
}
$options = Typecho_Widget::widget('Widget_Options', install_get_default_options());
Typecho_Widget::widget('Widget_Init');
/**
* display step 3
*/
function install_step_3() {
$options = Typecho_Widget::widget('Widget_Options');
?>
<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 action="install.php" method="post">
<ul class="typecho-option">
<li>
<label class="typecho-label" for="userUrl"><?php _e('网站地址'); ?></label>
<input type="text" name="userUrl" id="userUrl" class="text" value="<?php $options->siteUrl(); ?>" />
<p class="description"><?php _e('这是程序自动匹配的网站路径, 如果不正确请修改它'); ?></p>
</li>
</ul>
<ul class="typecho-option">
<li>
<label class="typecho-label" for="userName"><?php _e('用户名'); ?></label>
<input 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 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_ajax_support();
}
/**
* perform step 3
*/
function install_step_3_perform() {
global $installDb;
$request = Typecho_Request::getInstance();
$defaultPassword = Typecho_Common::randString(8);
$options = Typecho_Widget::widget('Widget_Options');
if (install_is_cli()) {
$config = [
'userUrl' => $request->getServer('TYPECHO_SITE_URL', 'http://localhost'),
'userName' => $request->getServer('TYPECHO_USER_NAME', 'typecho'),
'userPassword' => $request->getServer('TYPECHO_USER_PASSWORD'),
'userMail' => $request->getServer('TYPECHO_USER_MAIL', 'admin@localhost')
];
} else {
$config = $request->from([
'userUrl',
'userName',
'userPassword',
'userMail',
]);
}
$error = (new Typecho_Validate())
->addRule('userUrl', 'required', _t('请填写您的网站地址'))
->addRule('userUrl', 'url', _t('请填写您的网站地址'))
->addRule('userName', 'required', _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 options
foreach (install_get_default_options() as $key => $value) {
$installDb->query(
$installDb->insert('table.options')->rows(['name' => $key, 'user' => 0, 'value' => $value])
);
}
// write user
$hasher = new PasswordHash(8, true);
$installDb->query(
$installDb->insert('table.users')->rows([
'name' => $config['userName'],
'password' => $hasher->HashPassword($config['userPassword']),
'mail' => $config['userMail'],
'url' => $options->siteUrl,
'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('只是一个默认分类')
])
);
$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' => 'http://typecho.org',
'ip' => '127.0.0.1',
'agent' => $options->generator,
'text' => '欢迎加入 Typecho 大家族',
'type' => 'comment',
'status' => 'approved',
'parent' => 0
])
);
} catch (Typecho_Db_Exception $e) {
install_raise_error($e->getMessage());
}
install_success(0, [
$config['userName'],
$config['userPassword']
]);
}
/**
* dispatch install action
*
* @throws Typecho_Exception
*/
function install_dispatch() {
// init default options
$options = Typecho_Widget::widget('Widget_Options', install_get_default_options());
Typecho_Widget::widget('Widget_Init');
// install finished yet
if (
install_check('config')
&& install_check('db_structure')
&& install_check('db_data')
) {
exit;
}
if (install_is_cli()) {
install_step_2_perform();
} else {
$request = Typecho_Request::getInstance();
$response = Typecho_Response::getInstance();
$step = $request->get('step');
$action = 1;
switch (true) {
case $step == 2:
if (!install_check('db_structure')) {
$action = 2;
} else {
$response->redirect('install.php?step=3');
}
break;
case $step == 3:
if (install_check('db_structure')) {
$action = 3;
} else {
$response->redirect('install.php?step=2');
}
break;
default:
break;
}
$method = 'install_step_' . $action;
if ($request->isPost() && $action > 1) {
$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') ?>" />
</head>
<body>
<div class="body container">
<h1><a href="http://typecho.org" target="_blank" class="i-logo">Typecho</a></h1>
<?php $method(); ?>
</div>
</body>
</html>
<?php
}
}
install_dispatch();
exit;
// 挡掉可能的跨站请求
if (!empty($_GET) || !empty($_POST)) {

View File

@ -1,43 +1,66 @@
<?php if(!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<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>
<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>
<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>
<li>
<label class="typecho-label" for="dbPassword"><?php _e('数据库密码'); ?></label>
<input type="password" class="text" name="dbPassword" id="dbPassword" value="" />
</li>
<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 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>
<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 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>
<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 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>
</details>

View File

@ -1,26 +1,37 @@
<?php if(!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<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>
<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>
<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>
<li>
<label class="typecho-label" for="dbPassword"><?php _e('数据库密码'); ?></label>
<input type="password" class="text" name="dbPassword" id="dbPassword" value="" />
</li>
<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 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" />

View File

@ -1,7 +1,9 @@
<?php if(!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<?php $defaultDir = __TYPECHO_ROOT_DIR__ . '/usr/' . uniqid() . '.db'; ?>
<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 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>

6179
tools/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
"license": "GPL-2.0-only",
"dependencies": {
"chalk": "^4.0.0",
"node-sass": "^6.0.1",
"node-sass": "^4.14.1",
"sprite-magic-importer": "^1.6.2",
"uglify-js": "^3.11.6"
}

View File

@ -1,6 +1,6 @@
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<!DOCTYPE HTML>
<html class="no-js">
<html>
<head>
<meta charset="<?php $this->options->charset(); ?>">
<meta http-equiv="X-UA-Compatible" content="IE=edge">

View File

@ -981,7 +981,11 @@ EOF;
public static function url($path, $prefix)
{
$path = (0 === strpos($path, './')) ? substr($path, 2) : $path;
return rtrim($prefix, '/') . '/' . str_replace('//', '/', ltrim($path, '/'));
return rtrim(
rtrim($prefix, '/') . '/'
. str_replace('//', '/', ltrim($path, '/')),
'/'
);
}
/**

View File

@ -25,7 +25,7 @@ class Typecho_Config implements Iterator
* @access private
* @var array
*/
private $_currentConfig = array();
private $_currentConfig = [];
/**
* 实例化一个当前配置
@ -33,7 +33,7 @@ class Typecho_Config implements Iterator
* @access public
* @param mixed $config 配置列表
*/
public function __construct($config = array())
public function __construct($config = [])
{
/** 初始化参数 */
$this->setDefault($config);
@ -43,10 +43,12 @@ class Typecho_Config implements Iterator
* 工厂模式实例化一个当前配置
*
* @access public
* @param array $config 配置列表
*
* @param array|string $config 配置列表
*
* @return Typecho_Config
*/
public static function factory($config = array())
public static function factory($config = []): Typecho_Config
{
return new Typecho_Config($config);
}
@ -55,11 +57,13 @@ class Typecho_Config implements Iterator
* 设置默认的配置
*
* @access public
*
* @param mixed $config 配置信息
* @param boolean $replace 是否替换已经存在的信息
*
* @return void
*/
public function setDefault($config, $replace = false)
public function setDefault($config, bool $replace = false)
{
if (empty($config)) {
return;
@ -130,7 +134,7 @@ class Typecho_Config implements Iterator
* @access public
* @return boolean
*/
public function valid()
public function valid(): bool
{
return false !== $this->current();
}
@ -142,9 +146,9 @@ class Typecho_Config implements Iterator
* @param string $name 配置名称
* @return mixed
*/
public function __get($name)
public function __get(string $name)
{
return isset($this->_currentConfig[$name]) ? $this->_currentConfig[$name] : NULL;
return $this->_currentConfig[$name] ?? null;
}
/**
@ -155,7 +159,7 @@ class Typecho_Config implements Iterator
* @param mixed $value 配置值
* @return void
*/
public function __set($name, $value)
public function __set(string $name, $value)
{
$this->_currentConfig[$name] = $value;
}
@ -165,10 +169,10 @@ class Typecho_Config implements Iterator
*
* @access public
* @param string $name 配置名称
* @param array $args 参数
* @param array|null $args 参数
* @return void
*/
public function __call($name, $args)
public function __call(string $name, ?array $args)
{
echo $this->_currentConfig[$name];
}
@ -180,7 +184,7 @@ class Typecho_Config implements Iterator
* @param string $name 配置名称
* @return boolean
*/
public function __isSet($name)
public function __isSet(string $name): bool
{
return isset($this->_currentConfig[$name]);
}
@ -191,8 +195,16 @@ class Typecho_Config implements Iterator
* @access public
* @return string
*/
public function __toString()
public function __toString(): string
{
return serialize($this->_currentConfig);
}
/**
* @return array
*/
public function toArray(): array
{
return $this->_currentConfig;
}
}

View File

@ -61,8 +61,7 @@ class Typecho_Db
/**
* 默认配置
*
* @access private
* @var Typecho_Config
* @var array
*/
private $_config;
@ -109,9 +108,10 @@ class Typecho_Db
*
* @param mixed $adapterName 适配器名称
* @param string $prefix 前缀
*
* @throws Typecho_Db_Exception
*/
public function __construct($adapterName, $prefix = 'typecho_')
public function __construct($adapterName, string $prefix = 'typecho_')
{
/** 获取适配器名称 */
$this->_adapterName = $adapterName;
@ -119,16 +119,20 @@ class Typecho_Db
/** 数据库适配器 */
$adapterName = 'Typecho_Db_Adapter_' . $adapterName;
if (!call_user_func(array($adapterName, 'isAvailable'))) {
if (!call_user_func([$adapterName, 'isAvailable'])) {
throw new Typecho_Db_Exception("Adapter {$adapterName} is not available");
}
$this->_prefix = $prefix;
/** 初始化内部变量 */
$this->_pool = array();
$this->_connectedPool = array();
$this->_config = array();
$this->_pool = [];
$this->_connectedPool = [];
$this->_config = [
self::READ => [],
self::WRITE => []
];
//实例化适配器对象
$this->_adapter = new $adapterName();
@ -140,7 +144,7 @@ class Typecho_Db
* @access public
* @return string
*/
public function getAdapterName()
public function getAdapterName(): string
{
return $this->_adapterName;
}
@ -151,54 +155,69 @@ class Typecho_Db
* @access public
* @return string
*/
public function getPrefix()
public function getPrefix(): string
{
return $this->_prefix;
}
/**
* getConfig
*
* @access public
* @return array
* @param Typecho_Config $config
* @param int $op
*/
public function getConfig()
public function addConfig(Typecho_Config $config, int $op)
{
return $this->_config;
if ($op & self::READ) {
$this->_config[self::READ][] = $config;
}
if ($op & self::WRITE) {
$this->_config[self::WRITE][] = $config;
}
}
/**
* getConfig
*
* @param int $op
*
* @return Typecho_Config
* @throws Typecho_Db_Exception
*/
public function getConfig(int $op): Typecho_Config
{
if (empty($this->_config[$op])) {
/** Typecho_Db_Exception */
throw new Typecho_Db_Exception('Missing Database Connection');
}
$key = array_rand($this->_config[$op]);
return $this->_config[$op][$key];
}
/**
* 重置连接池
*
*
* @return void
*/
public function flushPool()
{
$this->_connectedPool = array();
$this->_connectedPool = [];
}
/**
* 选择数据库
*
* @param int $op
* @return Typecho_Db_Adapter
*
* @param int $op
*
* @return mixed
* @throws Typecho_Db_Exception
*/
public function selectDb($op)
public function selectDb(int $op)
{
if (!isset($this->_connectedPool[$op])) {
if (empty($this->_pool[$op])) {
/** Typecho_Db_Exception */
throw new Typecho_Db_Exception('Missing Database Connection');
}
//获取相应读或写服务器连接池中的一个
$selectConnection = rand(0, count($this->_pool[$op]) - 1);
//获取随机获得的连接池配置
$selectConnectionConfig = $this->_config[$this->_pool[$op][$selectConnection]];
$selectConnectionConfig = $this->getConfig($op);
$selectConnectionHandle = $this->_adapter->connect($selectConnectionConfig);
$this->_connectedPool[$op] = &$selectConnectionHandle;
$this->_connectedPool[$op] = $selectConnectionHandle;
}
return $this->_connectedPool[$op];
@ -209,7 +228,7 @@ class Typecho_Db
*
* @return Typecho_Db_Query
*/
public function sql()
public function sql(): Typecho_Db_Query
{
return new Typecho_Db_Query($this->_adapter, $this->_prefix);
}
@ -218,35 +237,25 @@ class Typecho_Db
* 为多数据库提供支持
*
* @access public
* @param Typecho_Db $db 数据库实例
* @param array $config 数据库实例
* @param integer $op 数据库操作
* @return void
*/
public function addServer($config, $op)
public function addServer(array $config, int $op)
{
$this->_config[] = Typecho_Config::factory($config);
$key = count($this->_config) - 1;
/** 将连接放入池中 */
switch ($op) {
case self::READ:
case self::WRITE:
$this->_pool[$op][] = $key;
break;
default:
$this->_pool[self::READ][] = $key;
$this->_pool[self::WRITE][] = $key;
break;
}
$this->addConfig(Typecho_Config::factory($config), $op);
$this->flushPool();
}
/**
* 获取版本
*
* @param int $op
*
* @param int $op
*
* @return string
* @throws Typecho_Db_Exception
*/
public function getVersion($op = self::READ)
public function getVersion(int $op = self::READ): string
{
return $this->_adapter->getVersion($this->selectDb($op));
}
@ -270,7 +279,7 @@ class Typecho_Db
* @return Typecho_Db
* @throws Typecho_Db_Exception
*/
public static function get()
public static function get(): Typecho_Db
{
if (empty(self::$_instance)) {
/** Typecho_Db_Exception */
@ -283,24 +292,31 @@ class Typecho_Db
/**
* 选择查询字段
*
* @access public
* @param mixed $field 查询字段
* @param ...$ags
*
* @return Typecho_Db_Query
* @throws Typecho_Db_Exception
*/
public function select()
public function select(...$ags): Typecho_Db_Query
{
$this->selectDb(self::READ);
$args = func_get_args();
return call_user_func_array(array($this->sql(), 'select'), $args ? $args : array('*'));
return call_user_func_array([$this->sql(), 'select'], $args ?: ['*']);
}
/**
* 更新记录操作(UPDATE)
*
* @param string $table 需要更新记录的表
*
* @return Typecho_Db_Query
* @throws Typecho_Db_Exception
*/
public function update($table)
public function update(string $table): Typecho_Db_Query
{
$this->selectDb(self::WRITE);
return $this->sql()->update($table);
}
@ -308,10 +324,14 @@ class Typecho_Db
* 删除记录操作(DELETE)
*
* @param string $table 需要删除记录的表
*
* @return Typecho_Db_Query
* @throws Typecho_Db_Exception
*/
public function delete($table)
public function delete(string $table): Typecho_Db_Query
{
$this->selectDb(self::WRITE);
return $this->sql()->delete($table);
}
@ -319,10 +339,14 @@ class Typecho_Db
* 插入记录操作(INSERT)
*
* @param string $table 需要插入记录的表
*
* @return Typecho_Db_Query
* @throws Typecho_Db_Exception
*/
public function insert($table)
public function insert(string $table): Typecho_Db_Query
{
$this->selectDb(self::WRITE);
return $this->sql()->insert($table);
}
@ -342,19 +366,20 @@ class Typecho_Db
* @param mixed $query 查询语句或者查询对象
* @param int $op 数据库读写状态
* @param string $action 操作动作
*
* @return mixed
* @throws Typecho_Db_Exception
*/
public function query($query, $op = self::READ, $action = self::SELECT)
public function query($query, int $op = self::READ, string $action = self::SELECT)
{
$table = NULL;
$table = null;
/** 在适配器中执行查询 */
if ($query instanceof Typecho_Db_Query) {
$action = $query->getAttribute('action');
$table = $query->getAttribute('table');
$op = (self::UPDATE == $action || self::DELETE == $action
|| self::INSERT == $action) ? self::WRITE : self::READ;
|| self::INSERT == $action) ? self::WRITE : self::READ;
} else if (!is_string($query)) {
/** 如果query不是对象也不是字符串,那么将其判断为查询资源句柄,直接返回 */
return $query;
@ -389,24 +414,26 @@ class Typecho_Db
* 一次取出所有行
*
* @param mixed $query 查询对象
* @param array $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
* @param array|null $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
*
* @return array
* @throws Typecho_Db_Exception
*/
public function fetchAll($query, array $filter = NULL)
public function fetchAll($query, ?array $filter = null): array
{
//执行查询
$resource = $this->query($query, self::READ);
$result = array();
$result = [];
/** 取出过滤器 */
if (!empty($filter)) {
list($object, $method) = $filter;
[$object, $method] = $filter;
}
//取出每一行
while ($rows = $this->_adapter->fetch($resource)) {
//判断是否有过滤器
$result[] = $filter ? call_user_func(array(&$object, $method), $rows) : $rows;
$result[] = $filter ? call_user_func([&$object, $method], $rows) : $rows;
}
return $result;
@ -416,41 +443,45 @@ class Typecho_Db
* 一次取出一行
*
* @param mixed $query 查询对象
* @param array $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
* @param array|null $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
*
* @return mixed
* @throws Typecho_Db_Exception
*/
public function fetchRow($query, array $filter = NULL)
public function fetchRow($query, ?array $filter = null)
{
$resource = $this->query($query, self::READ);
/** 取出过滤器 */
if ($filter) {
list($object, $method) = $filter;
[$object, $method] = $filter;
}
return ($rows = $this->_adapter->fetch($resource)) ?
($filter ? $object->$method($rows) : $rows) :
array();
($filter ? $object->$method($rows) : $rows) :
[];
}
/**
* 一次取出一个对象
*
* @param mixed $query 查询对象
* @param array $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
* @param array|null $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
*
* @return mixed
* @throws Typecho_Db_Exception
*/
public function fetchObject($query, array $filter = NULL)
public function fetchObject($query, ?array $filter = null)
{
$resource = $this->query($query, self::READ);
/** 取出过滤器 */
if ($filter) {
list($object, $method) = $filter;
[$object, $method] = $filter;
}
return ($rows = $this->_adapter->fetchObject($resource)) ?
($filter ? $object->$method($rows) : $rows) :
new stdClass();
($filter ? $object->$method($rows) : $rows) :
new stdClass();
}
}

View File

@ -27,7 +27,7 @@ interface Typecho_Db_Adapter
* 数据库连接函数
*
* @param Typecho_Config $config 数据库配置
* @return resource
* @return mixed
*/
public function connect(Typecho_Config $config);

View File

@ -39,7 +39,7 @@ class Typecho_Db_Adapter_Mysql implements Typecho_Db_Adapter
*
* @param Typecho_Config $config 数据库配置
* @throws Typecho_Db_Exception
* @return resource
* @return mixed
*/
public function connect(Typecho_Config $config)
{

View File

@ -19,7 +19,7 @@ class Typecho_Db_Adapter_Mysqli implements Typecho_Db_Adapter
* 数据库连接字符串标示
*
* @access private
* @var resource
* @var mysqli
*/
private $_dbLink;
@ -39,12 +39,18 @@ class Typecho_Db_Adapter_Mysqli implements Typecho_Db_Adapter
*
* @param Typecho_Config $config 数据库配置
* @throws Typecho_Db_Exception
* @return resource
* @return mixed
*/
public function connect(Typecho_Config $config)
{
if ($this->_dbLink = @new MySQLi($config->host, $config->user, $config->password, $config->database, (empty($config->port) ? '' : $config->port))) {
if ($this->_dbLink = @mysqli_connect(
$config->host,
$config->user,
$config->password,
$config->database,
(empty($config->port) ? null : $config->port))
) {
if ($config->charset) {
$this->_dbLink->query("SET NAMES '{$config->charset}'");
}

View File

@ -165,10 +165,10 @@ class Typecho_Validate
* 是否为空
*
* @access public
* @param string $str 待处理的字符串
* @param string|null $str 待处理的字符串
* @return boolean
*/
public function required(string $str): bool
public function required(?string $str): bool
{
return !empty($this->_data[$this->_key]);
}