Commit 94257e6b authored by Van's avatar Van

Merge remote-tracking branch 'origin/3.0.0-dev' into 3.0.0-dev

parents 6f87f922 af06067d
<?xml version="1.0" encoding="UTF-8"?>
<!--
Description: Solo POM.
Version: 3.18.3.46, Feb 7, 2019
Version: 3.18.3.46, Feb 10, 2019
Author: <a href="http://88250.b3log.org">Liang Ding</a>
Author: <a href="http://www.annpeter.cn">Ann Peter</a>
Author: <a href="http://vanessa.b3log.org">Vanessa</a>
......@@ -18,7 +18,7 @@
<name>Solo</name>
<version>2.9.9</version>
<description>
一款小而美的 Java 博客系统
一款小而美的博客系统,专为程序员设计
</description>
<inceptionYear>2010</inceptionYear>
......@@ -228,14 +228,6 @@
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.patchca</groupId>
<artifactId>patchca</artifactId>
<version>0.5.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/net/pusuo/patchca-0.5.0.jar</systemPath>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
......@@ -371,18 +363,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<webResources>
<resource>
<directory>${project.basedir}/src/main/resources/lib/net/pusuo</directory>
<targetPath>WEB-INF/lib</targetPath>
<filtering>false</filtering>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
</plugins>
......
......@@ -332,7 +332,6 @@ public final class SoloServletListener extends AbstractServletListener {
final ArticleConsole articleConsole = beanManager.getReference(ArticleConsole.class);
DispatcherServlet.get("/console/article/push2rhy", articleConsole::pushArticleToCommunity);
DispatcherServlet.get("/console/thumbs", articleConsole::getArticleThumbs);
DispatcherServlet.post("/console/markdown/2html", articleConsole::markdown2HTML);
DispatcherServlet.get("/console/article/{id}", articleConsole::getArticle);
DispatcherServlet.get("/console/articles/status/{status}/{page}/{pageSize}/{windowSize}", articleConsole::getArticles);
DispatcherServlet.delete("/console/article/{id}", articleConsole::removeArticle);
......
......@@ -43,6 +43,8 @@ import org.b3log.solo.event.EventTypes;
import org.b3log.solo.model.*;
import org.b3log.solo.processor.console.ConsoleRenderer;
import org.b3log.solo.service.*;
import org.b3log.solo.util.Emotions;
import org.b3log.solo.util.Markdowns;
import org.b3log.solo.util.Skins;
import org.b3log.solo.util.Solos;
import org.json.JSONObject;
......@@ -135,6 +137,49 @@ public class ArticleProcessor {
@Inject
private EventManager eventManager;
/**
* Markdowns.
* <p>
* Renders the response with a json object, for example,
* <pre>
* {
* "html": ""
* }
* </pre>
* </p>
*
* @param context the specified http request context
*/
@RequestProcessing(value = "/console/markdown/2html", method = HttpMethod.POST)
public void markdown2HTML(final RequestContext context) {
final JSONObject result = Solos.newSucc();
context.renderJSON(result);
final String markdownText = context.requestJSON().optString("markdownText");
if (StringUtils.isBlank(markdownText)) {
result.put(Common.DATA, "");
return;
}
if (!Solos.isLoggedIn(context)) {
result.put(Keys.CODE, -1);
result.put(Keys.MSG, langPropsService.get("getFailLabel"));
return;
}
try {
String html = Emotions.convert(markdownText);
html = Markdowns.toHTML(html);
result.put(Common.DATA, html);
} catch (final Exception e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
result.put(Keys.CODE, -1);
result.put(Keys.MSG, langPropsService.get("getFailLabel"));
}
}
/**
* Shows the article view password form.
*
......
/*
* Solo - A small and beautiful blogging system written in Java.
* Copyright (c) 2010-2019, b3log.org & hacpai.com
*
* 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/>.
*/
package org.b3log.solo.processor;
import org.apache.commons.lang.StringUtils;
import org.b3log.latke.logging.Level;
import org.b3log.latke.logging.Logger;
import org.b3log.latke.servlet.HttpMethod;
import org.b3log.latke.servlet.RequestContext;
import org.b3log.latke.servlet.annotation.RequestProcessing;
import org.b3log.latke.servlet.annotation.RequestProcessor;
import org.b3log.latke.servlet.renderer.PngRenderer;
import org.b3log.latke.util.Strings;
import org.patchca.color.GradientColorFactory;
import org.patchca.color.RandomColorFactory;
import org.patchca.filter.predefined.CurvesRippleFilterFactory;
import org.patchca.font.RandomFontFactory;
import org.patchca.service.Captcha;
import org.patchca.service.ConfigurableCaptchaService;
import org.patchca.word.RandomWordFactory;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Captcha processor.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 2.0.0.5, Sep 21, 2018
* @since 0.3.1
*/
@RequestProcessor
public class CaptchaProcessor {
/**
* Logger.
*/
private static final Logger LOGGER = Logger.getLogger(CaptchaProcessor.class);
/**
* Key of captcha.
*/
public static final String CAPTCHA = "captcha";
/**
* Captchas.
*/
private static final Set<String> CAPTCHAS = new HashSet<>();
/**
* Captcha length.
*/
private static final int CAPTCHA_LENGTH = 4;
/**
* Flag of captcha is enabled.
*/
public static boolean CAPTCHA_ON = true;
/**
* Captcha chars.
*/
private static final String CHARS = "acdefhijklmnprstuvwxy234578";
/**
* Gets captcha.
*
* @param context the specified context
*/
@RequestProcessing(value = "/captcha", method = HttpMethod.GET)
public void get(final RequestContext context) {
final PngRenderer renderer = new PngRenderer();
context.setRenderer(renderer);
try {
final ConfigurableCaptchaService cs = new ConfigurableCaptchaService();
if (0.5 < Math.random()) {
cs.setColorFactory(new GradientColorFactory());
} else {
cs.setColorFactory(new RandomColorFactory());
}
cs.setFilterFactory(new CurvesRippleFilterFactory(cs.getColorFactory()));
final RandomWordFactory randomWordFactory = new RandomWordFactory();
randomWordFactory.setCharacters(CHARS);
randomWordFactory.setMinLength(CAPTCHA_LENGTH);
randomWordFactory.setMaxLength(CAPTCHA_LENGTH);
cs.setWordFactory(randomWordFactory);
cs.setFontFactory(new RandomFontFactory(getAvaialbeFonts()));
final Captcha captcha = cs.getCaptcha();
final String challenge = captcha.getChallenge();
final BufferedImage bufferedImage = captcha.getImage();
if (CAPTCHAS.size() > 64) {
CAPTCHAS.clear();
}
CAPTCHAS.add(challenge);
final HttpServletResponse response = context.getResponse();
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ImageIO.write(bufferedImage, "png", baos);
renderer.setImage(baos.toByteArray());
}
} catch (final Exception e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
}
}
/**
* Checks whether the specified captcha is invalid.
*
* @param captcha the specified captcha
* @return {@code true} if it is invalid, returns {@code false} otherwise
*/
public static boolean invalidCaptcha(final String captcha) {
if (!CAPTCHA_ON) {
return false;
}
if (StringUtils.isBlank(captcha) || captcha.length() != CAPTCHA_LENGTH) {
return true;
}
boolean ret = !CaptchaProcessor.CAPTCHAS.contains(captcha);
if (!ret) {
CaptchaProcessor.CAPTCHAS.remove(captcha);
}
return ret;
}
private static List<String> getAvaialbeFonts() {
final List<String> ret = new ArrayList<>();
final GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
final Font[] fonts = e.getAllFonts();
for (final Font f : fonts) {
if (Strings.contains(f.getFontName(), new String[]{"Verdana", "DejaVu Sans Mono", "Tahoma"})) {
ret.add(f.getFontName());
}
}
final String defaultFontName = new JLabel().getFont().getFontName();
ret.add(defaultFontName);
return ret;
}
}
......@@ -48,7 +48,7 @@ import java.util.Map;
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @author <a href="https://hacpai.com/member/armstrong">ArmstrongCN</a>
* @version 1.3.3.5, Dec 3, 2018
* @version 1.3.3.6, Feb 10, 2019
* @since 0.3.1
*/
@RequestProcessor
......@@ -140,13 +140,10 @@ public class CommentProcessor {
}
if (!Solos.isLoggedIn(context)) {
final String captcha = requestJSONObject.optString(CaptchaProcessor.CAPTCHA);
if (CaptchaProcessor.invalidCaptcha(captcha)) {
jsonObject.put(Keys.STATUS_CODE, false);
jsonObject.put(Keys.MSG, langPropsService.get("captchaErrorLabel"));
jsonObject.put(Keys.STATUS_CODE, false);
jsonObject.put(Keys.MSG, "Need login");
return;
}
return;
}
try {
......@@ -241,13 +238,10 @@ public class CommentProcessor {
}
if (!Solos.isLoggedIn(context)) {
final String captcha = requestJSONObject.optString(CaptchaProcessor.CAPTCHA);
if (CaptchaProcessor.invalidCaptcha(captcha)) {
jsonObject.put(Keys.STATUS_CODE, false);
jsonObject.put(Keys.MSG, langPropsService.get("captchaErrorLabel"));
jsonObject.put(Keys.STATUS_CODE, false);
jsonObject.put(Keys.MSG, "Need login");
return;
}
return;
}
try {
......
......@@ -36,9 +36,7 @@ import org.b3log.solo.model.Common;
import org.b3log.solo.service.ArticleMgmtService;
import org.b3log.solo.service.ArticleQueryService;
import org.b3log.solo.service.UserQueryService;
import org.b3log.solo.util.Emotions;
import org.b3log.solo.util.Images;
import org.b3log.solo.util.Markdowns;
import org.b3log.solo.util.Solos;
import org.json.JSONArray;
import org.json.JSONObject;
......@@ -151,41 +149,6 @@ public class ArticleConsole {
result.put("data", urls.stream().map(url -> Images.imageSize(url, width, height)).collect(Collectors.toList()));
}
/**
* Markdowns.
* <p>
* Renders the response with a json object, for example,
* <pre>
* {
* "html": ""
* }
* </pre>
* </p>
*
* @param context the specified http request context
*/
public void markdown2HTML(final RequestContext context) {
final JSONObject result = Solos.newSucc();
context.renderJSON(result);
final String markdownText = context.requestJSON().optString("markdownText");
if (StringUtils.isBlank(markdownText)) {
result.put(Common.DATA, "");
return;
}
try {
String html = Emotions.convert(markdownText);
html = Markdowns.toHTML(html);
result.put(Common.DATA, html);
} catch (final Exception e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
context.renderJSONValue(Keys.CODE, -1);
context.renderJSONValue(Keys.MSG, langPropsService.get("getFailLabel"));
}
}
/**
* Gets an article by the specified request json object.
* <p>
......
......@@ -41,6 +41,32 @@ public final class Images {
*/
private static final Logger LOGGER = Logger.getLogger(Images.class);
/**
* Qiniu image processing.
*
* @param html the specified content HTML
* @return processed content
*/
public static String qiniuImgProcessing(final String html) {
String ret = html;
final String qiniuDomain = "https://img.hacpai.com";
final String[] imgSrcs = StringUtils.substringsBetween(html, "<img src=\"", "\"");
if (null == imgSrcs) {
return ret;
}
for (final String imgSrc : imgSrcs) {
if (!StringUtils.startsWith(imgSrc, qiniuDomain) || StringUtils.contains(imgSrc, ".gif")
|| StringUtils.containsIgnoreCase(imgSrc, "?imageView2")) {
continue;
}
ret = StringUtils.replace(ret, imgSrc, imgSrc + "?imageView2/2/w/768/format/webp/interlace/1");
}
return ret;
}
/**
* Returns image URL of Qiniu image processing style with the specified width and height.
*
......
......@@ -197,6 +197,7 @@ public final class Markdowns {
String ret = doc.select("body").html();
ret = StringUtils.trim(ret);
ret = Images.qiniuImgProcessing(ret);
// cache it
putHTML(markdownText, ret);
......
/*
* Solo - A small and beautiful blogging system written in Java.
* Copyright (c) 2010-2019, b3log.org & hacpai.com
*
* 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/>.
*/
package org.b3log.solo.processor;
import org.b3log.solo.AbstractTestCase;
import org.b3log.solo.MockHttpServletRequest;
import org.b3log.solo.MockHttpServletResponse;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* {@link CaptchaProcessor} test case.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.0, Nov 2, 2016
* @since 1.7.0
*/
@Test(suiteName = "processor")
public class CaptchaProcessorTestCase extends AbstractTestCase {
/**
* Init.
*
* @throws Exception exception
*/
@Test
public void init() throws Exception {
super.init();
}
/**
* get.
*
* @throws Exception exception
*/
@Test(dependsOnMethods = "init")
public void get() throws Exception {
final MockHttpServletRequest request = mockRequest();
request.setRequestURI("/captcha");
final MockHttpServletResponse response = mockResponse();
mockDispatcherServletService(request, response);
final String pragma = response.getHeader("Pragma");
Assert.assertEquals(pragma, "no-cache");
}
}
......@@ -40,7 +40,7 @@ import java.io.StringReader;
* {@link CommentProcessorTestCase} test case.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.4, Sep 20, 2018
* @version 1.0.0.5, Feb 10, 2019
* @since 1.7.0
*/
@Test(suiteName = "processor")
......@@ -68,10 +68,7 @@ public class CommentProcessorTestCase extends AbstractTestCase {
request.setMethod("POST");
request.setAttribute(Keys.TEMAPLTE_DIR_NAME, Option.DefaultPreference.DEFAULT_SKIN_DIR_NAME);
CaptchaProcessor.CAPTCHA_ON = false;
final JSONObject requestJSON = new JSONObject();
requestJSON.put("captcha", "captcha123456");
requestJSON.put("oId", addPage());
requestJSON.put("commentName", "88250");
requestJSON.put("commentEmail", "d@hacpai.com");
......@@ -81,6 +78,8 @@ public class CommentProcessorTestCase extends AbstractTestCase {
final BufferedReader reader = new BufferedReader(new StringReader(requestJSON.toString()));
request.setReader(reader);
mockAdminLogin(request);
final MockHttpServletResponse response = mockResponse();
mockDispatcherServletService(request, response);
......@@ -100,10 +99,7 @@ public class CommentProcessorTestCase extends AbstractTestCase {
request.setMethod("POST");
request.setAttribute(Keys.TEMAPLTE_DIR_NAME, Option.DefaultPreference.DEFAULT_SKIN_DIR_NAME);
CaptchaProcessor.CAPTCHA_ON = false;
final JSONObject requestJSON = new JSONObject();
requestJSON.put("captcha", "captcha123456");
requestJSON.put("oId", addArticle());
requestJSON.put("commentName", "88250");
requestJSON.put("commentEmail", "d@hacpai.com");
......@@ -113,6 +109,8 @@ public class CommentProcessorTestCase extends AbstractTestCase {
final BufferedReader reader = new BufferedReader(new StringReader(requestJSON.toString()));
request.setReader(reader);
mockAdminLogin(request);
final MockHttpServletResponse response = mockResponse();
mockDispatcherServletService(request, response);
......
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