• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

88250 / solo / #728

29 Aug 2024 02:37AM UTC coverage: 54.856% (-0.04%) from 54.892%
#728

push

88250
:see_no_evil: 皮肤不计入项目语言统计

4530 of 8258 relevant lines covered (54.86%)

0.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.9
/src/main/java/org/b3log/solo/processor/console/ArticleConsole.java
1
/*
2
 * Solo - A small and beautiful blogging system written in Java.
3
 * Copyright (c) 2010-present, b3log.org
4
 *
5
 * Solo is licensed under Mulan PSL v2.
6
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
7
 * You may obtain a copy of Mulan PSL v2 at:
8
 *         http://license.coscl.org.cn/MulanPSL2
9
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
10
 * See the Mulan PSL v2 for more details.
11
 */
12
package org.b3log.solo.processor.console;
13

14
import org.apache.commons.lang3.StringEscapeUtils;
15
import org.apache.commons.lang3.StringUtils;
16
import org.apache.logging.log4j.Level;
17
import org.apache.logging.log4j.LogManager;
18
import org.apache.logging.log4j.Logger;
19
import org.b3log.latke.Keys;
20
import org.b3log.latke.Latkes;
21
import org.b3log.latke.http.Request;
22
import org.b3log.latke.http.RequestContext;
23
import org.b3log.latke.http.renderer.JsonRenderer;
24
import org.b3log.latke.ioc.Inject;
25
import org.b3log.latke.ioc.Singleton;
26
import org.b3log.latke.service.LangPropsService;
27
import org.b3log.latke.service.ServiceException;
28
import org.b3log.latke.util.Strings;
29
import org.b3log.solo.model.Article;
30
import org.b3log.solo.model.Common;
31
import org.b3log.solo.service.ArticleMgmtService;
32
import org.b3log.solo.service.ArticleQueryService;
33
import org.b3log.solo.service.UserQueryService;
34
import org.b3log.solo.util.Images;
35
import org.b3log.solo.util.Solos;
36
import org.b3log.solo.util.StatusCodes;
37
import org.json.JSONArray;
38
import org.json.JSONObject;
39

40
import java.util.List;
41
import java.util.stream.Collectors;
42

43
/**
44
 * Article console request processing.
45
 *
46
 * @author <a href="http://88250.b3log.org">Liang Ding</a>
47
 * @version 2.0.0.0, Feb 9, 2020
48
 * @since 0.4.0
49
 */
50
@Singleton
51
public class ArticleConsole {
1✔
52

53
    /**
54
     * Logger.
55
     */
56
    private static final Logger LOGGER = LogManager.getLogger(ArticleConsole.class);
1✔
57

58
    /**
59
     * Article management service.
60
     */
61
    @Inject
62
    private ArticleMgmtService articleMgmtService;
63

64
    /**
65
     * Article query service.
66
     */
67
    @Inject
68
    private ArticleQueryService articleQueryService;
69

70
    /**
71
     * User query service.
72
     */
73
    @Inject
74
    private UserQueryService userQueryService;
75

76
    /**
77
     * Language service.
78
     */
79
    @Inject
80
    private LangPropsService langPropsService;
81

82
    /**
83
     * Pushes an article to community.
84
     *
85
     * @param context the specified request context
86
     */
87
    public void pushArticleToCommunity(final RequestContext context) {
88
        final JSONObject result = new JSONObject().put(Keys.CODE, 0);
×
89
        context.renderJSON(result);
×
90
        final String articleId = context.param("id");
×
91
        articleMgmtService.pushArticleToCommunity(articleId);
×
92
    }
×
93

94
    /**
95
     * Gets article thumbs.
96
     * <p>
97
     * Renders the response with a json object, for example,
98
     * <pre>
99
     * {
100
     *     "code": 0,
101
     *     "data": [
102
     *         "https://b3logfile.com/bing/20171226.jpg?imageView2/1/w/960/h/540/interlace/1/q/100",
103
     *         "https://b3logfile.com/bing/20171105.jpg?imageView2/1/w/960/h/540/interlace/1/q/100",
104
     *         "https://b3logfile.com/bing/20180105.jpg?imageView2/1/w/960/h/540/interlace/1/q/100",
105
     *         "https://b3logfile.com/bing/20171114.jpg?imageView2/1/w/960/h/540/interlace/1/q/100"
106
     *     ]
107
     * }
108
     * </pre>
109
     * </p>
110
     *
111
     * @param context the specified request context
112
     */
113
    public void getArticleThumbs(final RequestContext context) {
114
        final JsonRenderer renderer = new JsonRenderer();
1✔
115
        context.setRenderer(renderer);
1✔
116
        final JSONObject result = new JSONObject();
1✔
117
        renderer.setJSONObject(result);
1✔
118
        result.put(Keys.CODE, StatusCodes.SUCC);
1✔
119
        final Request request = context.getRequest();
1✔
120
        String strN = context.param("n");
1✔
121
        if (!Strings.isNumeric(strN)) {
1✔
122
            strN = "6";
1✔
123
        }
124

125
        final int n = Integer.valueOf(strN);
1✔
126
        final List<String> urls = Images.randomImages(n);
1✔
127

128
        // original: 1920*1080
129

130
        final String wStr = context.param("w");
1✔
131
        int w = Article.ARTICLE_THUMB_IMG_WIDTH;
1✔
132
        if (Strings.isNumeric(wStr)) {
1✔
133
            w = Integer.valueOf(wStr);
×
134
        }
135
        final int width = w;
1✔
136
        final String hStr = context.param("h");
1✔
137
        int h = Article.ARTICLE_THUMB_IMG_HEIGHT;
1✔
138
        if (Strings.isNumeric(hStr)) {
1✔
139
            h = Integer.valueOf(hStr);
×
140
        }
141
        final int height = h;
1✔
142

143
        result.put("data", urls.stream().map(url -> Images.imageSize(url, width, height)).collect(Collectors.toList()));
1✔
144
    }
1✔
145

146
    /**
147
     * Gets an article by the specified request json object.
148
     * <p>
149
     * Renders the response with a json object, for example,
150
     * <pre>
151
     * {
152
     *     "oId": "",
153
     *     "articleTitle": "",
154
     *     "articleAbstract": "",
155
     *     "articleContent": "",
156
     *     "articlePermalink": "",
157
     *     "articleTags": [{
158
     *         "oId": "",
159
     *         "tagTitle": ""
160
     *     }, ....],
161
     *     "articleSignId": "",
162
     *     "signs": [{
163
     *         "oId": "",
164
     *         "signHTML": ""
165
     *     }, ....]
166
     *     "code": 0
167
     * }
168
     * </pre>
169
     * </p>
170
     *
171
     * @param context the specified request context
172
     */
173
    public void getArticle(final RequestContext context) {
174
        final JsonRenderer renderer = new JsonRenderer();
1✔
175
        context.setRenderer(renderer);
1✔
176
        try {
177
            final String articleId = context.pathVar("id");
1✔
178
            final JSONObject currentUser = Solos.getCurrentUser(context);
1✔
179
            if (!articleQueryService.canAccessArticle(articleId, currentUser)) {
1✔
180
                final JSONObject ret = new JSONObject();
×
181
                renderer.setJSONObject(ret);
×
182
                ret.put(Keys.CODE, StatusCodes.ERR);
×
183
                ret.put(Keys.MSG, langPropsService.get("forbiddenLabel"));
×
184
                return;
×
185
            }
186

187
            final JSONObject result = articleQueryService.getArticle(articleId);
1✔
188
            result.put(Keys.CODE, StatusCodes.SUCC);
1✔
189
            renderer.setJSONObject(result);
1✔
190
        } catch (final ServiceException e) {
×
191
            LOGGER.log(Level.ERROR, e.getMessage(), e);
×
192

193
            final JSONObject jsonObject = new JSONObject().put(Keys.CODE, StatusCodes.ERR);
×
194
            renderer.setJSONObject(jsonObject);
×
195
            jsonObject.put(Keys.MSG, langPropsService.get("getFailLabel"));
×
196
        }
1✔
197
    }
1✔
198

199
    /**
200
     * Gets articles(by crate date descending) by the specified request json object.
201
     * <p>
202
     * The request URI contains the pagination arguments. For example, the request URI is
203
     * /console/articles/status/published/1/10/20, means the current page is 1, the page size is 10, and the window size
204
     * is 20.
205
     * </p>
206
     * <p>
207
     * Renders the response with a json object, for example,
208
     * <pre>
209
     * {
210
     *     "code": int,
211
     *     "pagination": {
212
     *         "paginationPageCount": 100,
213
     *         "paginationPageNums": [1, 2, 3, 4, 5]
214
     *     },
215
     *     "articles": [{
216
     *         "oId": "",
217
     *         "articleTitle": "",
218
     *         "articleCreateTime"; long,
219
     *         "articleViewCount": int,
220
     *         "articleTags": "tag1, tag2, ....",
221
     *         "articlePutTop": boolean,
222
     *         "articleStatus": int
223
     *      }, ....]
224
     * }
225
     * </pre>, order by article update date and sticky(put top).
226
     * </p>
227
     *
228
     * @param context the specified request context
229
     */
230
    public void getArticles(final RequestContext context) {
231
        final JsonRenderer renderer = new JsonRenderer();
1✔
232
        context.setRenderer(renderer);
1✔
233
        try {
234
            String path = context.requestURI().substring((Latkes.getContextPath() + "/console/articles/status/").length());
1✔
235
            final String status = StringUtils.substringBefore(path, "/");
1✔
236

237
            path = path.substring((status + "/").length());
1✔
238
            final JSONObject requestJSONObject = Solos.buildPaginationRequest(path);
1✔
239
            requestJSONObject.put(Article.ARTICLE_STATUS, "published".equals(status) ? Article.ARTICLE_STATUS_C_PUBLISHED : Article.ARTICLE_STATUS_C_DRAFT);
1✔
240

241
            final JSONArray excludes = new JSONArray();
1✔
242
            excludes.put(Article.ARTICLE_CONTENT);
1✔
243
            excludes.put(Article.ARTICLE_UPDATED);
1✔
244
            excludes.put(Article.ARTICLE_CREATED);
1✔
245
            excludes.put(Article.ARTICLE_AUTHOR_ID);
1✔
246
            excludes.put(Article.ARTICLE_RANDOM_DOUBLE);
1✔
247
            requestJSONObject.put(Keys.EXCLUDES, excludes);
1✔
248

249
            final String keyword = StringUtils.trim(context.param("k"));
1✔
250
            if (StringUtils.isNotBlank(keyword)) {
1✔
251
                requestJSONObject.put(Common.KEYWORD, keyword);
×
252
            }
253

254
            final JSONObject result = articleQueryService.getArticles(requestJSONObject);
1✔
255
            result.put(Keys.CODE, StatusCodes.SUCC);
1✔
256
            renderer.setJSONObject(result);
1✔
257

258
            final List<JSONObject> articles = (List<JSONObject>) result.opt(Article.ARTICLES);
1✔
259
            for (final JSONObject article : articles) {
1✔
260
                String title = article.optString(Article.ARTICLE_TITLE);
×
261
                title = StringEscapeUtils.escapeXml(title);
×
262
                article.put(Article.ARTICLE_TITLE, title);
×
263
            }
×
264
        } catch (final Exception e) {
×
265
            LOGGER.log(Level.ERROR, e.getMessage(), e);
×
266

267
            final JSONObject jsonObject = new JSONObject().put(Keys.CODE, StatusCodes.ERR);
×
268
            renderer.setJSONObject(jsonObject);
×
269
            jsonObject.put(Keys.MSG, langPropsService.get("getFailLabel"));
×
270
        }
1✔
271
    }
1✔
272

273
    /**
274
     * Removes an article by the specified request.
275
     * <p>
276
     * Renders the response with a json object, for example,
277
     * <pre>
278
     * {
279
     *     "code": int,
280
     *     "msg": ""
281
     * }
282
     * </pre>
283
     * </p>
284
     *
285
     * @param context the specified request context
286
     */
287
    public void removeArticle(final RequestContext context) {
288
        final JsonRenderer renderer = new JsonRenderer();
1✔
289
        context.setRenderer(renderer);
1✔
290
        final JSONObject ret = new JSONObject();
1✔
291
        renderer.setJSONObject(ret);
1✔
292
        final String articleId = context.pathVar("id");
1✔
293
        final JSONObject currentUser = Solos.getCurrentUser(context);
1✔
294

295
        try {
296
            if (!articleQueryService.canAccessArticle(articleId, currentUser)) {
1✔
297
                ret.put(Keys.CODE, StatusCodes.ERR);
×
298
                ret.put(Keys.MSG, langPropsService.get("forbiddenLabel"));
×
299
                return;
×
300
            }
301

302
            articleMgmtService.removeArticle(articleId);
1✔
303

304
            ret.put(Keys.CODE, StatusCodes.SUCC);
1✔
305
            ret.put(Keys.MSG, langPropsService.get("removeSuccLabel"));
1✔
306
        } catch (final Exception e) {
×
307
            LOGGER.log(Level.ERROR, e.getMessage(), e);
×
308

309
            final JSONObject jsonObject = new JSONObject();
×
310
            renderer.setJSONObject(jsonObject);
×
311
            jsonObject.put(Keys.CODE, StatusCodes.ERR);
×
312
            jsonObject.put(Keys.MSG, langPropsService.get("removeFailLabel"));
×
313
        }
1✔
314
    }
1✔
315

316
    /**
317
     * Cancels publish an article by the specified request.
318
     * <p>
319
     * Renders the response with a json object, for example,
320
     * <pre>
321
     * {
322
     *     "code": int,
323
     *     "msg": ""
324
     * }
325
     * </pre>
326
     * </p>
327
     *
328
     * @param context the specified request context
329
     */
330
    public void cancelPublishArticle(final RequestContext context) {
331
        final JsonRenderer renderer = new JsonRenderer();
1✔
332
        context.setRenderer(renderer);
1✔
333
        final JSONObject ret = new JSONObject();
1✔
334
        renderer.setJSONObject(ret);
1✔
335

336
        try {
337
            final String articleId = context.pathVar("id");
1✔
338
            final JSONObject currentUser = Solos.getCurrentUser(context);
1✔
339
            if (!articleQueryService.canAccessArticle(articleId, currentUser)) {
1✔
340
                ret.put(Keys.CODE, StatusCodes.ERR);
×
341
                ret.put(Keys.MSG, langPropsService.get("forbiddenLabel"));
×
342
                return;
×
343
            }
344

345
            articleMgmtService.cancelPublishArticle(articleId);
1✔
346

347
            ret.put(Keys.CODE, StatusCodes.SUCC);
1✔
348
            ret.put(Keys.MSG, langPropsService.get("unPulbishSuccLabel"));
1✔
349
        } catch (final Exception e) {
×
350
            LOGGER.log(Level.ERROR, e.getMessage(), e);
×
351

352
            final JSONObject jsonObject = new JSONObject();
×
353
            renderer.setJSONObject(jsonObject);
×
354
            jsonObject.put(Keys.CODE, StatusCodes.ERR);
×
355
            jsonObject.put(Keys.MSG, langPropsService.get("unPulbishFailLabel"));
×
356
        }
1✔
357
    }
1✔
358

359
    /**
360
     * Cancels an top article by the specified request.
361
     * <p>
362
     * Renders the response with a json object, for example,
363
     * <pre>
364
     * {
365
     *     "code": int,
366
     *     "msg": ""
367
     * }
368
     * </pre>
369
     * </p>
370
     *
371
     * @param context the specified request context
372
     */
373
    public void cancelTopArticle(final RequestContext context) {
374
        final JsonRenderer renderer = new JsonRenderer();
1✔
375
        context.setRenderer(renderer);
1✔
376
        final JSONObject ret = new JSONObject();
1✔
377
        renderer.setJSONObject(ret);
1✔
378
        if (!Solos.isAdminLoggedIn(context)) {
1✔
379
            ret.put(Keys.MSG, langPropsService.get("forbiddenLabel"));
×
380
            ret.put(Keys.CODE, StatusCodes.ERR);
×
381
            return;
×
382
        }
383

384
        try {
385
            final String articleId = context.pathVar("id");
1✔
386
            articleMgmtService.topArticle(articleId, false);
1✔
387
            ret.put(Keys.CODE, StatusCodes.SUCC);
1✔
388
            ret.put(Keys.MSG, langPropsService.get("cancelTopSuccLabel"));
1✔
389
        } catch (final Exception e) {
×
390
            LOGGER.log(Level.ERROR, e.getMessage(), e);
×
391

392
            final JSONObject jsonObject = new JSONObject();
×
393
            renderer.setJSONObject(jsonObject);
×
394
            jsonObject.put(Keys.CODE, StatusCodes.ERR);
×
395
            jsonObject.put(Keys.MSG, langPropsService.get("cancelTopFailLabel"));
×
396
        }
1✔
397
    }
1✔
398

399
    /**
400
     * Puts an article to top by the specified request.
401
     * <p>
402
     * Renders the response with a json object, for example,
403
     * <pre>
404
     * {
405
     *     "code": int,
406
     *     "msg": ""
407
     * }
408
     * </pre>
409
     * </p>
410
     *
411
     * @param context the specified request context
412
     */
413
    public void putTopArticle(final RequestContext context) {
414
        final JsonRenderer renderer = new JsonRenderer();
1✔
415
        context.setRenderer(renderer);
1✔
416
        final JSONObject ret = new JSONObject();
1✔
417
        renderer.setJSONObject(ret);
1✔
418
        if (!Solos.isAdminLoggedIn(context)) {
1✔
419
            ret.put(Keys.MSG, langPropsService.get("forbiddenLabel"));
×
420
            ret.put(Keys.CODE, StatusCodes.ERR);
×
421
            return;
×
422
        }
423

424
        try {
425
            final String articleId = context.pathVar("id");
1✔
426
            articleMgmtService.topArticle(articleId, true);
1✔
427
            ret.put(Keys.CODE, StatusCodes.SUCC);
1✔
428
            ret.put(Keys.MSG, langPropsService.get("putTopSuccLabel"));
1✔
429
        } catch (final Exception e) {
×
430
            LOGGER.log(Level.ERROR, e.getMessage(), e);
×
431

432
            final JSONObject jsonObject = new JSONObject();
×
433
            renderer.setJSONObject(jsonObject);
×
434
            jsonObject.put(Keys.CODE, StatusCodes.ERR);
×
435
            jsonObject.put(Keys.MSG, langPropsService.get("putTopFailLabel"));
×
436
        }
1✔
437
    }
1✔
438

439
    /**
440
     * Updates an article by the specified request json object.
441
     * <p>
442
     * The specified request json object, for example,
443
     * <pre>
444
     * {
445
     *     "article": {
446
     *         "oId": "",
447
     *         "articleTitle": "",
448
     *         "articleAbstract": "",
449
     *         "articleContent": "",
450
     *         "articleTags": "tag1,tag2,tag3", // optional, default set "待分类"
451
     *         "articlePermalink": "", // optional
452
     *         "articleStatus": int, // 0: published, 1: draft
453
     *         "articleSignId": "" // optional
454
     *         "articleViewPwd": "",
455
     *         "postToCommunity": boolean
456
     *     }
457
     *  }
458
     * </pre>
459
     * </p>
460
     * <p>
461
     * Renders the response with a json object, for example,
462
     * <pre>
463
     * {
464
     *     "code": int,
465
     *     "msg": ""
466
     * }
467
     * </pre>
468
     * </p>
469
     *
470
     * @param context the specified request context
471
     */
472
    public void updateArticle(final RequestContext context) {
473
        final JsonRenderer renderer = new JsonRenderer();
1✔
474
        context.setRenderer(renderer);
1✔
475
        final JSONObject ret = new JSONObject();
1✔
476
        try {
477
            final JSONObject requestJSONObject = context.requestJSON();
1✔
478
            final JSONObject article = requestJSONObject.getJSONObject(Article.ARTICLE);
1✔
479
            final String articleId = article.getString(Keys.OBJECT_ID);
1✔
480
            renderer.setJSONObject(ret);
1✔
481

482
            final JSONObject currentUser = Solos.getCurrentUser(context);
1✔
483
            if (!articleQueryService.canAccessArticle(articleId, currentUser)) {
1✔
484
                ret.put(Keys.MSG, langPropsService.get("forbiddenLabel"));
×
485
                ret.put(Keys.CODE, StatusCodes.ERR);
×
486
                return;
×
487
            }
488

489
            // 打印请求日志,如果发生特殊情况丢失数据,至少还可以根据日志寻回内容
490
            LOGGER.log(Level.INFO, "Updates an article request [" + requestJSONObject.toString() + "]");
1✔
491

492
            articleMgmtService.updateArticle(requestJSONObject);
1✔
493

494
            ret.put(Keys.MSG, langPropsService.get("updateSuccLabel"));
1✔
495
            ret.put(Keys.CODE, StatusCodes.SUCC);
1✔
496
        } catch (final ServiceException e) {
×
497
            final JSONObject jsonObject = new JSONObject().put(Keys.CODE, StatusCodes.ERR);
×
498
            renderer.setJSONObject(jsonObject);
×
499
            jsonObject.put(Keys.MSG, e.getMessage());
×
500
        }
1✔
501
    }
1✔
502

503
    /**
504
     * Adds an article with the specified request.
505
     * <p>
506
     * The specified request json object, for example,
507
     * <pre>
508
     * {
509
     *     "article": {
510
     *         "articleTitle": "",
511
     *         "articleAbstract": "",
512
     *         "articleContent": "",
513
     *         "articleTags": "tag1,tag2,tag3", // optional, default set "待分类"
514
     *         "articlePermalink": "", // optional
515
     *         "articleStatus": int, // 0: published, 1: draft
516
     *         "postToCommunity": boolean,
517
     *         "articleSignId": "" // optional
518
     *         "articleViewPwd": ""
519
     *     }
520
     * }
521
     * </pre>
522
     * </p>
523
     * <p>
524
     * Renders the response with a json object, for example,
525
     * <pre>
526
     * {
527
     *     "code": int,
528
     *     "oId": "", // Generated article id
529
     *     "msg": ""
530
     * }
531
     * </pre>
532
     * </p>
533
     *
534
     * @param context the specified request context
535
     */
536
    public void addArticle(final RequestContext context) {
537
        final JsonRenderer renderer = new JsonRenderer();
1✔
538
        context.setRenderer(renderer);
1✔
539
        final JSONObject ret = new JSONObject();
1✔
540
        try {
541
            final JSONObject requestJSONObject = context.requestJSON();
1✔
542
            final JSONObject currentUser = Solos.getCurrentUser(context);
1✔
543
            requestJSONObject.getJSONObject(Article.ARTICLE).put(Article.ARTICLE_AUTHOR_ID, currentUser.getString(Keys.OBJECT_ID));
1✔
544

545
            // 打印请求日志,如果发生特殊情况丢失数据,至少还可以根据日志寻回内容
546
            LOGGER.log(Level.INFO, "Adds an article request [" + requestJSONObject.toString() + "]");
1✔
547

548
            final String articleId = articleMgmtService.addArticle(requestJSONObject);
1✔
549
            ret.put(Keys.OBJECT_ID, articleId);
1✔
550
            ret.put(Keys.MSG, langPropsService.get("addSuccLabel"));
1✔
551
            ret.put(Keys.CODE, StatusCodes.SUCC);
1✔
552

553
            renderer.setJSONObject(ret);
1✔
554
        } catch (final ServiceException e) {
×
555
            final JSONObject jsonObject = new JSONObject().put(Keys.CODE, StatusCodes.ERR);
×
556
            renderer.setJSONObject(jsonObject);
×
557
            jsonObject.put(Keys.MSG, e.getMessage());
×
558
        }
1✔
559
    }
1✔
560
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc