Commit 499d4fbf authored by Liang Ding's avatar Liang Ding

🐛 Fix #19

parent 66478b73
...@@ -46,7 +46,7 @@ import org.json.JSONObject; ...@@ -46,7 +46,7 @@ import org.json.JSONObject;
* Server. * Server.
* *
* @author <a href="http://88250.b3log.org">Liang Ding</a> * @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 2.0.0.6, Jan 3, 2020 * @version 2.0.0.7, Jan 13, 2020
* @since 1.2.0 * @since 1.2.0
*/ */
public final class Server extends BaseServer { public final class Server extends BaseServer {
...@@ -373,6 +373,7 @@ public final class Server extends BaseServer { ...@@ -373,6 +373,7 @@ public final class Server extends BaseServer {
"/admin-category-list.do", "/admin-category-list.do",
"/admin-theme-list.do", "/admin-theme-list.do",
"/admin-plugin-list.do", "/admin-plugin-list.do",
"/admin-staticsite.do",
"/admin-main.do", "/admin-main.do",
"/admin-about.do"}, adminConsole::showAdminFunctions); "/admin-about.do"}, adminConsole::showAdminFunctions);
Dispatcher.get("/console/export/sql", adminConsole::exportSQL); Dispatcher.get("/console/export/sql", adminConsole::exportSQL);
...@@ -426,6 +427,8 @@ public final class Server extends BaseServer { ...@@ -426,6 +427,8 @@ public final class Server extends BaseServer {
Dispatcher.post("/console/plugin/toSetting", pluginConsole::toSetting); Dispatcher.post("/console/plugin/toSetting", pluginConsole::toSetting);
Dispatcher.post("/console/plugin/updateSetting", pluginConsole::updateSetting); Dispatcher.post("/console/plugin/updateSetting", pluginConsole::updateSetting);
final PreferenceConsole preferenceConsole = beanManager.getReference(PreferenceConsole.class); final PreferenceConsole preferenceConsole = beanManager.getReference(PreferenceConsole.class);
Dispatcher.get("/console/signs/", preferenceConsole::getSigns); Dispatcher.get("/console/signs/", preferenceConsole::getSigns);
Dispatcher.get("/console/preference/", preferenceConsole::getPreference); Dispatcher.get("/console/preference/", preferenceConsole::getPreference);
......
...@@ -15,23 +15,26 @@ ...@@ -15,23 +15,26 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package org.b3log.solo.processor; package org.b3log.solo.processor.console;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils; import org.apache.commons.lang.time.DateFormatUtils;
import org.b3log.latke.Latkes; import org.b3log.latke.Latkes;
import org.b3log.latke.http.HttpMethod;
import org.b3log.latke.http.RequestContext; import org.b3log.latke.http.RequestContext;
import org.b3log.latke.http.annotation.Before; import org.b3log.latke.http.annotation.Before;
import org.b3log.latke.http.annotation.RequestProcessing; import org.b3log.latke.http.annotation.RequestProcessing;
import org.b3log.latke.http.annotation.RequestProcessor; import org.b3log.latke.http.annotation.RequestProcessor;
import org.b3log.latke.ioc.BeanManager; import org.b3log.latke.ioc.BeanManager;
import org.b3log.latke.ioc.Inject;
import org.b3log.latke.logging.Level; import org.b3log.latke.logging.Level;
import org.b3log.latke.logging.Logger; import org.b3log.latke.logging.Logger;
import org.b3log.latke.service.LangPropsService;
import org.b3log.latke.util.CollectionUtils; import org.b3log.latke.util.CollectionUtils;
import org.b3log.latke.util.Strings;
import org.b3log.solo.model.*; import org.b3log.solo.model.*;
import org.b3log.solo.processor.console.ConsoleAuthAdvice;
import org.b3log.solo.service.*; import org.b3log.solo.service.*;
import org.b3log.solo.util.Mocks; import org.b3log.solo.util.Mocks;
import org.b3log.solo.util.Solos; import org.b3log.solo.util.Solos;
...@@ -44,52 +47,50 @@ import java.nio.charset.StandardCharsets; ...@@ -44,52 +47,50 @@ import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
/** /**
* Static site processor. HTML 静态站点生成 https://github.com/88250/solo/issues/19 * Static site console request processing. HTML 静态站点生成 https://github.com/88250/solo/issues/19
* *
* @author <a href="http://88250.b3log.org">Liang Ding</a> * @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.1, Jan 8, 2020 * @version 1.0.0.0, Jan 13, 2020
* @since 3.9.0 * @since 3.9.0
*/ */
@RequestProcessor @RequestProcessor
@Before(ConsoleAuthAdvice.class) @Before(ConsoleAdminAuthAdvice.class)
public class StaticSiteProcessor { public class StaticSiteConsole {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(StaticSiteProcessor.class); private static final Logger LOGGER = Logger.getLogger(StaticSiteConsole.class);
/** /**
* Name of generate directory. * Language service.
*/
private static final String STATIC_SITE = "static-site";
/**
* Path of generate directory.
*/ */
private static final String staticSitePath = StaticSiteProcessor.class.getResource("/" + STATIC_SITE).getPath(); @Inject
private LangPropsService langPropsService;
/**
* Source directory path.
*/
private static final String sourcePath = StaticSiteProcessor.class.getResource("/").getPath();
/** /**
* Generates static site. * Generates static site.
* *
* @param context the specified context * @param context the specified request context
*/ */
@RequestProcessing(value = "/static-site") @RequestProcessing(value = "/console/staticsite", method = HttpMethod.PUT)
public synchronized void genStaticSite(final RequestContext context) { public synchronized void genSite(final RequestContext context) {
try { try {
final JSONObject requestJSONObject = context.requestJSON();
final String url = requestJSONObject.optString(Common.URL);
if (!Strings.isURL(url)) {
context.renderJSON(-1);
context.renderMsg("Invalid site URL");
return;
}
FileUtils.deleteDirectory(new File(staticSitePath)); FileUtils.deleteDirectory(new File(staticSitePath));
FileUtils.forceMkdir(new File(staticSitePath)); FileUtils.forceMkdir(new File(staticSitePath));
// 切换至静态站点生成模式 // 切换至静态站点生成模式
Latkes.setServerScheme("https"); Latkes.setServerScheme("https");
// TODO: 前端传入生成站点域名 Latkes.setServerHost(url);
Latkes.setServerHost("88250.github.io");
// Latkes.setServerHost("dl88250.gitee.io");
Latkes.setServerPort(""); Latkes.setServerPort("");
Solos.GEN_STATIC_SITE = true; Solos.GEN_STATIC_SITE = true;
...@@ -121,13 +122,34 @@ public class StaticSiteProcessor { ...@@ -121,13 +122,34 @@ public class StaticSiteProcessor {
Solos.GEN_STATIC_SITE = false; Solos.GEN_STATIC_SITE = false;
LOGGER.log(Level.INFO, "Static site generated [dir=" + staticSitePath + "]"); LOGGER.log(Level.INFO, "Static site generated [dir=" + staticSitePath + "]");
String siteGenedLabel = langPropsService.get("siteGenedLabel");
siteGenedLabel = siteGenedLabel.replace("{dir}", staticSitePath);
context.renderJSON(0); context.renderJSON(0);
context.renderMsg(siteGenedLabel);
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.ERROR, "Generates static site failed", e); LOGGER.log(Level.ERROR, "Generates static site failed", e);
context.renderJSON(-1); context.renderJSON(-1);
context.renderMsg(langPropsService.get("updateFailLabel"));
} }
} }
/**
* Name of generate directory.
*/
private static final String STATIC_SITE = "static-site";
/**
* Path of generate directory.
*/
private static final String staticSitePath = StaticSiteConsole.class.getResource("/" + STATIC_SITE).getPath();
/**
* Source directory path.
*/
private static final String sourcePath = StaticSiteConsole.class.getResource("/").getPath();
private static void genCategories() throws Exception { private static void genCategories() throws Exception {
final BeanManager beanManager = BeanManager.getInstance(); final BeanManager beanManager = BeanManager.getInstance();
final CategoryQueryService categoryQueryService = beanManager.getReference(CategoryQueryService.class); final CategoryQueryService categoryQueryService = beanManager.getReference(CategoryQueryService.class);
...@@ -248,28 +270,28 @@ public class StaticSiteProcessor { ...@@ -248,28 +270,28 @@ public class StaticSiteProcessor {
private static void genSkins() throws Exception { private static void genSkins() throws Exception {
FileUtils.deleteDirectory(new File(staticSitePath + "/skins")); FileUtils.deleteDirectory(new File(staticSitePath + "/skins"));
FileUtils.forceMkdir(new File(staticSitePath + "/skins")); FileUtils.forceMkdir(new File(staticSitePath + "/skins"));
FileUtils.copyDirectory(new File(StaticSiteProcessor.class.getResource("/skins").toURI()), new File(staticSitePath + "/skins")); FileUtils.copyDirectory(new File(StaticSiteConsole.class.getResource("/skins").toURI()), new File(staticSitePath + "/skins"));
LOGGER.log(Level.INFO, "Generated skins"); LOGGER.log(Level.INFO, "Generated skins");
} }
private static void genJS() throws Exception { private static void genJS() throws Exception {
FileUtils.deleteDirectory(new File(staticSitePath + "/js")); FileUtils.deleteDirectory(new File(staticSitePath + "/js"));
FileUtils.forceMkdir(new File(staticSitePath + "/js")); FileUtils.forceMkdir(new File(staticSitePath + "/js"));
FileUtils.copyDirectory(new File(StaticSiteProcessor.class.getResource("/js").toURI()), new File(staticSitePath + "/js")); FileUtils.copyDirectory(new File(StaticSiteConsole.class.getResource("/js").toURI()), new File(staticSitePath + "/js"));
LOGGER.log(Level.INFO, "Generated js"); LOGGER.log(Level.INFO, "Generated js");
} }
private static void genImages() throws Exception { private static void genImages() throws Exception {
FileUtils.deleteDirectory(new File(staticSitePath + "/images")); FileUtils.deleteDirectory(new File(staticSitePath + "/images"));
FileUtils.forceMkdir(new File(staticSitePath + "/images")); FileUtils.forceMkdir(new File(staticSitePath + "/images"));
FileUtils.copyDirectory(new File(StaticSiteProcessor.class.getResource("/images").toURI()), new File(staticSitePath + "/images")); FileUtils.copyDirectory(new File(StaticSiteConsole.class.getResource("/images").toURI()), new File(staticSitePath + "/images"));
LOGGER.log(Level.INFO, "Generated images"); LOGGER.log(Level.INFO, "Generated images");
} }
private static void genPlugins() throws Exception { private static void genPlugins() throws Exception {
FileUtils.deleteDirectory(new File(staticSitePath + "/plugins")); FileUtils.deleteDirectory(new File(staticSitePath + "/plugins"));
FileUtils.forceMkdir(new File(staticSitePath + "/plugins")); FileUtils.forceMkdir(new File(staticSitePath + "/plugins"));
FileUtils.copyDirectory(new File(StaticSiteProcessor.class.getResource("/plugins").toURI()), new File(staticSitePath + "/plugins")); FileUtils.copyDirectory(new File(StaticSiteConsole.class.getResource("/plugins").toURI()), new File(staticSitePath + "/plugins"));
genURI("/plugins/kanbanniang/assets/model.json"); genURI("/plugins/kanbanniang/assets/model.json");
LOGGER.log(Level.INFO, "Generated plugins"); LOGGER.log(Level.INFO, "Generated plugins");
...@@ -277,8 +299,9 @@ public class StaticSiteProcessor { ...@@ -277,8 +299,9 @@ public class StaticSiteProcessor {
private static void genFile(final String file) throws Exception { private static void genFile(final String file) throws Exception {
FileUtils.forceMkdirParent(new File(staticSitePath + "/" + file)); FileUtils.forceMkdirParent(new File(staticSitePath + "/" + file));
final String staticSitePath = StaticSiteProcessor.class.getResource("/" + STATIC_SITE).toURI().getPath(); final String staticSitePath = StaticSiteConsole.class.getResource("/" + STATIC_SITE).toURI().getPath();
FileUtils.copyFile(new File(sourcePath + "/" + file), new File(staticSitePath + "/" + file)); FileUtils.copyFile(new File(sourcePath + "/" + file), new File(staticSitePath + "/" + file));
LOGGER.log(Level.INFO, "Generated a file [" + file + "]"); LOGGER.log(Level.INFO, "Generated a file [" + file + "]");
} }
} }
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
</a> </a>
</div> </div>
</li> </li>
<li> <li id="tools">
<div id="tabToolsTitle" onclick="admin.collapseNav(this)"> <div id="tabToolsTitle" onclick="admin.collapseNav(this)">
<span class="icon-setting"></span> <span class="icon-setting"></span>
${ToolLabel} ${ToolLabel}
...@@ -126,6 +126,11 @@ ...@@ -126,6 +126,11 @@
<a href="#tools/plugin-list">${pluginMgmtLabel}</a> <a href="#tools/plugin-list">${pluginMgmtLabel}</a>
</div> </div>
</li> </li>
<li>
<div id="tabs_staticsite">
<a href="#tools/staticsite">${staticsiteMgmtLabel}</a>
</div>
</li>
<li> <li>
<div id="tabs_others"> <div id="tabs_others">
<a href="#tools/others/tag">${othersLabel}</a> <a href="#tools/others/tag">${othersLabel}</a>
...@@ -157,6 +162,7 @@ ...@@ -157,6 +162,7 @@
<div id="tabsPanel_user-list" class="fn__none"></div> <div id="tabsPanel_user-list" class="fn__none"></div>
<div id="tabsPanel_comment-list" class="fn__none"></div> <div id="tabsPanel_comment-list" class="fn__none"></div>
<div id="tabsPanel_plugin-list" class="fn__none"></div> <div id="tabsPanel_plugin-list" class="fn__none"></div>
<div id="tabsPanel_staticsite" class="fn__none"></div>
<div id="tabsPanel_about" class="fn__none"></div> <div id="tabsPanel_about" class="fn__none"></div>
</div> </div>
<div class="fn__clear"></div> <div class="fn__clear"></div>
...@@ -184,6 +190,7 @@ ...@@ -184,6 +190,7 @@
<script src="${staticServePath}/js/admin/categoryList.js"></script> <script src="${staticServePath}/js/admin/categoryList.js"></script>
<script src="${staticServePath}/js/admin/commentList.js"></script> <script src="${staticServePath}/js/admin/commentList.js"></script>
<script src="${staticServePath}/js/admin/plugin.js"></script> <script src="${staticServePath}/js/admin/plugin.js"></script>
<script src="${staticServePath}/js/admin/staticsite.js"></script>
<script src="${staticServePath}/js/admin/main.js"></script> <script src="${staticServePath}/js/admin/main.js"></script>
<script src="${staticServePath}/js/admin/about.js"></script> <script src="${staticServePath}/js/admin/about.js"></script>
<script src="${staticServePath}/js/admin/themeList.js"></script> <script src="${staticServePath}/js/admin/themeList.js"></script>
......
<#--
Solo - A small and beautiful blogging system written in Java.
Copyright (c) 2010-present, b3log.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<div class="form form__no-table">
${configStaticSiteGenLabel}
<label>${siteURLLabel}</label>
<input id="siteURL" type="text" placeholder="https://yourname.github.io"/>
<br><br>
<button onclick="admin.staticsite.update();" class="fn__right">${generateLabel}</button>
<div class="fn__clear"></div>
</div>
${plugins}
\ No newline at end of file
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
* *
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a> * @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @author <a href="http://88250.b3log.org">Liang Ding</a> * @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.4.0.0, Apr 22, 2019 * @version 1.4.0.1, Jan 13, 2020
*/ */
Util.htmlDecode = function (code) { Util.htmlDecode = function (code) {
...@@ -34,11 +34,7 @@ var Admin = function () { ...@@ -34,11 +34,7 @@ var Admin = function () {
// 工具栏下的工具 // 工具栏下的工具
this.tools = [ this.tools = [
'#page-list', '#theme-list', '#link-list', '#preference', '#page-list', '#theme-list', '#link-list', '#preference',
'#user-list', '#plugin-list', '#others', '#category-list'] '#user-list', '#plugin-list', '#others', '#category-list', "#staticsite"]
// 多用户时,一般用户不能使用的功能
this.adTools = [
'link-list', 'preference', 'theme-list', 'page-list',
'user-list', 'plugin-list', 'others', 'category-list']
} }
$.extend(Admin.prototype, { $.extend(Admin.prototype, {
...@@ -264,9 +260,7 @@ $.extend(Admin.prototype, { ...@@ -264,9 +260,7 @@ $.extend(Admin.prototype, {
inited: function () { inited: function () {
// Removes functions with the current user role // Removes functions with the current user role
if (Label.userRole !== 'adminRole') { if (Label.userRole !== 'adminRole') {
for (var i = 0; i < this.adTools.length; i++) { $('#tools').remove();
$('#tabs').tabs('remove', this.adTools[i])
}
} else { } else {
// 当前 tab 属于 Tools 时,设其展开 // 当前 tab 属于 Tools 时,设其展开
for (var j = 0; j < this.tools.length; j++) { for (var j = 0; j < this.tools.length; j++) {
......
/*
* Solo - A small and beautiful blogging system written in Java.
* Copyright (c) 2010-present, b3log.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* staticsite for admin.
*
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.0, Jan 13, 2020
*/
/* staticsite 相关操作 */
admin.staticsite = {
/*
* 初始化
*/
init: function () {
$('#loadMsg').text('')
},
/*
* @description 更新
*/
update: function () {
$('#tipMsg').text('')
$('#loadMsg').text(Label.loadingLabel)
var requestJSONObject = {
'url': $('#siteURL').val(),
}
$.ajax({
url: Label.servePath + '/console/staticsite',
type: 'PUT',
cache: false,
data: JSON.stringify(requestJSONObject),
success: function (result, textStatus) {
$('#tipMsg').text(result.msg)
$('#loadMsg').text('')
},
})
},
}
/*
* 注册到 admin 进行管理
*/
admin.register['staticsite'] = {
'obj': admin.staticsite,
'init': admin.staticsite.init,
'refresh': function () {
admin.clearTip()
},
}
...@@ -18,12 +18,16 @@ ...@@ -18,12 +18,16 @@
# #
# Description: Solo language configurations(en_US). # Description: Solo language configurations(en_US).
# Version: 2.36.0.0, Jan 2, 2020 # Version: 2.37.0.0, Jan 13, 2020
# Author: Liang Ding # Author: Liang Ding
# Author: Liyuan Li # Author: Liyuan Li
# Author: Dongxu Wang # Author: Dongxu Wang
# #
siteURLLabel=Site URL:
siteGenedLabel=Site generated, target dir is [{dir}]
generateLabel=Gen
configStaticSiteGenLabel=Static site generate
siteLinkLabel=Site links siteLinkLabel=Site links
setMobileLabel=Set to mobile skin setMobileLabel=Set to mobile skin
configSiteLabel=Go to the site link to configure GitHub, Twitter, etc. configSiteLabel=Go to the site link to configure GitHub, Twitter, etc.
...@@ -125,6 +129,7 @@ updateCategoryLabel=Update Category ...@@ -125,6 +129,7 @@ updateCategoryLabel=Update Category
linkManagementLabel=Links linkManagementLabel=Links
categoryListLabel=Category categoryListLabel=Category
pluginMgmtLabel=Plugins pluginMgmtLabel=Plugins
staticsiteMgmtLabel=Static Site
pluginNameLabel=Name pluginNameLabel=Name
versionLabel=Version versionLabel=Version
statusLabel=Status statusLabel=Status
......
...@@ -18,12 +18,16 @@ ...@@ -18,12 +18,16 @@
# #
# Description: Solo default language configurations(zh_CN). # Description: Solo default language configurations(zh_CN).
# Version: 2.36.0.0, Jan 2, 2020 # Version: 2.37.0.0, Jan 13, 2020
# Author: Liang Ding # Author: Liang Ding
# Author: Liyuan Li # Author: Liyuan Li
# Author: Dongxu Wang # Author: Dongxu Wang
# #
siteURLLabel=\u7AD9\u70B9\u5730\u5740\uFF1A
siteGenedLabel=\u7AD9\u70B9\u751F\u6210\u5B8C\u6BD5\uFF0C\u8BF7\u67E5\u770B\u76EE\u5F55 [{dir}]
generateLabel=\u751F\u6210
configStaticSiteGenLabel=\u9759\u6001\u7AD9\u70B9\u751F\u6210
siteLinkLabel=\u7AD9\u70B9\u8FDE\u63A5 siteLinkLabel=\u7AD9\u70B9\u8FDE\u63A5
setMobileLabel=\u8BBE\u7F6E\u4E3A\u79FB\u52A8\u7AEF\u76AE\u80A4 setMobileLabel=\u8BBE\u7F6E\u4E3A\u79FB\u52A8\u7AEF\u76AE\u80A4
configSiteLabel=\u524D\u5F80\u914D\u7F6E GitHub\uFF0CTwitter \u7B49\u7AD9\u70B9\u94FE\u63A5 configSiteLabel=\u524D\u5F80\u914D\u7F6E GitHub\uFF0CTwitter \u7B49\u7AD9\u70B9\u94FE\u63A5
...@@ -125,6 +129,7 @@ updateCategoryLabel=\u66F4\u65B0\u5206\u7C7B ...@@ -125,6 +129,7 @@ updateCategoryLabel=\u66F4\u65B0\u5206\u7C7B
linkManagementLabel=\u94FE\u63A5\u7BA1\u7406 linkManagementLabel=\u94FE\u63A5\u7BA1\u7406
categoryListLabel=\u5206\u7C7B\u7BA1\u7406 categoryListLabel=\u5206\u7C7B\u7BA1\u7406
pluginMgmtLabel=\u63D2\u4EF6\u7BA1\u7406 pluginMgmtLabel=\u63D2\u4EF6\u7BA1\u7406
staticsiteMgmtLabel=\u9759\u6001\u7AD9\u70B9
pluginNameLabel=\u63D2\u4EF6\u540D pluginNameLabel=\u63D2\u4EF6\u540D
versionLabel=\u7248\u672C versionLabel=\u7248\u672C
statusLabel=\u72B6\u6001 statusLabel=\u72B6\u6001
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment