Commit 65f8a5e9 authored by Liang Ding's avatar Liang Ding

Fixed #34

升级 GAE SDK 到 1.7.0,Latke 中加入了对 OR 查询的支持。
parent bd44f5d3
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.processor; package org.b3log.solo.processor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringEscapeUtils;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.annotation.RequestProcessing; import org.b3log.latke.annotation.RequestProcessing;
import org.b3log.latke.annotation.RequestProcessor; import org.b3log.latke.annotation.RequestProcessor;
import org.b3log.latke.model.User; import org.b3log.latke.model.User;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.servlet.HTTPRequestContext; import org.b3log.latke.servlet.HTTPRequestContext;
import org.b3log.latke.servlet.HTTPRequestMethod; import org.b3log.latke.servlet.HTTPRequestMethod;
import org.b3log.latke.servlet.renderer.AtomRenderer; import org.b3log.latke.servlet.renderer.AtomRenderer;
import org.b3log.latke.servlet.renderer.RssRenderer; import org.b3log.latke.servlet.renderer.RssRenderer;
import org.b3log.latke.util.Locales; import org.b3log.latke.util.Locales;
import org.b3log.latke.util.Strings; import org.b3log.latke.util.Strings;
import org.b3log.solo.SoloServletListener; import org.b3log.solo.SoloServletListener;
import org.b3log.solo.model.Article; import org.b3log.solo.model.Article;
import org.b3log.solo.model.Preference; import org.b3log.solo.model.Preference;
import org.b3log.solo.model.Tag; import org.b3log.solo.model.Tag;
import org.b3log.solo.model.feed.atom.Category; import org.b3log.solo.model.feed.atom.Category;
import org.b3log.solo.model.feed.atom.Entry; import org.b3log.solo.model.feed.atom.Entry;
import org.b3log.solo.model.feed.atom.Feed; import org.b3log.solo.model.feed.atom.Feed;
import org.b3log.solo.model.feed.rss.Channel; import org.b3log.solo.model.feed.rss.Channel;
import org.b3log.solo.model.feed.rss.Item; import org.b3log.solo.model.feed.rss.Item;
import org.b3log.solo.repository.ArticleRepository; import org.b3log.solo.repository.ArticleRepository;
import org.b3log.solo.repository.TagArticleRepository; import org.b3log.solo.repository.TagArticleRepository;
import org.b3log.solo.repository.TagRepository; import org.b3log.solo.repository.TagRepository;
import org.b3log.solo.repository.impl.ArticleRepositoryImpl; import org.b3log.solo.repository.impl.ArticleRepositoryImpl;
import org.b3log.solo.repository.impl.TagArticleRepositoryImpl; import org.b3log.solo.repository.impl.TagArticleRepositoryImpl;
import org.b3log.solo.repository.impl.TagRepositoryImpl; import org.b3log.solo.repository.impl.TagRepositoryImpl;
import org.b3log.solo.service.PreferenceQueryService; import org.b3log.solo.service.PreferenceQueryService;
import org.b3log.solo.util.Articles; import org.b3log.solo.util.Articles;
import org.b3log.solo.util.TimeZones; import org.b3log.solo.util.TimeZones;
import org.b3log.solo.util.Users; import org.b3log.solo.util.Users;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Feed (Atom/RSS) processor. * Feed (Atom/RSS) processor.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.1.0.0, May 10, 2012 * @version 1.1.0.0, May 10, 2012
* @since 0.3.1 * @since 0.3.1
*/ */
@RequestProcessor @RequestProcessor
public final class FeedProcessor { public final class FeedProcessor {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(FeedProcessor.class.getName()); private static final Logger LOGGER = Logger.getLogger(FeedProcessor.class.getName());
/** /**
* Article repository. * Article repository.
*/ */
private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance(); private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance();
/** /**
* Preference query service. * Preference query service.
*/ */
private PreferenceQueryService preferenceQueryService = PreferenceQueryService.getInstance(); private PreferenceQueryService preferenceQueryService = PreferenceQueryService.getInstance();
/** /**
* Count of output entry. * Count of output entry.
*/ */
public static final int ENTRY_OUTPUT_CNT = 10; public static final int ENTRY_OUTPUT_CNT = 10;
/** /**
* Article utilities. * Article utilities.
*/ */
private Articles articleUtils = Articles.getInstance(); private Articles articleUtils = Articles.getInstance();
/** /**
* Tag repository. * Tag repository.
*/ */
private TagRepository tagRepository = TagRepositoryImpl.getInstance(); private TagRepository tagRepository = TagRepositoryImpl.getInstance();
/** /**
* Tag-Article repository. * Tag-Article repository.
*/ */
private TagArticleRepository tagArticleRepository = TagArticleRepositoryImpl.getInstance(); private TagArticleRepository tagArticleRepository = TagArticleRepositoryImpl.getInstance();
/** /**
* Blog articles Atom output. * Blog articles Atom output.
* *
* @param context the specified context * @param context the specified context
*/ */
@RequestProcessing(value = {"/blog-articles-feed.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD}) @RequestProcessing(value = {"/blog-articles-feed.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
public void blogArticlesAtom(final HTTPRequestContext context) { public void blogArticlesAtom(final HTTPRequestContext context) {
final AtomRenderer renderer = new AtomRenderer(); final AtomRenderer renderer = new AtomRenderer();
context.setRenderer(renderer); context.setRenderer(renderer);
final Feed feed = new Feed(); final Feed feed = new Feed();
try { try {
final JSONObject preference = preferenceQueryService.getPreference(); final JSONObject preference = preferenceQueryService.getPreference();
final String blogTitle = preference.getString(Preference.BLOG_TITLE); final String blogTitle = preference.getString(Preference.BLOG_TITLE);
final String blogSubtitle = preference.getString(Preference.BLOG_SUBTITLE); final String blogSubtitle = preference.getString(Preference.BLOG_SUBTITLE);
final String blogHost = preference.getString(Preference.BLOG_HOST); final String blogHost = preference.getString(Preference.BLOG_HOST);
feed.setTitle(StringEscapeUtils.escapeXml(blogTitle)); feed.setTitle(StringEscapeUtils.escapeXml(blogTitle));
feed.setSubtitle(StringEscapeUtils.escapeXml(blogSubtitle)); feed.setSubtitle(StringEscapeUtils.escapeXml(blogSubtitle));
feed.setUpdated(TimeZones.getTime(preference.getString(Preference.TIME_ZONE_ID))); feed.setUpdated(TimeZones.getTime(preference.getString(Preference.TIME_ZONE_ID)));
feed.setAuthor(StringEscapeUtils.escapeXml(blogTitle)); feed.setAuthor(StringEscapeUtils.escapeXml(blogTitle));
feed.setLink("http://" + blogHost + "/blog-articles-feed.do"); feed.setLink("http://" + blogHost + "/blog-articles-feed.do");
feed.setId("http://" + blogHost + "/"); feed.setId("http://" + blogHost + "/");
final Query query = new Query().setCurrentPageNum(1). final Query query = new Query().setCurrentPageNum(1).
setPageSize(ENTRY_OUTPUT_CNT). setPageSize(ENTRY_OUTPUT_CNT).
addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true). addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true).
addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING). addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING).
setPageCount(1); setPageCount(1);
final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers(); final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers();
String authorName = ""; String authorName = "";
final JSONObject articleResult = articleRepository.get(query); final JSONObject articleResult = articleRepository.get(query);
final JSONArray articles = articleResult.getJSONArray(Keys.RESULTS); final JSONArray articles = articleResult.getJSONArray(Keys.RESULTS);
if (!hasMultipleUsers && 0 != articles.length()) { if (!hasMultipleUsers && 0 != articles.length()) {
authorName = articleUtils.getAuthor(articles.getJSONObject(0)).getString(User.USER_NAME); authorName = articleUtils.getAuthor(articles.getJSONObject(0)).getString(User.USER_NAME);
} }
final boolean isFullContent = "fullContent".equals(preference.getString(Preference.FEED_OUTPUT_MODE)); final boolean isFullContent = "fullContent".equals(preference.getString(Preference.FEED_OUTPUT_MODE));
for (int i = 0; i < articles.length(); i++) { for (int i = 0; i < articles.length(); i++) {
final JSONObject article = articles.getJSONObject(i); final JSONObject article = articles.getJSONObject(i);
final Entry entry = new Entry(); final Entry entry = new Entry();
feed.addEntry(entry); feed.addEntry(entry);
final String title = StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_TITLE)); final String title = StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_TITLE));
entry.setTitle(title); entry.setTitle(title);
final String summary = isFullContent ? StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_CONTENT)) final String summary = isFullContent ? StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_CONTENT))
: StringEscapeUtils.escapeXml(article.optString(Article.ARTICLE_ABSTRACT)); : StringEscapeUtils.escapeXml(article.optString(Article.ARTICLE_ABSTRACT));
entry.setSummary(summary); entry.setSummary(summary);
final Date updated = (Date) article.get(Article.ARTICLE_UPDATE_DATE); final Date updated = (Date) article.get(Article.ARTICLE_UPDATE_DATE);
entry.setUpdated(updated); entry.setUpdated(updated);
final String link = "http://" + blogHost + article.getString(Article.ARTICLE_PERMALINK); final String link = "http://" + blogHost + article.getString(Article.ARTICLE_PERMALINK);
entry.setLink(link); entry.setLink(link);
entry.setId(link); entry.setId(link);
if (hasMultipleUsers) { if (hasMultipleUsers) {
authorName = StringEscapeUtils.escapeXml(articleUtils.getAuthor(article).getString(User.USER_NAME)); authorName = StringEscapeUtils.escapeXml(articleUtils.getAuthor(article).getString(User.USER_NAME));
} }
entry.setAuthor(authorName); entry.setAuthor(authorName);
final String tagsString = article.getString(Article.ARTICLE_TAGS_REF); final String tagsString = article.getString(Article.ARTICLE_TAGS_REF);
final String[] tagStrings = tagsString.split(","); final String[] tagStrings = tagsString.split(",");
for (int j = 0; j < tagStrings.length; j++) { for (int j = 0; j < tagStrings.length; j++) {
final Category catetory = new Category(); final Category catetory = new Category();
entry.addCatetory(catetory); entry.addCatetory(catetory);
final String tag = tagStrings[j]; final String tag = tagStrings[j];
catetory.setTerm(tag); catetory.setTerm(tag);
} }
} }
renderer.setContent(feed.toString()); renderer.setContent(feed.toString());
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Get blog article feed error", e); LOGGER.log(Level.SEVERE, "Get blog article feed error", e);
try { try {
context.getResponse().sendError( context.getResponse().sendError(
HttpServletResponse.SC_SERVICE_UNAVAILABLE); HttpServletResponse.SC_SERVICE_UNAVAILABLE);
} catch (final IOException ex) { } catch (final IOException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
} }
/** /**
* Tag articles Atom output. * Tag articles Atom output.
* *
* @param context the specified context * @param context the specified context
* @throws IOException io exception * @throws IOException io exception
*/ */
@RequestProcessing(value = {"/tag-articles-feed.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD}) @RequestProcessing(value = {"/tag-articles-feed.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
public void tagArticlesAtom(final HTTPRequestContext context) throws IOException { public void tagArticlesAtom(final HTTPRequestContext context) throws IOException {
final AtomRenderer renderer = new AtomRenderer(); final AtomRenderer renderer = new AtomRenderer();
context.setRenderer(renderer); context.setRenderer(renderer);
final HttpServletRequest request = context.getRequest(); final HttpServletRequest request = context.getRequest();
final HttpServletResponse response = context.getResponse(); final HttpServletResponse response = context.getResponse();
final String queryString = request.getQueryString(); final String queryString = request.getQueryString();
if (Strings.isEmptyOrNull(queryString)) { if (Strings.isEmptyOrNull(queryString)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST); response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return; return;
} }
final String oIdMap = queryString.split("&")[0]; final String oIdMap = queryString.split("&")[0];
final String tagId = oIdMap.split("=")[1]; final String tagId = oIdMap.split("=")[1];
final Feed feed = new Feed(); final Feed feed = new Feed();
try { try {
final String tagTitle = tagRepository.get(tagId).getString(Tag.TAG_TITLE); final String tagTitle = tagRepository.get(tagId).getString(Tag.TAG_TITLE);
final JSONObject preference = preferenceQueryService.getPreference(); final JSONObject preference = preferenceQueryService.getPreference();
if (null == preference) { if (null == preference) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); response.sendError(HttpServletResponse.SC_NOT_FOUND);
return; return;
} }
final String blogTitle = preference.getString(Preference.BLOG_TITLE); final String blogTitle = preference.getString(Preference.BLOG_TITLE);
final String blogSubtitle = preference.getString(Preference.BLOG_SUBTITLE) + ", " + tagTitle; final String blogSubtitle = preference.getString(Preference.BLOG_SUBTITLE) + ", " + tagTitle;
final String blogHost = preference.getString(Preference.BLOG_HOST); final String blogHost = preference.getString(Preference.BLOG_HOST);
feed.setTitle(StringEscapeUtils.escapeXml(blogTitle)); feed.setTitle(StringEscapeUtils.escapeXml(blogTitle));
feed.setSubtitle(StringEscapeUtils.escapeXml(blogSubtitle)); feed.setSubtitle(StringEscapeUtils.escapeXml(blogSubtitle));
feed.setUpdated(TimeZones.getTime(preference.getString(Preference.TIME_ZONE_ID))); feed.setUpdated(TimeZones.getTime(preference.getString(Preference.TIME_ZONE_ID)));
feed.setAuthor(StringEscapeUtils.escapeXml(blogTitle)); feed.setAuthor(StringEscapeUtils.escapeXml(blogTitle));
feed.setLink("http://" + blogHost + "/tag-articles-feed.do"); feed.setLink("http://" + blogHost + "/tag-articles-feed.do");
feed.setId("http://" + blogHost + "/"); feed.setId("http://" + blogHost + "/");
final JSONObject tagArticleResult = tagArticleRepository.getByTagId(tagId, 1, ENTRY_OUTPUT_CNT); final JSONObject tagArticleResult = tagArticleRepository.getByTagId(tagId, 1, ENTRY_OUTPUT_CNT);
final JSONArray tagArticleRelations = tagArticleResult.getJSONArray(Keys.RESULTS); final JSONArray tagArticleRelations = tagArticleResult.getJSONArray(Keys.RESULTS);
if (0 == tagArticleRelations.length()) { if (0 == tagArticleRelations.length()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); response.sendError(HttpServletResponse.SC_NOT_FOUND);
return; return;
} }
final List<JSONObject> articles = new ArrayList<JSONObject>(); final List<JSONObject> articles = new ArrayList<JSONObject>();
for (int i = 0; i < tagArticleRelations.length(); i++) { for (int i = 0; i < tagArticleRelations.length(); i++) {
final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(i); final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(i);
final String articleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID); final String articleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID);
final JSONObject article = articleRepository.get(articleId); final JSONObject article = articleRepository.get(articleId);
if (article.getBoolean(Article.ARTICLE_IS_PUBLISHED)) { // Skips the unpublished article if (article.getBoolean(Article.ARTICLE_IS_PUBLISHED)) { // Skips the unpublished article
articles.add(article); articles.add(article);
} }
} }
final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers(); final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers();
String authorName = ""; String authorName = "";
if (!hasMultipleUsers && !articles.isEmpty()) { if (!hasMultipleUsers && !articles.isEmpty()) {
authorName = articleUtils.getAuthor(articles.get(0)).getString(User.USER_NAME); authorName = articleUtils.getAuthor(articles.get(0)).getString(User.USER_NAME);
} }
final boolean isFullContent = "fullContent".equals(preference.getString(Preference.FEED_OUTPUT_MODE)); final boolean isFullContent = "fullContent".equals(preference.getString(Preference.FEED_OUTPUT_MODE));
for (int i = 0; i < articles.size(); i++) { for (int i = 0; i < articles.size(); i++) {
final JSONObject article = articles.get(i); final JSONObject article = articles.get(i);
final Entry entry = new Entry(); final Entry entry = new Entry();
feed.addEntry(entry); feed.addEntry(entry);
final String title = StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_TITLE)); final String title = StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_TITLE));
entry.setTitle(title); entry.setTitle(title);
final String summary = isFullContent ? StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_CONTENT)) final String summary = isFullContent ? StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_CONTENT))
: StringEscapeUtils.escapeXml(article.optString(Article.ARTICLE_ABSTRACT)); : StringEscapeUtils.escapeXml(article.optString(Article.ARTICLE_ABSTRACT));
entry.setSummary(summary); entry.setSummary(summary);
final Date updated = (Date) article.get(Article.ARTICLE_UPDATE_DATE); final Date updated = (Date) article.get(Article.ARTICLE_UPDATE_DATE);
entry.setUpdated(updated); entry.setUpdated(updated);
final String link = "http://" + blogHost + article.getString(Article.ARTICLE_PERMALINK); final String link = "http://" + blogHost + article.getString(Article.ARTICLE_PERMALINK);
entry.setLink(link); entry.setLink(link);
entry.setId(link); entry.setId(link);
if (hasMultipleUsers) { if (hasMultipleUsers) {
authorName = StringEscapeUtils.escapeXml(articleUtils.getAuthor(article).getString(User.USER_NAME)); authorName = StringEscapeUtils.escapeXml(articleUtils.getAuthor(article).getString(User.USER_NAME));
} }
entry.setAuthor(authorName); entry.setAuthor(authorName);
final String tagsString = article.getString(Article.ARTICLE_TAGS_REF); final String tagsString = article.getString(Article.ARTICLE_TAGS_REF);
final String[] tagStrings = tagsString.split(","); final String[] tagStrings = tagsString.split(",");
for (int j = 0; j < tagStrings.length; j++) { for (int j = 0; j < tagStrings.length; j++) {
final Category catetory = new Category(); final Category catetory = new Category();
entry.addCatetory(catetory); entry.addCatetory(catetory);
final String tag = tagStrings[j]; final String tag = tagStrings[j];
catetory.setTerm(tag); catetory.setTerm(tag);
} }
} }
renderer.setContent(feed.toString()); renderer.setContent(feed.toString());
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Get tag article feed error", e); LOGGER.log(Level.SEVERE, "Get tag article feed error", e);
try { try {
context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
} catch (final IOException ex) { } catch (final IOException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
} }
/** /**
* Blog articles RSS output. * Blog articles RSS output.
* *
* @param context the specified context * @param context the specified context
*/ */
@RequestProcessing(value = {"/blog-articles-rss.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD}) @RequestProcessing(value = {"/blog-articles-rss.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
public void blogArticlesRSS(final HTTPRequestContext context) { public void blogArticlesRSS(final HTTPRequestContext context) {
final HttpServletResponse response = context.getResponse(); final HttpServletResponse response = context.getResponse();
final RssRenderer renderer = new RssRenderer(); final RssRenderer renderer = new RssRenderer();
context.setRenderer(renderer); context.setRenderer(renderer);
final Channel channel = new Channel(); final Channel channel = new Channel();
try { try {
final JSONObject preference = preferenceQueryService.getPreference(); final JSONObject preference = preferenceQueryService.getPreference();
if (null == preference) { if (null == preference) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); response.sendError(HttpServletResponse.SC_NOT_FOUND);
return; return;
} }
final String blogTitle = preference.getString(Preference.BLOG_TITLE); final String blogTitle = preference.getString(Preference.BLOG_TITLE);
final String blogSubtitle = preference.getString(Preference.BLOG_SUBTITLE); final String blogSubtitle = preference.getString(Preference.BLOG_SUBTITLE);
final String blogHost = preference.getString(Preference.BLOG_HOST); final String blogHost = preference.getString(Preference.BLOG_HOST);
channel.setTitle(StringEscapeUtils.escapeXml(blogTitle)); channel.setTitle(StringEscapeUtils.escapeXml(blogTitle));
channel.setLastBuildDate(TimeZones.getTime(preference.getString(Preference.TIME_ZONE_ID))); channel.setLastBuildDate(TimeZones.getTime(preference.getString(Preference.TIME_ZONE_ID)));
channel.setLink("http://" + blogHost); channel.setLink("http://" + blogHost);
channel.setAtomLink("http://" + blogHost + "/blog-articles-rss.do"); channel.setAtomLink("http://" + blogHost + "/blog-articles-rss.do");
channel.setGenerator("B3log Solo, ver " + SoloServletListener.VERSION); channel.setGenerator("B3log Solo, ver " + SoloServletListener.VERSION);
final String localeString = preference.getString(Preference.LOCALE_STRING); final String localeString = preference.getString(Preference.LOCALE_STRING);
final String country = Locales.getCountry(localeString).toLowerCase(); final String country = Locales.getCountry(localeString).toLowerCase();
final String language = Locales.getLanguage(localeString).toLowerCase(); final String language = Locales.getLanguage(localeString).toLowerCase();
channel.setLanguage(language + '-' + country); channel.setLanguage(language + '-' + country);
channel.setDescription(blogSubtitle); channel.setDescription(blogSubtitle);
final Query query = new Query().setCurrentPageNum(1). final Query query = new Query().setCurrentPageNum(1).
setPageSize(ENTRY_OUTPUT_CNT). setPageSize(ENTRY_OUTPUT_CNT).
addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true). addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true).
addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING). addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING).
setPageCount(1); setPageCount(1);
final JSONObject articleResult = articleRepository.get(query); final JSONObject articleResult = articleRepository.get(query);
final JSONArray articles = articleResult.getJSONArray(Keys.RESULTS); final JSONArray articles = articleResult.getJSONArray(Keys.RESULTS);
final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers(); final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers();
String authorName = ""; String authorName = "";
if (!hasMultipleUsers && 0 != articles.length()) { if (!hasMultipleUsers && 0 != articles.length()) {
authorName = articleUtils.getAuthor(articles.getJSONObject(0)).getString(User.USER_NAME); authorName = articleUtils.getAuthor(articles.getJSONObject(0)).getString(User.USER_NAME);
} }
final boolean isFullContent = "fullContent".equals(preference.getString(Preference.FEED_OUTPUT_MODE)); final boolean isFullContent = "fullContent".equals(preference.getString(Preference.FEED_OUTPUT_MODE));
for (int i = 0; i < articles.length(); i++) { for (int i = 0; i < articles.length(); i++) {
final JSONObject article = articles.getJSONObject(i); final JSONObject article = articles.getJSONObject(i);
final Item item = new Item(); final Item item = new Item();
channel.addItem(item); channel.addItem(item);
final String title = StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_TITLE)); final String title = StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_TITLE));
item.setTitle(title); item.setTitle(title);
final String description = isFullContent ? StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_CONTENT)) final String description = isFullContent ? StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_CONTENT))
: StringEscapeUtils.escapeXml(article.optString(Article.ARTICLE_ABSTRACT)); : StringEscapeUtils.escapeXml(article.optString(Article.ARTICLE_ABSTRACT));
item.setDescription(description); item.setDescription(description);
final Date pubDate = (Date) article.get(Article.ARTICLE_UPDATE_DATE); final Date pubDate = (Date) article.get(Article.ARTICLE_UPDATE_DATE);
item.setPubDate(pubDate); item.setPubDate(pubDate);
final String link = "http://" + blogHost + article.getString(Article.ARTICLE_PERMALINK); final String link = "http://" + blogHost + article.getString(Article.ARTICLE_PERMALINK);
item.setLink(link); item.setLink(link);
item.setGUID(link); item.setGUID(link);
final String authorEmail = article.getString(Article.ARTICLE_AUTHOR_EMAIL); final String authorEmail = article.getString(Article.ARTICLE_AUTHOR_EMAIL);
if (hasMultipleUsers) { if (hasMultipleUsers) {
authorName = StringEscapeUtils.escapeXml(articleUtils.getAuthor(article).getString(User.USER_NAME)); authorName = StringEscapeUtils.escapeXml(articleUtils.getAuthor(article).getString(User.USER_NAME));
} }
item.setAuthor(authorEmail + "(" + authorName + ")"); item.setAuthor(authorEmail + "(" + authorName + ")");
final String tagsString = article.getString(Article.ARTICLE_TAGS_REF); final String tagsString = article.getString(Article.ARTICLE_TAGS_REF);
final String[] tagStrings = tagsString.split(","); final String[] tagStrings = tagsString.split(",");
for (int j = 0; j < tagStrings.length; j++) { for (int j = 0; j < tagStrings.length; j++) {
final org.b3log.solo.model.feed.rss.Category catetory = new org.b3log.solo.model.feed.rss.Category(); final org.b3log.solo.model.feed.rss.Category catetory = new org.b3log.solo.model.feed.rss.Category();
item.addCatetory(catetory); item.addCatetory(catetory);
final String tag = tagStrings[j]; final String tag = tagStrings[j];
catetory.setTerm(tag); catetory.setTerm(tag);
} }
} }
renderer.setContent(channel.toString()); renderer.setContent(channel.toString());
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Get blog article rss error", e); LOGGER.log(Level.SEVERE, "Get blog article rss error", e);
try { try {
context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
} catch (final IOException ex) { } catch (final IOException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
} }
/** /**
* Tag articles RSS output. * Tag articles RSS output.
* *
* @param context the specified context * @param context the specified context
* @throws IOException io exception * @throws IOException io exception
*/ */
@RequestProcessing(value = {"/tag-articles-rss.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD}) @RequestProcessing(value = {"/tag-articles-rss.do"}, method = {HTTPRequestMethod.GET, HTTPRequestMethod.HEAD})
public void tagArticlesRSS(final HTTPRequestContext context) throws IOException { public void tagArticlesRSS(final HTTPRequestContext context) throws IOException {
final HttpServletResponse response = context.getResponse(); final HttpServletResponse response = context.getResponse();
final HttpServletRequest request = context.getRequest(); final HttpServletRequest request = context.getRequest();
final RssRenderer renderer = new RssRenderer(); final RssRenderer renderer = new RssRenderer();
context.setRenderer(renderer); context.setRenderer(renderer);
final String queryString = request.getQueryString(); final String queryString = request.getQueryString();
if (Strings.isEmptyOrNull(queryString)) { if (Strings.isEmptyOrNull(queryString)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST); response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return; return;
} }
final String oIdMap = queryString.split("&")[0]; final String oIdMap = queryString.split("&")[0];
final String tagId = oIdMap.split("=")[1]; final String tagId = oIdMap.split("=")[1];
final Channel channel = new Channel(); final Channel channel = new Channel();
try { try {
final String tagTitle = tagRepository.get(tagId).getString(Tag.TAG_TITLE); final String tagTitle = tagRepository.get(tagId).getString(Tag.TAG_TITLE);
final JSONObject preference = preferenceQueryService.getPreference(); final JSONObject preference = preferenceQueryService.getPreference();
if (null == preference) { if (null == preference) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); response.sendError(HttpServletResponse.SC_NOT_FOUND);
return; return;
} }
final String blogTitle = preference.getString(Preference.BLOG_TITLE); final String blogTitle = preference.getString(Preference.BLOG_TITLE);
final String blogSubtitle = preference.getString(Preference.BLOG_SUBTITLE) + ", " + tagTitle; final String blogSubtitle = preference.getString(Preference.BLOG_SUBTITLE) + ", " + tagTitle;
final String blogHost = preference.getString(Preference.BLOG_HOST); final String blogHost = preference.getString(Preference.BLOG_HOST);
channel.setTitle(StringEscapeUtils.escapeXml(blogTitle)); channel.setTitle(StringEscapeUtils.escapeXml(blogTitle));
channel.setLastBuildDate(TimeZones.getTime(preference.getString(Preference.TIME_ZONE_ID))); channel.setLastBuildDate(TimeZones.getTime(preference.getString(Preference.TIME_ZONE_ID)));
channel.setLink("http://" + blogHost); channel.setLink("http://" + blogHost);
channel.setAtomLink("http://" + blogHost + "/tag-articles-rss.do"); channel.setAtomLink("http://" + blogHost + "/tag-articles-rss.do");
channel.setGenerator("B3log Solo, ver " + SoloServletListener.VERSION); channel.setGenerator("B3log Solo, ver " + SoloServletListener.VERSION);
final String localeString = preference.getString(Preference.LOCALE_STRING); final String localeString = preference.getString(Preference.LOCALE_STRING);
final String country = Locales.getCountry(localeString).toLowerCase(); final String country = Locales.getCountry(localeString).toLowerCase();
final String language = Locales.getLanguage(localeString).toLowerCase(); final String language = Locales.getLanguage(localeString).toLowerCase();
channel.setLanguage(language + '-' + country); channel.setLanguage(language + '-' + country);
channel.setDescription(blogSubtitle); channel.setDescription(blogSubtitle);
final JSONObject tagArticleResult = tagArticleRepository.getByTagId(tagId, 1, ENTRY_OUTPUT_CNT); final JSONObject tagArticleResult = tagArticleRepository.getByTagId(tagId, 1, ENTRY_OUTPUT_CNT);
final JSONArray tagArticleRelations = tagArticleResult.getJSONArray(Keys.RESULTS); final JSONArray tagArticleRelations = tagArticleResult.getJSONArray(Keys.RESULTS);
if (0 == tagArticleRelations.length()) { if (0 == tagArticleRelations.length()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); response.sendError(HttpServletResponse.SC_NOT_FOUND);
return; return;
} }
final List<JSONObject> articles = new ArrayList<JSONObject>(); final List<JSONObject> articles = new ArrayList<JSONObject>();
for (int i = 0; i < tagArticleRelations.length(); i++) { for (int i = 0; i < tagArticleRelations.length(); i++) {
final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(i); final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(i);
final String articleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID); final String articleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID);
final JSONObject article = articleRepository.get(articleId); final JSONObject article = articleRepository.get(articleId);
if (article.getBoolean(Article.ARTICLE_IS_PUBLISHED)) { // Skips the unpublished article if (article.getBoolean(Article.ARTICLE_IS_PUBLISHED)) { // Skips the unpublished article
articles.add(article); articles.add(article);
} }
} }
final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers(); final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers();
String authorName = ""; String authorName = "";
if (!hasMultipleUsers && !articles.isEmpty()) { if (!hasMultipleUsers && !articles.isEmpty()) {
authorName = articleUtils.getAuthor(articles.get(0)).getString(User.USER_NAME); authorName = articleUtils.getAuthor(articles.get(0)).getString(User.USER_NAME);
} }
final boolean isFullContent = "fullContent".equals(preference.getString(Preference.FEED_OUTPUT_MODE)); final boolean isFullContent = "fullContent".equals(preference.getString(Preference.FEED_OUTPUT_MODE));
for (int i = 0; i < articles.size(); i++) { for (int i = 0; i < articles.size(); i++) {
final JSONObject article = articles.get(i); final JSONObject article = articles.get(i);
final Item item = new Item(); final Item item = new Item();
channel.addItem(item); channel.addItem(item);
final String title = StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_TITLE)); final String title = StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_TITLE));
item.setTitle(title); item.setTitle(title);
final String description = isFullContent ? StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_CONTENT)) final String description = isFullContent ? StringEscapeUtils.escapeXml(article.getString(Article.ARTICLE_CONTENT))
: StringEscapeUtils.escapeXml(article.optString(Article.ARTICLE_ABSTRACT)); : StringEscapeUtils.escapeXml(article.optString(Article.ARTICLE_ABSTRACT));
item.setDescription(description); item.setDescription(description);
final Date pubDate = (Date) article.get(Article.ARTICLE_UPDATE_DATE); final Date pubDate = (Date) article.get(Article.ARTICLE_UPDATE_DATE);
item.setPubDate(pubDate); item.setPubDate(pubDate);
final String link = "http://" + blogHost + article.getString(Article.ARTICLE_PERMALINK); final String link = "http://" + blogHost + article.getString(Article.ARTICLE_PERMALINK);
item.setLink(link); item.setLink(link);
item.setGUID(link); item.setGUID(link);
final String authorEmail = article.getString(Article.ARTICLE_AUTHOR_EMAIL); final String authorEmail = article.getString(Article.ARTICLE_AUTHOR_EMAIL);
if (hasMultipleUsers) { if (hasMultipleUsers) {
authorName = StringEscapeUtils.escapeXml(articleUtils.getAuthor(article).getString(User.USER_NAME)); authorName = StringEscapeUtils.escapeXml(articleUtils.getAuthor(article).getString(User.USER_NAME));
} }
item.setAuthor(authorEmail + "(" + authorName + ")"); item.setAuthor(authorEmail + "(" + authorName + ")");
final String tagsString = article.getString(Article.ARTICLE_TAGS_REF); final String tagsString = article.getString(Article.ARTICLE_TAGS_REF);
final String[] tagStrings = tagsString.split(","); final String[] tagStrings = tagsString.split(",");
for (int j = 0; j < tagStrings.length; j++) { for (int j = 0; j < tagStrings.length; j++) {
final org.b3log.solo.model.feed.rss.Category catetory = new org.b3log.solo.model.feed.rss.Category(); final org.b3log.solo.model.feed.rss.Category catetory = new org.b3log.solo.model.feed.rss.Category();
item.addCatetory(catetory); item.addCatetory(catetory);
final String tag = tagStrings[j]; final String tag = tagStrings[j];
catetory.setTerm(tag); catetory.setTerm(tag);
} }
} }
renderer.setContent(channel.toString()); renderer.setContent(channel.toString());
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Get tag article rss error", e); LOGGER.log(Level.SEVERE, "Get tag article rss error", e);
try { try {
context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
} catch (final IOException ex) { } catch (final IOException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.processor; package org.b3log.solo.processor;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Date; import java.util.Date;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.time.DateFormatUtils; import org.apache.commons.lang.time.DateFormatUtils;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.annotation.RequestProcessing; import org.b3log.latke.annotation.RequestProcessing;
import org.b3log.latke.annotation.RequestProcessor; import org.b3log.latke.annotation.RequestProcessor;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.servlet.HTTPRequestContext; import org.b3log.latke.servlet.HTTPRequestContext;
import org.b3log.latke.servlet.HTTPRequestMethod; import org.b3log.latke.servlet.HTTPRequestMethod;
import org.b3log.latke.servlet.renderer.TextXMLRenderer; import org.b3log.latke.servlet.renderer.TextXMLRenderer;
import org.b3log.solo.model.ArchiveDate; import org.b3log.solo.model.ArchiveDate;
import org.b3log.solo.model.Article; import org.b3log.solo.model.Article;
import org.b3log.solo.model.Page; import org.b3log.solo.model.Page;
import org.b3log.solo.model.Preference; import org.b3log.solo.model.Preference;
import org.b3log.solo.model.Tag; import org.b3log.solo.model.Tag;
import org.b3log.solo.model.sitemap.Sitemap; import org.b3log.solo.model.sitemap.Sitemap;
import org.b3log.solo.model.sitemap.URL; import org.b3log.solo.model.sitemap.URL;
import org.b3log.solo.repository.ArchiveDateRepository; import org.b3log.solo.repository.ArchiveDateRepository;
import org.b3log.solo.repository.PageRepository; import org.b3log.solo.repository.PageRepository;
import org.b3log.solo.repository.TagRepository; import org.b3log.solo.repository.TagRepository;
import org.b3log.solo.repository.impl.ArchiveDateRepositoryImpl; import org.b3log.solo.repository.impl.ArchiveDateRepositoryImpl;
import org.b3log.solo.repository.impl.ArticleRepositoryImpl; import org.b3log.solo.repository.impl.ArticleRepositoryImpl;
import org.b3log.solo.repository.impl.PageRepositoryImpl; import org.b3log.solo.repository.impl.PageRepositoryImpl;
import org.b3log.solo.repository.impl.TagRepositoryImpl; import org.b3log.solo.repository.impl.TagRepositoryImpl;
import org.b3log.solo.service.PreferenceQueryService; import org.b3log.solo.service.PreferenceQueryService;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Site map (sitemap) processor. * Site map (sitemap) processor.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.4, Jun 11, 2012 * @version 1.0.0.4, Jun 11, 2012
* @since 0.3.1 * @since 0.3.1
*/ */
@RequestProcessor @RequestProcessor
public final class SitemapProcessor { public final class SitemapProcessor {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(SitemapProcessor.class.getName()); private static final Logger LOGGER = Logger.getLogger(SitemapProcessor.class.getName());
/** /**
* Preference query service. * Preference query service.
*/ */
private PreferenceQueryService preferenceQueryService = PreferenceQueryService.getInstance(); private PreferenceQueryService preferenceQueryService = PreferenceQueryService.getInstance();
/** /**
* Article repository. * Article repository.
*/ */
private ArticleRepositoryImpl articleRepository = ArticleRepositoryImpl.getInstance(); private ArticleRepositoryImpl articleRepository = ArticleRepositoryImpl.getInstance();
/** /**
* Page repository. * Page repository.
*/ */
private PageRepository pageRepository = PageRepositoryImpl.getInstance(); private PageRepository pageRepository = PageRepositoryImpl.getInstance();
/** /**
* Tag repository. * Tag repository.
*/ */
private TagRepository tagRepository = TagRepositoryImpl.getInstance(); private TagRepository tagRepository = TagRepositoryImpl.getInstance();
/** /**
* Archive date repository. * Archive date repository.
*/ */
private ArchiveDateRepository archiveDateRepository = ArchiveDateRepositoryImpl.getInstance(); private ArchiveDateRepository archiveDateRepository = ArchiveDateRepositoryImpl.getInstance();
/** /**
* Returns the sitemap. * Returns the sitemap.
* *
* @param context the specified context * @param context the specified context
*/ */
@RequestProcessing(value = {"/sitemap.xml"}, method = HTTPRequestMethod.GET) @RequestProcessing(value = {"/sitemap.xml"}, method = HTTPRequestMethod.GET)
public void sitemap(final HTTPRequestContext context) { public void sitemap(final HTTPRequestContext context) {
final TextXMLRenderer renderer = new TextXMLRenderer(); final TextXMLRenderer renderer = new TextXMLRenderer();
context.setRenderer(renderer); context.setRenderer(renderer);
final Sitemap sitemap = new Sitemap(); final Sitemap sitemap = new Sitemap();
try { try {
final JSONObject preference = preferenceQueryService.getPreference(); final JSONObject preference = preferenceQueryService.getPreference();
addArticles(sitemap, preference); addArticles(sitemap, preference);
addNavigations(sitemap, preference); addNavigations(sitemap, preference);
addTags(sitemap, preference); addTags(sitemap, preference);
addArchives(sitemap, preference); addArchives(sitemap, preference);
LOGGER.log(Level.INFO, "Generating sitemap...."); LOGGER.log(Level.INFO, "Generating sitemap....");
final String content = sitemap.toString(); final String content = sitemap.toString();
LOGGER.log(Level.INFO, "Generated sitemap"); LOGGER.log(Level.INFO, "Generated sitemap");
renderer.setContent(content); renderer.setContent(content);
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Get blog article feed error", e); LOGGER.log(Level.SEVERE, "Get blog article feed error", e);
try { try {
context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); context.getResponse().sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
} catch (final IOException ex) { } catch (final IOException ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
} }
/** /**
* Adds articles into the specified sitemap. * Adds articles into the specified sitemap.
* *
* @param sitemap the specified sitemap * @param sitemap the specified sitemap
* @param preference the specified preference * @param preference the specified preference
* @throws Exception exception * @throws Exception exception
*/ */
private void addArticles(final Sitemap sitemap, final JSONObject preference) throws Exception { private void addArticles(final Sitemap sitemap, final JSONObject preference) throws Exception {
final String host = preference.getString(Preference.BLOG_HOST); final String host = preference.getString(Preference.BLOG_HOST);
// XXX: query all articles? // XXX: query all articles?
final Query query = new Query().setCurrentPageNum(1). final Query query = new Query().setCurrentPageNum(1).
addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true). addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true).
addSort(Article.ARTICLE_CREATE_DATE, SortDirection.DESCENDING); addSort(Article.ARTICLE_CREATE_DATE, SortDirection.DESCENDING);
// Closes cache avoid Java heap space out of memory while caching // Closes cache avoid Java heap space out of memory while caching
// query results // query results
articleRepository.setCacheEnabled(false); articleRepository.setCacheEnabled(false);
final JSONObject articleResult = articleRepository.get(query); final JSONObject articleResult = articleRepository.get(query);
articleRepository.setCacheEnabled(true); // Restores cache articleRepository.setCacheEnabled(true); // Restores cache
final JSONArray articles = articleResult.getJSONArray(Keys.RESULTS); final JSONArray articles = articleResult.getJSONArray(Keys.RESULTS);
for (int i = 0; i < articles.length(); i++) { for (int i = 0; i < articles.length(); i++) {
final JSONObject article = articles.getJSONObject(i); final JSONObject article = articles.getJSONObject(i);
final String permalink = article.getString(Article.ARTICLE_PERMALINK); final String permalink = article.getString(Article.ARTICLE_PERMALINK);
final URL url = new URL(); final URL url = new URL();
url.setLoc("http://" + host + permalink); url.setLoc("http://" + host + permalink);
final Date updateDate = (Date) article.get(Article.ARTICLE_UPDATE_DATE); final Date updateDate = (Date) article.get(Article.ARTICLE_UPDATE_DATE);
final String lastMod = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(updateDate); final String lastMod = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(updateDate);
url.setLastMod(lastMod); url.setLastMod(lastMod);
sitemap.addURL(url); sitemap.addURL(url);
} }
} }
/** /**
* Adds navigations into the specified sitemap. * Adds navigations into the specified sitemap.
* *
* @param sitemap the specified sitemap * @param sitemap the specified sitemap
* @param preference the specified preference * @param preference the specified preference
* @throws Exception exception * @throws Exception exception
*/ */
private void addNavigations(final Sitemap sitemap, final JSONObject preference) throws Exception { private void addNavigations(final Sitemap sitemap, final JSONObject preference) throws Exception {
final String host = preference.getString(Preference.BLOG_HOST); final String host = preference.getString(Preference.BLOG_HOST);
final JSONObject result = pageRepository.get(new Query()); final JSONObject result = pageRepository.get(new Query());
final JSONArray pages = result.getJSONArray(Keys.RESULTS); final JSONArray pages = result.getJSONArray(Keys.RESULTS);
for (int i = 0; i < pages.length(); i++) { for (int i = 0; i < pages.length(); i++) {
final JSONObject page = pages.getJSONObject(i); final JSONObject page = pages.getJSONObject(i);
final String permalink = page.getString(Page.PAGE_PERMALINK); final String permalink = page.getString(Page.PAGE_PERMALINK);
final URL url = new URL(); final URL url = new URL();
// The navigation maybe a page or a link // The navigation maybe a page or a link
// Just filters for user mistakes tolerance // Just filters for user mistakes tolerance
if (!permalink.contains("://")) { if (!permalink.contains("://")) {
url.setLoc("http://" + host + permalink); url.setLoc("http://" + host + permalink);
} else { } else {
url.setLoc(permalink); url.setLoc(permalink);
} }
sitemap.addURL(url); sitemap.addURL(url);
} }
} }
/** /**
* Adds tags (tag-articles) and tags wall (/tags.html) into the specified * Adds tags (tag-articles) and tags wall (/tags.html) into the specified
* sitemap. * sitemap.
* *
* @param sitemap the specified sitemap * @param sitemap the specified sitemap
* @param preference the specified preference * @param preference the specified preference
* @throws Exception exception * @throws Exception exception
*/ */
private void addTags(final Sitemap sitemap, final JSONObject preference) throws Exception { private void addTags(final Sitemap sitemap, final JSONObject preference) throws Exception {
final String host = preference.getString(Preference.BLOG_HOST); final String host = preference.getString(Preference.BLOG_HOST);
final JSONObject result = tagRepository.get(new Query()); final JSONObject result = tagRepository.get(new Query());
final JSONArray tags = result.getJSONArray(Keys.RESULTS); final JSONArray tags = result.getJSONArray(Keys.RESULTS);
for (int i = 0; i < tags.length(); i++) { for (int i = 0; i < tags.length(); i++) {
final JSONObject tag = tags.getJSONObject(i); final JSONObject tag = tags.getJSONObject(i);
final String link = URLEncoder.encode(tag.getString(Tag.TAG_TITLE), "UTF-8"); final String link = URLEncoder.encode(tag.getString(Tag.TAG_TITLE), "UTF-8");
final URL url = new URL(); final URL url = new URL();
url.setLoc("http://" + host + "/tags/" + link); url.setLoc("http://" + host + "/tags/" + link);
sitemap.addURL(url); sitemap.addURL(url);
} }
// Tags wall // Tags wall
final URL url = new URL(); final URL url = new URL();
url.setLoc("http://" + host + "/tags.html"); url.setLoc("http://" + host + "/tags.html");
sitemap.addURL(url); sitemap.addURL(url);
} }
/** /**
* Adds archives (archive-articles) into the specified sitemap. * Adds archives (archive-articles) into the specified sitemap.
* *
* @param sitemap the specified sitemap * @param sitemap the specified sitemap
* @param preference the specified preference * @param preference the specified preference
* @throws Exception exception * @throws Exception exception
*/ */
private void addArchives(final Sitemap sitemap, final JSONObject preference) throws Exception { private void addArchives(final Sitemap sitemap, final JSONObject preference) throws Exception {
final String host = preference.getString(Preference.BLOG_HOST); final String host = preference.getString(Preference.BLOG_HOST);
final JSONObject result = archiveDateRepository.get(new Query()); final JSONObject result = archiveDateRepository.get(new Query());
final JSONArray archiveDates = result.getJSONArray(Keys.RESULTS); final JSONArray archiveDates = result.getJSONArray(Keys.RESULTS);
for (int i = 0; i < archiveDates.length(); i++) { for (int i = 0; i < archiveDates.length(); i++) {
final JSONObject archiveDate = archiveDates.getJSONObject(i); final JSONObject archiveDate = archiveDates.getJSONObject(i);
final long time = archiveDate.getLong(ArchiveDate.ARCHIVE_TIME); final long time = archiveDate.getLong(ArchiveDate.ARCHIVE_TIME);
final String dateString = ArchiveDate.DATE_FORMAT.format(time); final String dateString = ArchiveDate.DATE_FORMAT.format(time);
final URL url = new URL(); final URL url = new URL();
url.setLoc("http://" + host + "/archives/" + dateString); url.setLoc("http://" + host + "/archives/" + dateString);
sitemap.addURL(url); sitemap.addURL(url);
} }
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.processor.util; package org.b3log.solo.processor.util;
import freemarker.template.Template; import freemarker.template.Template;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringEscapeUtils;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.solo.util.Articles; import org.b3log.solo.util.Articles;
import org.b3log.solo.model.Article; import org.b3log.solo.model.Article;
import org.b3log.solo.repository.ArticleRepository; import org.b3log.solo.repository.ArticleRepository;
import org.b3log.solo.repository.TagRepository; import org.b3log.solo.repository.TagRepository;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.Latkes; import org.b3log.latke.Latkes;
import org.b3log.latke.action.AbstractAction; import org.b3log.latke.action.AbstractAction;
import org.b3log.latke.event.Event; import org.b3log.latke.event.Event;
import org.b3log.latke.event.EventException; import org.b3log.latke.event.EventException;
import org.b3log.latke.event.EventManager; import org.b3log.latke.event.EventManager;
import org.b3log.latke.model.Pagination; import org.b3log.latke.model.Pagination;
import org.b3log.latke.model.Plugin; import org.b3log.latke.model.Plugin;
import org.b3log.latke.model.User; import org.b3log.latke.model.User;
import org.b3log.latke.plugin.ViewLoadEventData; import org.b3log.latke.plugin.ViewLoadEventData;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.service.ServiceException; import org.b3log.latke.service.ServiceException;
import org.b3log.latke.util.*; import org.b3log.latke.util.*;
import org.b3log.latke.util.freemarker.Templates; import org.b3log.latke.util.freemarker.Templates;
import org.b3log.solo.model.ArchiveDate; import org.b3log.solo.model.ArchiveDate;
import org.b3log.solo.model.Link; import org.b3log.solo.model.Link;
import org.b3log.solo.model.Preference; import org.b3log.solo.model.Preference;
import org.b3log.solo.repository.CommentRepository; import org.b3log.solo.repository.CommentRepository;
import org.b3log.solo.repository.LinkRepository; import org.b3log.solo.repository.LinkRepository;
import org.b3log.solo.SoloServletListener; import org.b3log.solo.SoloServletListener;
import org.b3log.solo.model.*; import org.b3log.solo.model.*;
import org.b3log.solo.repository.ArchiveDateRepository; import org.b3log.solo.repository.ArchiveDateRepository;
import org.b3log.solo.repository.PageRepository; import org.b3log.solo.repository.PageRepository;
import org.b3log.solo.repository.StatisticRepository; import org.b3log.solo.repository.StatisticRepository;
import org.b3log.solo.repository.UserRepository; import org.b3log.solo.repository.UserRepository;
import org.b3log.solo.repository.impl.ArchiveDateRepositoryImpl; import org.b3log.solo.repository.impl.ArchiveDateRepositoryImpl;
import org.b3log.solo.repository.impl.ArticleRepositoryImpl; import org.b3log.solo.repository.impl.ArticleRepositoryImpl;
import org.b3log.solo.repository.impl.CommentRepositoryImpl; import org.b3log.solo.repository.impl.CommentRepositoryImpl;
import org.b3log.solo.repository.impl.LinkRepositoryImpl; import org.b3log.solo.repository.impl.LinkRepositoryImpl;
import org.b3log.solo.repository.impl.PageRepositoryImpl; import org.b3log.solo.repository.impl.PageRepositoryImpl;
import org.b3log.solo.repository.impl.StatisticRepositoryImpl; import org.b3log.solo.repository.impl.StatisticRepositoryImpl;
import org.b3log.solo.repository.impl.TagRepositoryImpl; import org.b3log.solo.repository.impl.TagRepositoryImpl;
import org.b3log.solo.repository.impl.UserRepositoryImpl; import org.b3log.solo.repository.impl.UserRepositoryImpl;
import org.b3log.solo.service.ArticleQueryService; import org.b3log.solo.service.ArticleQueryService;
import org.b3log.solo.util.Tags; import org.b3log.solo.util.Tags;
import org.b3log.solo.util.Users; import org.b3log.solo.util.Users;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Filler utilities. * Filler utilities.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.5.9, May 22, 2012 * @version 1.0.5.9, May 22, 2012
* @since 0.3.1 * @since 0.3.1
*/ */
public final class Filler { public final class Filler {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(Filler.class.getName()); private static final Logger LOGGER = Logger.getLogger(Filler.class.getName());
/** /**
* Article repository. * Article repository.
*/ */
private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance(); private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance();
/** /**
* Comment repository. * Comment repository.
*/ */
private CommentRepository commentRepository = CommentRepositoryImpl.getInstance(); private CommentRepository commentRepository = CommentRepositoryImpl.getInstance();
/** /**
* Archive date repository. * Archive date repository.
*/ */
private ArchiveDateRepository archiveDateRepository = ArchiveDateRepositoryImpl.getInstance(); private ArchiveDateRepository archiveDateRepository = ArchiveDateRepositoryImpl.getInstance();
/** /**
* Tag repository. * Tag repository.
*/ */
private TagRepository tagRepository = TagRepositoryImpl.getInstance(); private TagRepository tagRepository = TagRepositoryImpl.getInstance();
/** /**
* Article utilities. * Article utilities.
*/ */
private Articles articleUtils = Articles.getInstance(); private Articles articleUtils = Articles.getInstance();
/** /**
* Tag utilities. * Tag utilities.
*/ */
private Tags tagUtils = Tags.getInstance(); private Tags tagUtils = Tags.getInstance();
/** /**
* Link repository. * Link repository.
*/ */
private LinkRepository linkRepository = LinkRepositoryImpl.getInstance(); private LinkRepository linkRepository = LinkRepositoryImpl.getInstance();
/** /**
* Page repository. * Page repository.
*/ */
private PageRepository pageRepository = PageRepositoryImpl.getInstance(); private PageRepository pageRepository = PageRepositoryImpl.getInstance();
/** /**
* Statistic repository. * Statistic repository.
*/ */
private StatisticRepository statisticRepository = StatisticRepositoryImpl.getInstance(); private StatisticRepository statisticRepository = StatisticRepositoryImpl.getInstance();
/** /**
* User repository. * User repository.
*/ */
private UserRepository userRepository = UserRepositoryImpl.getInstance(); private UserRepository userRepository = UserRepositoryImpl.getInstance();
/** /**
* Article query service. * Article query service.
*/ */
private ArticleQueryService articleQueryService = ArticleQueryService.getInstance(); private ArticleQueryService articleQueryService = ArticleQueryService.getInstance();
/** /**
* {@code true} for published. * {@code true} for published.
*/ */
private static final boolean PUBLISHED = true; private static final boolean PUBLISHED = true;
/** /**
* Fills articles in index.ftl. * Fills articles in index.ftl.
* *
* @param dataModel data model * @param dataModel data model
* @param currentPageNum current page number * @param currentPageNum current page number
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillIndexArticles(final Map<String, Object> dataModel, final int currentPageNum, final JSONObject preference) public void fillIndexArticles(final Map<String, Object> dataModel, final int currentPageNum, final JSONObject preference)
throws ServiceException { throws ServiceException {
Stopwatchs.start("Fill Index Articles"); Stopwatchs.start("Fill Index Articles");
try { try {
final int pageSize = preference.getInt(Preference.ARTICLE_LIST_DISPLAY_COUNT); final int pageSize = preference.getInt(Preference.ARTICLE_LIST_DISPLAY_COUNT);
final int windowSize = preference.getInt(Preference.ARTICLE_LIST_PAGINATION_WINDOW_SIZE); final int windowSize = preference.getInt(Preference.ARTICLE_LIST_PAGINATION_WINDOW_SIZE);
final JSONObject statistic = statisticRepository.get(Statistic.STATISTIC); final JSONObject statistic = statisticRepository.get(Statistic.STATISTIC);
final int publishedArticleCnt = statistic.getInt(Statistic.STATISTIC_PUBLISHED_ARTICLE_COUNT); final int publishedArticleCnt = statistic.getInt(Statistic.STATISTIC_PUBLISHED_ARTICLE_COUNT);
final int pageCount = (int) Math.ceil((double) publishedArticleCnt / (double) pageSize); final int pageCount = (int) Math.ceil((double) publishedArticleCnt / (double) pageSize);
final Query query = new Query().setCurrentPageNum(currentPageNum).setPageSize(pageSize).setPageCount(pageCount). final Query query = new Query().setCurrentPageNum(currentPageNum).setPageSize(pageSize).setPageCount(pageCount).
addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, PUBLISHED). addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, PUBLISHED).
addSort(Article.ARTICLE_PUT_TOP, SortDirection.DESCENDING). addSort(Article.ARTICLE_PUT_TOP, SortDirection.DESCENDING).
index(Article.ARTICLE_PERMALINK); index(Article.ARTICLE_PERMALINK);
if (preference.getBoolean(Preference.ENABLE_ARTICLE_UPDATE_HINT)) { if (preference.getBoolean(Preference.ENABLE_ARTICLE_UPDATE_HINT)) {
query.addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING); query.addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING);
} else { } else {
query.addSort(Article.ARTICLE_CREATE_DATE, SortDirection.DESCENDING); query.addSort(Article.ARTICLE_CREATE_DATE, SortDirection.DESCENDING);
} }
final JSONObject result = articleRepository.get(query); final JSONObject result = articleRepository.get(query);
final List<Integer> pageNums = Paginator.paginate(currentPageNum, pageSize, pageCount, windowSize); final List<Integer> pageNums = Paginator.paginate(currentPageNum, pageSize, pageCount, windowSize);
if (0 != pageNums.size()) { if (0 != pageNums.size()) {
dataModel.put(Pagination.PAGINATION_FIRST_PAGE_NUM, pageNums.get(0)); dataModel.put(Pagination.PAGINATION_FIRST_PAGE_NUM, pageNums.get(0));
dataModel.put(Pagination.PAGINATION_LAST_PAGE_NUM, pageNums.get(pageNums.size() - 1)); dataModel.put(Pagination.PAGINATION_LAST_PAGE_NUM, pageNums.get(pageNums.size() - 1));
} }
dataModel.put(Pagination.PAGINATION_PAGE_COUNT, pageCount); dataModel.put(Pagination.PAGINATION_PAGE_COUNT, pageCount);
dataModel.put(Pagination.PAGINATION_PAGE_NUMS, pageNums); dataModel.put(Pagination.PAGINATION_PAGE_NUMS, pageNums);
final List<JSONObject> articles = org.b3log.latke.util.CollectionUtils.jsonArrayToList(result.getJSONArray(Keys.RESULTS)); final List<JSONObject> articles = org.b3log.latke.util.CollectionUtils.jsonArrayToList(result.getJSONArray(Keys.RESULTS));
final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers(); final boolean hasMultipleUsers = Users.getInstance().hasMultipleUsers();
if (hasMultipleUsers) { if (hasMultipleUsers) {
setArticlesExProperties(articles, preference); setArticlesExProperties(articles, preference);
} else { } else {
if (!articles.isEmpty()) { if (!articles.isEmpty()) {
final JSONObject author = articleUtils.getAuthor(articles.get(0)); final JSONObject author = articleUtils.getAuthor(articles.get(0));
setArticlesExProperties(articles, author, preference); setArticlesExProperties(articles, author, preference);
} }
} }
dataModel.put(Article.ARTICLES, articles); dataModel.put(Article.ARTICLES, articles);
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Fills index articles failed", e); LOGGER.log(Level.SEVERE, "Fills index articles failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Fills index articles failed", e); LOGGER.log(Level.SEVERE, "Fills index articles failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills links. * Fills links.
* *
* @param dataModel data model * @param dataModel data model
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillLinks(final Map<String, Object> dataModel) throws ServiceException { public void fillLinks(final Map<String, Object> dataModel) throws ServiceException {
Stopwatchs.start("Fill Links"); Stopwatchs.start("Fill Links");
try { try {
final Map<String, SortDirection> sorts = new HashMap<String, SortDirection>(); final Map<String, SortDirection> sorts = new HashMap<String, SortDirection>();
sorts.put(Link.LINK_ORDER, SortDirection.ASCENDING); sorts.put(Link.LINK_ORDER, SortDirection.ASCENDING);
final Query query = new Query().addSort(Link.LINK_ORDER, SortDirection.ASCENDING).setPageCount(1); final Query query = new Query().addSort(Link.LINK_ORDER, SortDirection.ASCENDING).setPageCount(1);
final JSONObject linkResult = linkRepository.get(query); final JSONObject linkResult = linkRepository.get(query);
final List<JSONObject> links = org.b3log.latke.util.CollectionUtils.jsonArrayToList(linkResult.getJSONArray(Keys.RESULTS)); final List<JSONObject> links = org.b3log.latke.util.CollectionUtils.jsonArrayToList(linkResult.getJSONArray(Keys.RESULTS));
dataModel.put(Link.LINKS, links); dataModel.put(Link.LINKS, links);
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Fills links failed", e); LOGGER.log(Level.SEVERE, "Fills links failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Fills links failed", e); LOGGER.log(Level.SEVERE, "Fills links failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
Stopwatchs.end(); Stopwatchs.end();
} }
/** /**
* Fills most used tags. * Fills most used tags.
* *
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillMostUsedTags(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException { public void fillMostUsedTags(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException {
Stopwatchs.start("Fill Most Used Tags"); Stopwatchs.start("Fill Most Used Tags");
try { try {
LOGGER.finer("Filling most used tags...."); LOGGER.finer("Filling most used tags....");
final int mostUsedTagDisplayCnt = preference.getInt(Preference.MOST_USED_TAG_DISPLAY_CNT); final int mostUsedTagDisplayCnt = preference.getInt(Preference.MOST_USED_TAG_DISPLAY_CNT);
final List<JSONObject> tags = tagRepository.getMostUsedTags(mostUsedTagDisplayCnt); final List<JSONObject> tags = tagRepository.getMostUsedTags(mostUsedTagDisplayCnt);
tagUtils.removeForUnpublishedArticles(tags); tagUtils.removeForUnpublishedArticles(tags);
dataModel.put(Common.MOST_USED_TAGS, tags); dataModel.put(Common.MOST_USED_TAGS, tags);
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Fills most used tags failed", e); LOGGER.log(Level.SEVERE, "Fills most used tags failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Fills most used tags failed", e); LOGGER.log(Level.SEVERE, "Fills most used tags failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills archive dates. * Fills archive dates.
* *
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillArchiveDates(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException { public void fillArchiveDates(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException {
Stopwatchs.start("Fill Archive Dates"); Stopwatchs.start("Fill Archive Dates");
try { try {
LOGGER.finer("Filling archive dates...."); LOGGER.finer("Filling archive dates....");
final List<JSONObject> archiveDates = archiveDateRepository.getArchiveDates(); final List<JSONObject> archiveDates = archiveDateRepository.getArchiveDates();
final String localeString = preference.getString(Preference.LOCALE_STRING); final String localeString = preference.getString(Preference.LOCALE_STRING);
final String language = Locales.getLanguage(localeString); final String language = Locales.getLanguage(localeString);
for (final JSONObject archiveDate : archiveDates) { for (final JSONObject archiveDate : archiveDates) {
final long time = archiveDate.getLong(ArchiveDate.ARCHIVE_TIME); final long time = archiveDate.getLong(ArchiveDate.ARCHIVE_TIME);
final String dateString = ArchiveDate.DATE_FORMAT.format(time); final String dateString = ArchiveDate.DATE_FORMAT.format(time);
final String[] dateStrings = dateString.split("/"); final String[] dateStrings = dateString.split("/");
final String year = dateStrings[0]; final String year = dateStrings[0];
final String month = dateStrings[1]; final String month = dateStrings[1];
archiveDate.put(ArchiveDate.ARCHIVE_DATE_YEAR, year); archiveDate.put(ArchiveDate.ARCHIVE_DATE_YEAR, year);
archiveDate.put(ArchiveDate.ARCHIVE_DATE_MONTH, month); archiveDate.put(ArchiveDate.ARCHIVE_DATE_MONTH, month);
if ("en".equals(language)) { if ("en".equals(language)) {
final String monthName = Dates.EN_MONTHS.get(month); final String monthName = Dates.EN_MONTHS.get(month);
archiveDate.put(Common.MONTH_NAME, monthName); archiveDate.put(Common.MONTH_NAME, monthName);
} }
} }
dataModel.put(ArchiveDate.ARCHIVE_DATES, archiveDates); dataModel.put(ArchiveDate.ARCHIVE_DATES, archiveDates);
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Fills archive dates failed", e); LOGGER.log(Level.SEVERE, "Fills archive dates failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Fills archive dates failed", e); LOGGER.log(Level.SEVERE, "Fills archive dates failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills most view count articles. * Fills most view count articles.
* *
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillMostViewCountArticles(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException { public void fillMostViewCountArticles(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException {
Stopwatchs.start("Fill Most View Articles"); Stopwatchs.start("Fill Most View Articles");
try { try {
LOGGER.finer("Filling the most view count articles...."); LOGGER.finer("Filling the most view count articles....");
final int mostCommentArticleDisplayCnt = preference.getInt(Preference.MOST_VIEW_ARTICLE_DISPLAY_CNT); final int mostCommentArticleDisplayCnt = preference.getInt(Preference.MOST_VIEW_ARTICLE_DISPLAY_CNT);
final List<JSONObject> mostViewCountArticles = articleRepository.getMostViewCountArticles(mostCommentArticleDisplayCnt); final List<JSONObject> mostViewCountArticles = articleRepository.getMostViewCountArticles(mostCommentArticleDisplayCnt);
dataModel.put(Common.MOST_VIEW_COUNT_ARTICLES, mostViewCountArticles); dataModel.put(Common.MOST_VIEW_COUNT_ARTICLES, mostViewCountArticles);
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Fills most view count articles failed", e); LOGGER.log(Level.SEVERE, "Fills most view count articles failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills most comments articles. * Fills most comments articles.
* *
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillMostCommentArticles(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException { public void fillMostCommentArticles(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException {
Stopwatchs.start("Fill Most CMMTs Articles"); Stopwatchs.start("Fill Most CMMTs Articles");
try { try {
LOGGER.finer("Filling most comment articles...."); LOGGER.finer("Filling most comment articles....");
final int mostCommentArticleDisplayCnt = preference.getInt(Preference.MOST_COMMENT_ARTICLE_DISPLAY_CNT); final int mostCommentArticleDisplayCnt = preference.getInt(Preference.MOST_COMMENT_ARTICLE_DISPLAY_CNT);
final List<JSONObject> mostCommentArticles = articleRepository.getMostCommentArticles(mostCommentArticleDisplayCnt); final List<JSONObject> mostCommentArticles = articleRepository.getMostCommentArticles(mostCommentArticleDisplayCnt);
dataModel.put(Common.MOST_COMMENT_ARTICLES, mostCommentArticles); dataModel.put(Common.MOST_COMMENT_ARTICLES, mostCommentArticles);
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Fills most comment articles failed", e); LOGGER.log(Level.SEVERE, "Fills most comment articles failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills post articles recently. * Fills post articles recently.
* *
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillRecentArticles(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException { public void fillRecentArticles(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException {
Stopwatchs.start("Fill Recent Articles"); Stopwatchs.start("Fill Recent Articles");
try { try {
final int recentArticleDisplayCnt = preference.getInt(Preference.RECENT_ARTICLE_DISPLAY_CNT); final int recentArticleDisplayCnt = preference.getInt(Preference.RECENT_ARTICLE_DISPLAY_CNT);
final List<JSONObject> recentArticles = articleRepository.getRecentArticles(recentArticleDisplayCnt); final List<JSONObject> recentArticles = articleRepository.getRecentArticles(recentArticleDisplayCnt);
dataModel.put(Common.RECENT_ARTICLES, recentArticles); dataModel.put(Common.RECENT_ARTICLES, recentArticles);
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Fills recent articles failed", e); LOGGER.log(Level.SEVERE, "Fills recent articles failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Fills recent articles failed", e); LOGGER.log(Level.SEVERE, "Fills recent articles failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills post comments recently. * Fills post comments recently.
* *
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillRecentComments(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException { public void fillRecentComments(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException {
Stopwatchs.start("Fill Recent Comments"); Stopwatchs.start("Fill Recent Comments");
try { try {
LOGGER.finer("Filling recent comments...."); LOGGER.finer("Filling recent comments....");
final int recentCommentDisplayCnt = preference.getInt(Preference.RECENT_COMMENT_DISPLAY_CNT); final int recentCommentDisplayCnt = preference.getInt(Preference.RECENT_COMMENT_DISPLAY_CNT);
final List<JSONObject> recentComments = commentRepository.getRecentComments(recentCommentDisplayCnt); final List<JSONObject> recentComments = commentRepository.getRecentComments(recentCommentDisplayCnt);
for (final JSONObject comment : recentComments) { for (final JSONObject comment : recentComments) {
final String content = comment.getString(Comment.COMMENT_CONTENT).replaceAll(SoloServletListener.ENTER_ESC, "&nbsp;"); final String content = comment.getString(Comment.COMMENT_CONTENT).replaceAll(SoloServletListener.ENTER_ESC, "&nbsp;");
comment.put(Comment.COMMENT_CONTENT, content); comment.put(Comment.COMMENT_CONTENT, content);
comment.put(Comment.COMMENT_NAME, StringEscapeUtils.escapeHtml(comment.getString(Comment.COMMENT_NAME))); comment.put(Comment.COMMENT_NAME, StringEscapeUtils.escapeHtml(comment.getString(Comment.COMMENT_NAME)));
comment.put(Comment.COMMENT_URL, StringEscapeUtils.escapeHtml(comment.getString(Comment.COMMENT_URL))); comment.put(Comment.COMMENT_URL, StringEscapeUtils.escapeHtml(comment.getString(Comment.COMMENT_URL)));
comment.remove(Comment.COMMENT_EMAIL); // Erases email for security reason comment.remove(Comment.COMMENT_EMAIL); // Erases email for security reason
} }
dataModel.put(Common.RECENT_COMMENTS, recentComments); dataModel.put(Common.RECENT_COMMENTS, recentComments);
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Fills recent comments failed", e); LOGGER.log(Level.SEVERE, "Fills recent comments failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Fills recent comments failed", e); LOGGER.log(Level.SEVERE, "Fills recent comments failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills footer.ftl. * Fills footer.ftl.
* *
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillBlogFooter(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException { public void fillBlogFooter(final Map<String, Object> dataModel, final JSONObject preference) throws ServiceException {
Stopwatchs.start("Fill Footer"); Stopwatchs.start("Fill Footer");
try { try {
LOGGER.finer("Filling footer...."); LOGGER.finer("Filling footer....");
final String blogTitle = preference.getString(Preference.BLOG_TITLE); final String blogTitle = preference.getString(Preference.BLOG_TITLE);
dataModel.put(Preference.BLOG_TITLE, blogTitle); dataModel.put(Preference.BLOG_TITLE, blogTitle);
final String blogHost = preference.getString(Preference.BLOG_HOST); final String blogHost = preference.getString(Preference.BLOG_HOST);
dataModel.put(Preference.BLOG_HOST, blogHost); dataModel.put(Preference.BLOG_HOST, blogHost);
dataModel.put(Common.VERSION, SoloServletListener.VERSION); dataModel.put(Common.VERSION, SoloServletListener.VERSION);
dataModel.put(Common.STATIC_RESOURCE_VERSION, Latkes.getStaticResourceVersion()); dataModel.put(Common.STATIC_RESOURCE_VERSION, Latkes.getStaticResourceVersion());
dataModel.put(Common.YEAR, String.valueOf(Calendar.getInstance().get(Calendar.YEAR))); dataModel.put(Common.YEAR, String.valueOf(Calendar.getInstance().get(Calendar.YEAR)));
dataModel.put(Keys.Server.STATIC_SERVER, Latkes.getStaticServer()); dataModel.put(Keys.Server.STATIC_SERVER, Latkes.getStaticServer());
dataModel.put(Keys.Server.SERVER, Latkes.getServer()); dataModel.put(Keys.Server.SERVER, Latkes.getServer());
// Activates plugins // Activates plugins
try { try {
final ViewLoadEventData data = new ViewLoadEventData(); final ViewLoadEventData data = new ViewLoadEventData();
data.setViewName("footer.ftl"); data.setViewName("footer.ftl");
data.setDataModel(dataModel); data.setDataModel(dataModel);
EventManager.getInstance().fireEventSynchronously(new Event<ViewLoadEventData>(AbstractAction.FREEMARKER_ACTION, data)); EventManager.getInstance().fireEventSynchronously(new Event<ViewLoadEventData>(AbstractAction.FREEMARKER_ACTION, data));
if (Strings.isEmptyOrNull((String) dataModel.get(Plugin.PLUGINS))) { if (Strings.isEmptyOrNull((String) dataModel.get(Plugin.PLUGINS))) {
// There is no plugin for this template, fill ${plugins} with blank. // There is no plugin for this template, fill ${plugins} with blank.
dataModel.put(Plugin.PLUGINS, ""); dataModel.put(Plugin.PLUGINS, "");
} }
} catch (final EventException e) { } catch (final EventException e) {
LOGGER.log(Level.WARNING, "Event[FREEMARKER_ACTION] handle failed, ignores this exception for kernel health", e); LOGGER.log(Level.WARNING, "Event[FREEMARKER_ACTION] handle failed, ignores this exception for kernel health", e);
} }
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Fills blog footer failed", e); LOGGER.log(Level.SEVERE, "Fills blog footer failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills header.ftl. * Fills header.ftl.
* *
* @param request the specified HTTP servlet request * @param request the specified HTTP servlet request
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillBlogHeader(final HttpServletRequest request, final Map<String, Object> dataModel, final JSONObject preference) public void fillBlogHeader(final HttpServletRequest request, final Map<String, Object> dataModel, final JSONObject preference)
throws ServiceException { throws ServiceException {
Stopwatchs.start("Fill Header"); Stopwatchs.start("Fill Header");
try { try {
LOGGER.fine("Filling header...."); LOGGER.fine("Filling header....");
dataModel.put(Preference.ARTICLE_LIST_DISPLAY_COUNT, preference.getInt(Preference.ARTICLE_LIST_DISPLAY_COUNT)); dataModel.put(Preference.ARTICLE_LIST_DISPLAY_COUNT, preference.getInt(Preference.ARTICLE_LIST_DISPLAY_COUNT));
dataModel.put(Preference.ARTICLE_LIST_PAGINATION_WINDOW_SIZE, dataModel.put(Preference.ARTICLE_LIST_PAGINATION_WINDOW_SIZE,
preference.getInt(Preference.ARTICLE_LIST_PAGINATION_WINDOW_SIZE)); preference.getInt(Preference.ARTICLE_LIST_PAGINATION_WINDOW_SIZE));
dataModel.put(Preference.LOCALE_STRING, preference.getString(Preference.LOCALE_STRING)); dataModel.put(Preference.LOCALE_STRING, preference.getString(Preference.LOCALE_STRING));
dataModel.put(Preference.BLOG_TITLE, preference.getString(Preference.BLOG_TITLE)); dataModel.put(Preference.BLOG_TITLE, preference.getString(Preference.BLOG_TITLE));
dataModel.put(Preference.BLOG_SUBTITLE, preference.getString(Preference.BLOG_SUBTITLE)); dataModel.put(Preference.BLOG_SUBTITLE, preference.getString(Preference.BLOG_SUBTITLE));
dataModel.put(Preference.HTML_HEAD, preference.getString(Preference.HTML_HEAD)); dataModel.put(Preference.HTML_HEAD, preference.getString(Preference.HTML_HEAD));
dataModel.put(Preference.META_KEYWORDS, preference.getString(Preference.META_KEYWORDS)); dataModel.put(Preference.META_KEYWORDS, preference.getString(Preference.META_KEYWORDS));
dataModel.put(Preference.META_DESCRIPTION, preference.getString(Preference.META_DESCRIPTION)); dataModel.put(Preference.META_DESCRIPTION, preference.getString(Preference.META_DESCRIPTION));
dataModel.put(Common.YEAR, String.valueOf(Calendar.getInstance().get(Calendar.YEAR))); dataModel.put(Common.YEAR, String.valueOf(Calendar.getInstance().get(Calendar.YEAR)));
final String noticeBoard = preference.getString(Preference.NOTICE_BOARD); final String noticeBoard = preference.getString(Preference.NOTICE_BOARD);
dataModel.put(Preference.NOTICE_BOARD, noticeBoard); dataModel.put(Preference.NOTICE_BOARD, noticeBoard);
final Query query = new Query().setPageCount(1); final Query query = new Query().setPageCount(1);
final JSONObject result = userRepository.get(query); final JSONObject result = userRepository.get(query);
final JSONArray users = result.getJSONArray(Keys.RESULTS); final JSONArray users = result.getJSONArray(Keys.RESULTS);
final List<JSONObject> userList = CollectionUtils.jsonArrayToList(users); final List<JSONObject> userList = CollectionUtils.jsonArrayToList(users);
dataModel.put(User.USERS, userList); dataModel.put(User.USERS, userList);
for (final JSONObject user : userList) { for (final JSONObject user : userList) {
user.remove(User.USER_EMAIL); user.remove(User.USER_EMAIL);
} }
final String skinDirName = (String) request.getAttribute(Keys.TEMAPLTE_DIR_NAME); final String skinDirName = (String) request.getAttribute(Keys.TEMAPLTE_DIR_NAME);
dataModel.put(Skin.SKIN_DIR_NAME, skinDirName); dataModel.put(Skin.SKIN_DIR_NAME, skinDirName);
Keys.fillServer(dataModel); Keys.fillServer(dataModel);
fillMinified(dataModel); fillMinified(dataModel);
fillPageNavigations(dataModel); fillPageNavigations(dataModel);
fillStatistic(dataModel); fillStatistic(dataModel);
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Fills blog header failed", e); LOGGER.log(Level.SEVERE, "Fills blog header failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Fills blog header failed", e); LOGGER.log(Level.SEVERE, "Fills blog header failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills minified directory and file postfix for static JavaScript, CSS. * Fills minified directory and file postfix for static JavaScript, CSS.
* *
* @param dataModel the specified data model * @param dataModel the specified data model
*/ */
public void fillMinified(final Map<String, Object> dataModel) { public void fillMinified(final Map<String, Object> dataModel) {
switch (Latkes.getRuntimeMode()) { switch (Latkes.getRuntimeMode()) {
case DEVELOPMENT: case DEVELOPMENT:
dataModel.put(Common.MINI_POSTFIX, ""); dataModel.put(Common.MINI_POSTFIX, "");
break; break;
case PRODUCTION: case PRODUCTION:
dataModel.put(Common.MINI_POSTFIX, Common.MINI_POSTFIX_VALUE); dataModel.put(Common.MINI_POSTFIX, Common.MINI_POSTFIX_VALUE);
break; break;
default: default:
throw new AssertionError(); throw new AssertionError();
} }
} }
/** /**
* Fills side.ftl. * Fills side.ftl.
* *
* @param request the specified HTTP servlet request * @param request the specified HTTP servlet request
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillSide(final HttpServletRequest request, final Map<String, Object> dataModel, final JSONObject preference) public void fillSide(final HttpServletRequest request, final Map<String, Object> dataModel, final JSONObject preference)
throws ServiceException { throws ServiceException {
Stopwatchs.start("Fill Side"); Stopwatchs.start("Fill Side");
try { try {
LOGGER.fine("Filling side...."); LOGGER.fine("Filling side....");
final Template template = Templates.getTemplate((String) request.getAttribute(Keys.TEMAPLTE_DIR_NAME), "side.ftl"); final Template template = Templates.getTemplate((String) request.getAttribute(Keys.TEMAPLTE_DIR_NAME), "side.ftl");
if (null == template) { if (null == template) {
LOGGER.fine("The skin dose not contain [side.ftl] template"); LOGGER.fine("The skin dose not contain [side.ftl] template");
return; return;
} }
// TODO: fillRecentArticles(dataModel, preference); // TODO: fillRecentArticles(dataModel, preference);
if (Templates.hasExpression(template, "<#list links as link>")) { if (Templates.hasExpression(template, "<#list links as link>")) {
fillLinks(dataModel); fillLinks(dataModel);
} }
if (Templates.hasExpression(template, "<#list recentComments as comment>")) { if (Templates.hasExpression(template, "<#list recentComments as comment>")) {
fillRecentComments(dataModel, preference); fillRecentComments(dataModel, preference);
} }
if (Templates.hasExpression(template, "<#list mostUsedTags as tag>")) { if (Templates.hasExpression(template, "<#list mostUsedTags as tag>")) {
fillMostUsedTags(dataModel, preference); fillMostUsedTags(dataModel, preference);
} }
if (Templates.hasExpression(template, "<#list mostCommentArticles as article>")) { if (Templates.hasExpression(template, "<#list mostCommentArticles as article>")) {
fillMostCommentArticles(dataModel, preference); fillMostCommentArticles(dataModel, preference);
} }
if (Templates.hasExpression(template, "<#list mostViewCountArticles as article>")) { if (Templates.hasExpression(template, "<#list mostViewCountArticles as article>")) {
fillMostViewCountArticles(dataModel, preference); fillMostViewCountArticles(dataModel, preference);
} }
if (Templates.hasExpression(template, "<#list archiveDates as archiveDate>")) { if (Templates.hasExpression(template, "<#list archiveDates as archiveDate>")) {
fillArchiveDates(dataModel, preference); fillArchiveDates(dataModel, preference);
} }
} catch (final ServiceException e) { } catch (final ServiceException e) {
LOGGER.log(Level.SEVERE, "Fills side failed", e); LOGGER.log(Level.SEVERE, "Fills side failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills the specified template. * Fills the specified template.
* *
* @param template the specified template * @param template the specified template
* @param dataModel data model * @param dataModel data model
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public void fillUserTemplate(final Template template, final Map<String, Object> dataModel, final JSONObject preference) public void fillUserTemplate(final Template template, final Map<String, Object> dataModel, final JSONObject preference)
throws ServiceException { throws ServiceException {
Stopwatchs.start("Fill User Template[name=" + template.getName() + "]"); Stopwatchs.start("Fill User Template[name=" + template.getName() + "]");
try { try {
LOGGER.log(Level.FINE, "Filling user template[name{0}]", template.getName()); LOGGER.log(Level.FINE, "Filling user template[name{0}]", template.getName());
if (Templates.hasExpression(template, "<#list links as link>")) { if (Templates.hasExpression(template, "<#list links as link>")) {
fillLinks(dataModel); fillLinks(dataModel);
} }
if (Templates.hasExpression(template, "<#list recentComments as comment>")) { if (Templates.hasExpression(template, "<#list recentComments as comment>")) {
fillRecentComments(dataModel, preference); fillRecentComments(dataModel, preference);
} }
if (Templates.hasExpression(template, "<#list mostUsedTags as tag>")) { if (Templates.hasExpression(template, "<#list mostUsedTags as tag>")) {
fillMostUsedTags(dataModel, preference); fillMostUsedTags(dataModel, preference);
} }
if (Templates.hasExpression(template, "<#list mostCommentArticles as article>")) { if (Templates.hasExpression(template, "<#list mostCommentArticles as article>")) {
fillMostCommentArticles(dataModel, preference); fillMostCommentArticles(dataModel, preference);
} }
if (Templates.hasExpression(template, "<#list mostViewCountArticles as article>")) { if (Templates.hasExpression(template, "<#list mostViewCountArticles as article>")) {
fillMostViewCountArticles(dataModel, preference); fillMostViewCountArticles(dataModel, preference);
} }
if (Templates.hasExpression(template, "<#list archiveDates as archiveDate>")) { if (Templates.hasExpression(template, "<#list archiveDates as archiveDate>")) {
fillArchiveDates(dataModel, preference); fillArchiveDates(dataModel, preference);
} }
final String noticeBoard = preference.getString(Preference.NOTICE_BOARD); final String noticeBoard = preference.getString(Preference.NOTICE_BOARD);
dataModel.put(Preference.NOTICE_BOARD, noticeBoard); dataModel.put(Preference.NOTICE_BOARD, noticeBoard);
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Fills user template failed", e); LOGGER.log(Level.SEVERE, "Fills user template failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills page navigations. * Fills page navigations.
* *
* @param dataModel data model * @param dataModel data model
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
private void fillPageNavigations(final Map<String, Object> dataModel) throws ServiceException { private void fillPageNavigations(final Map<String, Object> dataModel) throws ServiceException {
Stopwatchs.start("Fill Navigations"); Stopwatchs.start("Fill Navigations");
try { try {
LOGGER.finer("Filling page navigations...."); LOGGER.finer("Filling page navigations....");
final List<JSONObject> pages = pageRepository.getPages(); final List<JSONObject> pages = pageRepository.getPages();
for (final JSONObject page : pages) { for (final JSONObject page : pages) {
if ("page".equals(page.optString(Page.PAGE_TYPE))) { if ("page".equals(page.optString(Page.PAGE_TYPE))) {
final String permalink = page.optString(Page.PAGE_PERMALINK); final String permalink = page.optString(Page.PAGE_PERMALINK);
page.put(Page.PAGE_PERMALINK, Latkes.getServePath() + permalink); page.put(Page.PAGE_PERMALINK, Latkes.getServePath() + permalink);
} }
} }
dataModel.put(Common.PAGE_NAVIGATIONS, pages); dataModel.put(Common.PAGE_NAVIGATIONS, pages);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Fills page navigations failed", e); LOGGER.log(Level.SEVERE, "Fills page navigations failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Fills statistic. * Fills statistic.
* *
* @param dataModel data model * @param dataModel data model
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
private void fillStatistic(final Map<String, Object> dataModel) throws ServiceException { private void fillStatistic(final Map<String, Object> dataModel) throws ServiceException {
Stopwatchs.start("Fill Statistic"); Stopwatchs.start("Fill Statistic");
try { try {
LOGGER.finer("Filling statistic...."); LOGGER.finer("Filling statistic....");
final JSONObject statistic = statisticRepository.get(Statistic.STATISTIC); final JSONObject statistic = statisticRepository.get(Statistic.STATISTIC);
dataModel.put(Statistic.STATISTIC, statistic); dataModel.put(Statistic.STATISTIC, statistic);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Fills statistic failed", e); LOGGER.log(Level.SEVERE, "Fills statistic failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} finally { } finally {
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Sets some extra properties into the specified article with the specified author and preference, performs content and * Sets some extra properties into the specified article with the specified author and preference, performs content and
* abstract editor processing. * abstract editor processing.
* *
* <p> * <p>
* Article ext properties: * Article ext properties:
* <pre> * <pre>
* { * {
* ...., * ....,
* "authorName": "", * "authorName": "",
* "authorId": "", * "authorId": "",
* "hasUpdated": boolean * "hasUpdated": boolean
* } * }
* </pre> * </pre>
* </p> * </p>
* *
* @param article the specified article * @param article the specified article
* @param author the specified author * @param author the specified author
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
* @see #setArticlesExProperties(java.util.List, org.json.JSONObject) * @see #setArticlesExProperties(java.util.List, org.json.JSONObject)
*/ */
private void setArticleExProperties(final JSONObject article, final JSONObject author, final JSONObject preference) private void setArticleExProperties(final JSONObject article, final JSONObject author, final JSONObject preference)
throws ServiceException { throws ServiceException {
try { try {
final String authorName = author.getString(User.USER_NAME); final String authorName = author.getString(User.USER_NAME);
article.put(Common.AUTHOR_NAME, authorName); article.put(Common.AUTHOR_NAME, authorName);
final String authorId = author.getString(Keys.OBJECT_ID); final String authorId = author.getString(Keys.OBJECT_ID);
article.put(Common.AUTHOR_ID, authorId); article.put(Common.AUTHOR_ID, authorId);
if (preference.getBoolean(Preference.ENABLE_ARTICLE_UPDATE_HINT)) { if (preference.getBoolean(Preference.ENABLE_ARTICLE_UPDATE_HINT)) {
article.put(Common.HAS_UPDATED, articleUtils.hasUpdated(article)); article.put(Common.HAS_UPDATED, articleUtils.hasUpdated(article));
} else { } else {
article.put(Common.HAS_UPDATED, false); article.put(Common.HAS_UPDATED, false);
} }
processArticleAbstract(preference, article); processArticleAbstract(preference, article);
articleQueryService.markdown(article); articleQueryService.markdown(article);
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Sets article extra properties failed", e); LOGGER.log(Level.SEVERE, "Sets article extra properties failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Sets some extra properties into the specified article with the specified preference, performs content and * Sets some extra properties into the specified article with the specified preference, performs content and
* abstract editor processing. * abstract editor processing.
* *
* <p> * <p>
* Article ext properties: * Article ext properties:
* <pre> * <pre>
* { * {
* ...., * ....,
* "authorName": "", * "authorName": "",
* "authorId": "", * "authorId": "",
* "hasUpdated": boolean * "hasUpdated": boolean
* } * }
* </pre> * </pre>
* </p> * </p>
* *
* @param article the specified article * @param article the specified article
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
* @see #setArticlesExProperties(java.util.List, org.json.JSONObject) * @see #setArticlesExProperties(java.util.List, org.json.JSONObject)
*/ */
private void setArticleExProperties(final JSONObject article, final JSONObject preference) throws ServiceException { private void setArticleExProperties(final JSONObject article, final JSONObject preference) throws ServiceException {
try { try {
final JSONObject author = articleUtils.getAuthor(article); final JSONObject author = articleUtils.getAuthor(article);
final String authorName = author.getString(User.USER_NAME); final String authorName = author.getString(User.USER_NAME);
article.put(Common.AUTHOR_NAME, authorName); article.put(Common.AUTHOR_NAME, authorName);
final String authorId = author.getString(Keys.OBJECT_ID); final String authorId = author.getString(Keys.OBJECT_ID);
article.put(Common.AUTHOR_ID, authorId); article.put(Common.AUTHOR_ID, authorId);
if (preference.getBoolean(Preference.ENABLE_ARTICLE_UPDATE_HINT)) { if (preference.getBoolean(Preference.ENABLE_ARTICLE_UPDATE_HINT)) {
article.put(Common.HAS_UPDATED, articleUtils.hasUpdated(article)); article.put(Common.HAS_UPDATED, articleUtils.hasUpdated(article));
} else { } else {
article.put(Common.HAS_UPDATED, false); article.put(Common.HAS_UPDATED, false);
} }
processArticleAbstract(preference, article); processArticleAbstract(preference, article);
articleQueryService.markdown(article); articleQueryService.markdown(article);
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Sets article extra properties failed", e); LOGGER.log(Level.SEVERE, "Sets article extra properties failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Sets some extra properties into the specified article with the specified * Sets some extra properties into the specified article with the specified
* author and preference. * author and preference.
* *
* <p> * <p>
* The batch version of method * The batch version of method
* {@linkplain #setArticleExProperties(org.json.JSONObject, org.json.JSONObject)}. * {@linkplain #setArticleExProperties(org.json.JSONObject, org.json.JSONObject)}.
* </p> * </p>
* *
* <p> * <p>
* Article ext properties: * Article ext properties:
* <pre> * <pre>
* { * {
* ...., * ....,
* "authorName": "", * "authorName": "",
* "authorId": "", * "authorId": "",
* "hasUpdated": boolean * "hasUpdated": boolean
* } * }
* </pre> * </pre>
* </p> * </p>
* *
* @param articles the specified articles * @param articles the specified articles
* @param author the specified author * @param author the specified author
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
* @see #setArticleExProperties(org.json.JSONObject, org.json.JSONObject) * @see #setArticleExProperties(org.json.JSONObject, org.json.JSONObject)
*/ */
public void setArticlesExProperties(final List<JSONObject> articles, final JSONObject author, final JSONObject preference) public void setArticlesExProperties(final List<JSONObject> articles, final JSONObject author, final JSONObject preference)
throws ServiceException { throws ServiceException {
for (final JSONObject article : articles) { for (final JSONObject article : articles) {
setArticleExProperties(article, author, preference); setArticleExProperties(article, author, preference);
} }
} }
/** /**
* Sets some extra properties into the specified article with the specified * Sets some extra properties into the specified article with the specified
* preference. * preference.
* *
* <p> * <p>
* The batch version of method * The batch version of method
* {@linkplain #setArticleExProperties(org.json.JSONObject, org.json.JSONObject)}. * {@linkplain #setArticleExProperties(org.json.JSONObject, org.json.JSONObject)}.
* </p> * </p>
* *
* <p> * <p>
* Article ext properties: * Article ext properties:
* <pre> * <pre>
* { * {
* ...., * ....,
* "authorName": "", * "authorName": "",
* "authorId": "", * "authorId": "",
* "hasUpdated": boolean * "hasUpdated": boolean
* } * }
* </pre> * </pre>
* </p> * </p>
* *
* @param articles the specified articles * @param articles the specified articles
* @param preference the specified preference * @param preference the specified preference
* @throws ServiceException service exception * @throws ServiceException service exception
* @see #setArticleExProperties(org.json.JSONObject, org.json.JSONObject) * @see #setArticleExProperties(org.json.JSONObject, org.json.JSONObject)
*/ */
public void setArticlesExProperties(final List<JSONObject> articles, final JSONObject preference) public void setArticlesExProperties(final List<JSONObject> articles, final JSONObject preference)
throws ServiceException { throws ServiceException {
for (final JSONObject article : articles) { for (final JSONObject article : articles) {
setArticleExProperties(article, preference); setArticleExProperties(article, preference);
} }
} }
/** /**
* Processes the abstract of the specified article with the specified preference. * Processes the abstract of the specified article with the specified preference.
* *
* <p> * <p>
* <ul> * <ul>
* <li>If the abstract is {@code null}, sets it with ""</li> * <li>If the abstract is {@code null}, sets it with ""</li>
* <li>If user configured preference "titleOnly", sets the abstract with ""</li> * <li>If user configured preference "titleOnly", sets the abstract with ""</li>
* <li>If user configured preference "titleAndContent", sets the abstract with the content of the article</li> * <li>If user configured preference "titleAndContent", sets the abstract with the content of the article</li>
* </ul> * </ul>
* </p> * </p>
* *
* @param preference the specified preference * @param preference the specified preference
* @param article the specified article * @param article the specified article
*/ */
private void processArticleAbstract(final JSONObject preference, final JSONObject article) { private void processArticleAbstract(final JSONObject preference, final JSONObject article) {
final String articleAbstract = article.optString(Article.ARTICLE_ABSTRACT, null); final String articleAbstract = article.optString(Article.ARTICLE_ABSTRACT, null);
if (null == articleAbstract) { if (null == articleAbstract) {
article.put(Article.ARTICLE_ABSTRACT, ""); article.put(Article.ARTICLE_ABSTRACT, "");
} }
final String articleListStyle = preference.optString(Preference.ARTICLE_LIST_STYLE); final String articleListStyle = preference.optString(Preference.ARTICLE_LIST_STYLE);
if ("titleOnly".equals(articleListStyle)) { if ("titleOnly".equals(articleListStyle)) {
article.put(Article.ARTICLE_ABSTRACT, ""); article.put(Article.ARTICLE_ABSTRACT, "");
} else if ("titleAndContent".equals(articleListStyle)) { } else if ("titleAndContent".equals(articleListStyle)) {
article.put(Article.ARTICLE_ABSTRACT, article.optString(Article.ARTICLE_CONTENT)); article.put(Article.ARTICLE_ABSTRACT, article.optString(Article.ARTICLE_CONTENT));
} }
} }
/** /**
* Gets the {@link Filler} singleton. * Gets the {@link Filler} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static Filler getInstance() { public static Filler getInstance() {
return SingletonHolder.SINGLETON; return SingletonHolder.SINGLETON;
} }
/** /**
* Private default constructor. * Private default constructor.
*/ */
private Filler() { private Filler() {
} }
/** /**
* Singleton holder. * Singleton holder.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.0, Jan 12, 2011 * @version 1.0.0.0, Jan 12, 2011
*/ */
private static final class SingletonHolder { private static final class SingletonHolder {
/** /**
* Singleton. * Singleton.
*/ */
private static final Filler SINGLETON = new Filler(); private static final Filler SINGLETON = new Filler();
/** /**
* Private default constructor. * Private default constructor.
*/ */
private SingletonHolder() { private SingletonHolder() {
} }
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.repository.AbstractRepository; import org.b3log.latke.repository.AbstractRepository;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.solo.model.Article; import org.b3log.solo.model.Article;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.solo.model.ArchiveDate; import org.b3log.solo.model.ArchiveDate;
import org.b3log.solo.repository.ArchiveDateArticleRepository; import org.b3log.solo.repository.ArchiveDateArticleRepository;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Archive date-Article relation repository. * Archive date-Article relation repository.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.6, Nov 9, 2011 * @version 1.0.0.6, Nov 9, 2011
* @since 0.3.1 * @since 0.3.1
*/ */
public final class ArchiveDateArticleRepositoryImpl extends AbstractRepository implements ArchiveDateArticleRepository { public final class ArchiveDateArticleRepositoryImpl extends AbstractRepository implements ArchiveDateArticleRepository {
/** /**
* Singleton. * Singleton.
*/ */
private static final ArchiveDateArticleRepositoryImpl SINGLETON = private static final ArchiveDateArticleRepositoryImpl SINGLETON =
new ArchiveDateArticleRepositoryImpl(ArchiveDate.ARCHIVE_DATE + "_" + Article.ARTICLE); new ArchiveDateArticleRepositoryImpl(ArchiveDate.ARCHIVE_DATE + "_" + Article.ARTICLE);
@Override @Override
public JSONObject getByArchiveDateId(final String archiveDateId, final int currentPageNum, final int pageSize) public JSONObject getByArchiveDateId(final String archiveDateId, final int currentPageNum, final int pageSize)
throws RepositoryException { throws RepositoryException {
final Query query = new Query().addFilter(ArchiveDate.ARCHIVE_DATE + "_" + Keys.OBJECT_ID, final Query query = new Query().addFilter(ArchiveDate.ARCHIVE_DATE + "_" + Keys.OBJECT_ID,
FilterOperator.EQUAL, archiveDateId). FilterOperator.EQUAL, archiveDateId).
addSort(Article.ARTICLE + "_" + Keys.OBJECT_ID, addSort(Article.ARTICLE + "_" + Keys.OBJECT_ID,
SortDirection.DESCENDING). SortDirection.DESCENDING).
setCurrentPageNum(currentPageNum). setCurrentPageNum(currentPageNum).
setPageSize(pageSize). setPageSize(pageSize).
setPageCount(1); setPageCount(1);
return get(query); return get(query);
} }
@Override @Override
public JSONObject getByArticleId(final String articleId) throws RepositoryException { public JSONObject getByArticleId(final String articleId) throws RepositoryException {
final Query query = new Query(); final Query query = new Query();
query.addFilter(Article.ARTICLE + "_" + Keys.OBJECT_ID, FilterOperator.EQUAL, articleId); query.addFilter(Article.ARTICLE + "_" + Keys.OBJECT_ID, FilterOperator.EQUAL, articleId);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
/** /**
* Gets the {@link ArchiveDateArticleRepositoryImpl} singleton. * Gets the {@link ArchiveDateArticleRepositoryImpl} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static ArchiveDateArticleRepositoryImpl getInstance() { public static ArchiveDateArticleRepositoryImpl getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
* *
* @param name the specified name * @param name the specified name
*/ */
private ArchiveDateArticleRepositoryImpl(final String name) { private ArchiveDateArticleRepositoryImpl(final String name) {
super(name); super(name);
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import java.text.ParseException; import java.text.ParseException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.repository.AbstractRepository; import org.b3log.latke.repository.AbstractRepository;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.util.CollectionUtils; import org.b3log.latke.util.CollectionUtils;
import org.b3log.solo.model.ArchiveDate; import org.b3log.solo.model.ArchiveDate;
import org.b3log.solo.repository.ArchiveDateRepository; import org.b3log.solo.repository.ArchiveDateRepository;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Archive date repository. * Archive date repository.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.6, Dec 31, 2011 * @version 1.0.0.6, Dec 31, 2011
* @since 0.3.1 * @since 0.3.1
*/ */
public final class ArchiveDateRepositoryImpl extends AbstractRepository implements ArchiveDateRepository { public final class ArchiveDateRepositoryImpl extends AbstractRepository implements ArchiveDateRepository {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(ArchiveDateRepositoryImpl.class.getName()); private static final Logger LOGGER = Logger.getLogger(ArchiveDateRepositoryImpl.class.getName());
/** /**
* Singleton. * Singleton.
*/ */
private static final ArchiveDateRepositoryImpl SINGLETON = new ArchiveDateRepositoryImpl(ArchiveDate.ARCHIVE_DATE); private static final ArchiveDateRepositoryImpl SINGLETON = new ArchiveDateRepositoryImpl(ArchiveDate.ARCHIVE_DATE);
@Override @Override
public JSONObject getByArchiveDate(final String archiveDate) throws RepositoryException { public JSONObject getByArchiveDate(final String archiveDate) throws RepositoryException {
long time = 0L; long time = 0L;
try { try {
time = ArchiveDate.DATE_FORMAT.parse(archiveDate).getTime(); time = ArchiveDate.DATE_FORMAT.parse(archiveDate).getTime();
} catch (final ParseException e) { } catch (final ParseException e) {
LOGGER.log(Level.SEVERE, "Can not parse archive date [" + archiveDate + "]", e); LOGGER.log(Level.SEVERE, "Can not parse archive date [" + archiveDate + "]", e);
throw new RepositoryException("Can not parse archive date [" + archiveDate + "]"); throw new RepositoryException("Can not parse archive date [" + archiveDate + "]");
} }
final Query query = new Query(); final Query query = new Query();
query.addFilter(ArchiveDate.ARCHIVE_TIME, FilterOperator.EQUAL, time).setPageCount(1); query.addFilter(ArchiveDate.ARCHIVE_TIME, FilterOperator.EQUAL, time).setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public List<JSONObject> getArchiveDates() throws RepositoryException { public List<JSONObject> getArchiveDates() throws RepositoryException {
final org.b3log.latke.repository.Query query = final org.b3log.latke.repository.Query query =
new Query().addSort(ArchiveDate.ARCHIVE_TIME, SortDirection.DESCENDING).setPageCount(1); new Query().addSort(ArchiveDate.ARCHIVE_TIME, SortDirection.DESCENDING).setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray archiveDates = result.optJSONArray(Keys.RESULTS); final JSONArray archiveDates = result.optJSONArray(Keys.RESULTS);
final List<JSONObject> ret = CollectionUtils.jsonArrayToList(archiveDates); final List<JSONObject> ret = CollectionUtils.jsonArrayToList(archiveDates);
removeForUnpublishedArticles(ret); removeForUnpublishedArticles(ret);
return ret; return ret;
} }
/** /**
* Removes archive dates of unpublished articles from the specified archive * Removes archive dates of unpublished articles from the specified archive
* dates. * dates.
* *
* @param archiveDates the specified archive dates * @param archiveDates the specified archive dates
* @throws RepositoryException repository exception * @throws RepositoryException repository exception
*/ */
private void removeForUnpublishedArticles(final List<JSONObject> archiveDates) throws RepositoryException { private void removeForUnpublishedArticles(final List<JSONObject> archiveDates) throws RepositoryException {
final Iterator<JSONObject> iterator = archiveDates.iterator(); final Iterator<JSONObject> iterator = archiveDates.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final JSONObject archiveDate = iterator.next(); final JSONObject archiveDate = iterator.next();
if (0 == archiveDate.optInt(ArchiveDate.ARCHIVE_DATE_PUBLISHED_ARTICLE_COUNT)) { if (0 == archiveDate.optInt(ArchiveDate.ARCHIVE_DATE_PUBLISHED_ARTICLE_COUNT)) {
iterator.remove(); iterator.remove();
} }
} }
} }
/** /**
* Gets the {@link ArchiveDateRepositoryImpl} singleton. * Gets the {@link ArchiveDateRepositoryImpl} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static ArchiveDateRepositoryImpl getInstance() { public static ArchiveDateRepositoryImpl getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
* *
* @param name the specified name * @param name the specified name
*/ */
private ArchiveDateRepositoryImpl(final String name) { private ArchiveDateRepositoryImpl(final String name) {
super(name); super(name);
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.b3log.solo.model.Article; import org.b3log.solo.model.Article;
import org.b3log.solo.repository.ArticleRepository; import org.b3log.solo.repository.ArticleRepository;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.repository.*; import org.b3log.latke.repository.*;
import org.b3log.latke.util.CollectionUtils; import org.b3log.latke.util.CollectionUtils;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Article repository. * Article repository.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.3.9, May 8, 2012 * @version 1.0.3.9, May 8, 2012
* @since 0.3.1 * @since 0.3.1
*/ */
public final class ArticleRepositoryImpl extends AbstractRepository implements ArticleRepository { public final class ArticleRepositoryImpl extends AbstractRepository implements ArticleRepository {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(ArticleRepositoryImpl.class.getName()); private static final Logger LOGGER = Logger.getLogger(ArticleRepositoryImpl.class.getName());
/** /**
* Singleton. * Singleton.
*/ */
private static final ArticleRepositoryImpl SINGLETON = new ArticleRepositoryImpl(Article.ARTICLE); private static final ArticleRepositoryImpl SINGLETON = new ArticleRepositoryImpl(Article.ARTICLE);
/** /**
* Random range. * Random range.
*/ */
private static final double RANDOM_RANGE = 0.1D; private static final double RANDOM_RANGE = 0.1D;
@Override @Override
public JSONObject getByAuthorEmail(final String authorEmail, final int currentPageNum, final int pageSize) public JSONObject getByAuthorEmail(final String authorEmail, final int currentPageNum, final int pageSize)
throws RepositoryException { throws RepositoryException {
final Query query = new Query().addFilter(Article.ARTICLE_AUTHOR_EMAIL, FilterOperator.EQUAL, authorEmail). final Query query = new Query().addFilter(Article.ARTICLE_AUTHOR_EMAIL, FilterOperator.EQUAL, authorEmail).
addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true). addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true).
addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING). addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING).
setCurrentPageNum(currentPageNum).setPageSize(pageSize).setPageCount(1); setCurrentPageNum(currentPageNum).setPageSize(pageSize).setPageCount(1);
return get(query); return get(query);
} }
@Override @Override
public JSONObject getByPermalink(final String permalink) throws RepositoryException { public JSONObject getByPermalink(final String permalink) throws RepositoryException {
final Query query = new Query().addFilter(Article.ARTICLE_PERMALINK, FilterOperator.EQUAL, permalink). final Query query = new Query().addFilter(Article.ARTICLE_PERMALINK, FilterOperator.EQUAL, permalink).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public List<JSONObject> getRecentArticles(final int fetchSize) throws RepositoryException { public List<JSONObject> getRecentArticles(final int fetchSize) throws RepositoryException {
final Query query = new Query(); final Query query = new Query();
query.addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true); query.addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true);
query.addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING); query.addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING);
query.setCurrentPageNum(1); query.setCurrentPageNum(1);
query.setPageSize(fetchSize); query.setPageSize(fetchSize);
query.setPageCount(1); query.setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
return CollectionUtils.jsonArrayToList(array); return CollectionUtils.jsonArrayToList(array);
} }
@Override @Override
public List<JSONObject> getMostCommentArticles(final int num) throws RepositoryException { public List<JSONObject> getMostCommentArticles(final int num) throws RepositoryException {
final Query query = new Query().addSort(Article.ARTICLE_COMMENT_COUNT, SortDirection.DESCENDING). final Query query = new Query().addSort(Article.ARTICLE_COMMENT_COUNT, SortDirection.DESCENDING).
addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING). addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING).
addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true). addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true).
setCurrentPageNum(1).setPageSize(num).setPageCount(1); setCurrentPageNum(1).setPageSize(num).setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
return CollectionUtils.jsonArrayToList(array); return CollectionUtils.jsonArrayToList(array);
} }
@Override @Override
public List<JSONObject> getMostViewCountArticles(final int num) throws RepositoryException { public List<JSONObject> getMostViewCountArticles(final int num) throws RepositoryException {
final Query query = new Query(); final Query query = new Query();
query.addSort(Article.ARTICLE_VIEW_COUNT, SortDirection.DESCENDING). query.addSort(Article.ARTICLE_VIEW_COUNT, SortDirection.DESCENDING).
addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING); addSort(Article.ARTICLE_UPDATE_DATE, SortDirection.DESCENDING);
query.addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true); query.addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true);
query.setCurrentPageNum(1); query.setCurrentPageNum(1);
query.setPageSize(num); query.setPageSize(num);
query.setPageCount(1); query.setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
return CollectionUtils.jsonArrayToList(array); return CollectionUtils.jsonArrayToList(array);
} }
@Override @Override
public JSONObject getPreviousArticle(final String articleId) throws RepositoryException { public JSONObject getPreviousArticle(final String articleId) throws RepositoryException {
final JSONObject currentArticle = get(articleId); final JSONObject currentArticle = get(articleId);
final Date currentArticleCreateDate = (Date) currentArticle.opt(Article.ARTICLE_CREATE_DATE); final Date currentArticleCreateDate = (Date) currentArticle.opt(Article.ARTICLE_CREATE_DATE);
final Query query = new Query().addFilter(Article.ARTICLE_CREATE_DATE, FilterOperator.LESS_THAN, currentArticleCreateDate). final Query query = new Query().addFilter(Article.ARTICLE_CREATE_DATE, FilterOperator.LESS_THAN, currentArticleCreateDate).
addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true). addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true).
addSort(Article.ARTICLE_CREATE_DATE, SortDirection.DESCENDING).setCurrentPageNum(1). addSort(Article.ARTICLE_CREATE_DATE, SortDirection.DESCENDING).setCurrentPageNum(1).
setPageSize(1).setPageCount(1). setPageSize(1).setPageCount(1).
addProjection(Article.ARTICLE_TITLE, String.class). addProjection(Article.ARTICLE_TITLE, String.class).
addProjection(Article.ARTICLE_PERMALINK, String.class); addProjection(Article.ARTICLE_PERMALINK, String.class);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (1 != array.length()) { if (1 != array.length()) {
return null; return null;
} }
final JSONObject ret = new JSONObject(); final JSONObject ret = new JSONObject();
final JSONObject article = array.optJSONObject(0); final JSONObject article = array.optJSONObject(0);
try { try {
ret.put(Article.ARTICLE_TITLE, article.getString(Article.ARTICLE_TITLE)); ret.put(Article.ARTICLE_TITLE, article.getString(Article.ARTICLE_TITLE));
ret.put(Article.ARTICLE_PERMALINK, article.getString(Article.ARTICLE_PERMALINK)); ret.put(Article.ARTICLE_PERMALINK, article.getString(Article.ARTICLE_PERMALINK));
} catch (final JSONException e) { } catch (final JSONException e) {
throw new RepositoryException(e); throw new RepositoryException(e);
} }
return ret; return ret;
} }
@Override @Override
public JSONObject getNextArticle(final String articleId) throws RepositoryException { public JSONObject getNextArticle(final String articleId) throws RepositoryException {
final JSONObject currentArticle = get(articleId); final JSONObject currentArticle = get(articleId);
final Date currentArticleCreateDate = (Date) currentArticle.opt(Article.ARTICLE_CREATE_DATE); final Date currentArticleCreateDate = (Date) currentArticle.opt(Article.ARTICLE_CREATE_DATE);
final Query query = new Query().addFilter(Article.ARTICLE_CREATE_DATE, FilterOperator.GREATER_THAN, currentArticleCreateDate). final Query query = new Query().addFilter(Article.ARTICLE_CREATE_DATE, FilterOperator.GREATER_THAN, currentArticleCreateDate).
addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true). addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true).
addSort(Article.ARTICLE_CREATE_DATE, SortDirection.ASCENDING).setCurrentPageNum(1). addSort(Article.ARTICLE_CREATE_DATE, SortDirection.ASCENDING).setCurrentPageNum(1).
setPageSize(1).setPageCount(1). setPageSize(1).setPageCount(1).
addProjection(Article.ARTICLE_TITLE, String.class). addProjection(Article.ARTICLE_TITLE, String.class).
addProjection(Article.ARTICLE_PERMALINK, String.class); addProjection(Article.ARTICLE_PERMALINK, String.class);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (1 != array.length()) { if (1 != array.length()) {
return null; return null;
} }
final JSONObject ret = new JSONObject(); final JSONObject ret = new JSONObject();
final JSONObject article = array.optJSONObject(0); final JSONObject article = array.optJSONObject(0);
try { try {
ret.put(Article.ARTICLE_TITLE, article.getString(Article.ARTICLE_TITLE)); ret.put(Article.ARTICLE_TITLE, article.getString(Article.ARTICLE_TITLE));
ret.put(Article.ARTICLE_PERMALINK, article.getString(Article.ARTICLE_PERMALINK)); ret.put(Article.ARTICLE_PERMALINK, article.getString(Article.ARTICLE_PERMALINK));
} catch (final JSONException e) { } catch (final JSONException e) {
throw new RepositoryException(e); throw new RepositoryException(e);
} }
return ret; return ret;
} }
@Override @Override
public boolean isPublished(final String articleId) throws RepositoryException { public boolean isPublished(final String articleId) throws RepositoryException {
final JSONObject article = get(articleId); final JSONObject article = get(articleId);
if (null == article) { if (null == article) {
return false; return false;
} }
return article.optBoolean(Article.ARTICLE_IS_PUBLISHED); return article.optBoolean(Article.ARTICLE_IS_PUBLISHED);
} }
@Override @Override
public List<JSONObject> getRandomly(final int fetchSize) throws RepositoryException { public List<JSONObject> getRandomly(final int fetchSize) throws RepositoryException {
final List<JSONObject> ret = new ArrayList<JSONObject>(); final List<JSONObject> ret = new ArrayList<JSONObject>();
if (0 == count()) { if (0 == count()) {
return ret; return ret;
} }
final double mid = Math.random() + RANDOM_RANGE; final double mid = Math.random() + RANDOM_RANGE;
LOGGER.log(Level.FINEST, "Random mid[{0}]", mid); LOGGER.log(Level.FINEST, "Random mid[{0}]", mid);
Query query = new Query(); Query query = new Query();
query.addFilter(Article.ARTICLE_RANDOM_DOUBLE, FilterOperator.GREATER_THAN_OR_EQUAL, mid); query.addFilter(Article.ARTICLE_RANDOM_DOUBLE, FilterOperator.GREATER_THAN_OR_EQUAL, mid);
query.addFilter(Article.ARTICLE_RANDOM_DOUBLE, FilterOperator.LESS_THAN_OR_EQUAL, mid); query.addFilter(Article.ARTICLE_RANDOM_DOUBLE, FilterOperator.LESS_THAN_OR_EQUAL, mid);
query.addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true); query.addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true);
query.setCurrentPageNum(1); query.setCurrentPageNum(1);
query.setPageSize(fetchSize); query.setPageSize(fetchSize);
query.setPageCount(1); query.setPageCount(1);
final JSONObject result1 = get(query); final JSONObject result1 = get(query);
final JSONArray array1 = result1.optJSONArray(Keys.RESULTS); final JSONArray array1 = result1.optJSONArray(Keys.RESULTS);
final List<JSONObject> list1 = CollectionUtils.<JSONObject>jsonArrayToList(array1); final List<JSONObject> list1 = CollectionUtils.<JSONObject>jsonArrayToList(array1);
ret.addAll(list1); ret.addAll(list1);
final int reminingSize = fetchSize - array1.length(); final int reminingSize = fetchSize - array1.length();
if (0 != reminingSize) { // Query for remains if (0 != reminingSize) { // Query for remains
query = new Query(); query = new Query();
query.addFilter(Article.ARTICLE_RANDOM_DOUBLE, FilterOperator.GREATER_THAN_OR_EQUAL, 0D); query.addFilter(Article.ARTICLE_RANDOM_DOUBLE, FilterOperator.GREATER_THAN_OR_EQUAL, 0D);
query.addFilter(Article.ARTICLE_RANDOM_DOUBLE, FilterOperator.LESS_THAN_OR_EQUAL, mid); query.addFilter(Article.ARTICLE_RANDOM_DOUBLE, FilterOperator.LESS_THAN_OR_EQUAL, mid);
query.addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true); query.addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true);
query.setCurrentPageNum(1); query.setCurrentPageNum(1);
query.setPageSize(reminingSize); query.setPageSize(reminingSize);
query.setPageCount(1); query.setPageCount(1);
final JSONObject result2 = get(query); final JSONObject result2 = get(query);
final JSONArray array2 = result2.optJSONArray(Keys.RESULTS); final JSONArray array2 = result2.optJSONArray(Keys.RESULTS);
final List<JSONObject> list2 = CollectionUtils.<JSONObject>jsonArrayToList(array2); final List<JSONObject> list2 = CollectionUtils.<JSONObject>jsonArrayToList(array2);
ret.addAll(list2); ret.addAll(list2);
} }
return ret; return ret;
} }
/** /**
* Gets the {@link ArticleRepositoryImpl} singleton. * Gets the {@link ArticleRepositoryImpl} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static ArticleRepositoryImpl getInstance() { public static ArticleRepositoryImpl getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
* *
* @param name the specified name * @param name the specified name
*/ */
private ArticleRepositoryImpl(final String name) { private ArticleRepositoryImpl(final String name) {
super(name); super(name);
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import java.io.Serializable; import java.io.Serializable;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.cache.Cache; import org.b3log.latke.cache.Cache;
import org.b3log.latke.repository.AbstractRepository; import org.b3log.latke.repository.AbstractRepository;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.solo.model.Comment; import org.b3log.solo.model.Comment;
import org.b3log.solo.repository.CommentRepository; import org.b3log.solo.repository.CommentRepository;
import org.b3log.latke.util.CollectionUtils; import org.b3log.latke.util.CollectionUtils;
import org.b3log.solo.model.Article; import org.b3log.solo.model.Article;
import org.b3log.solo.repository.ArticleRepository; import org.b3log.solo.repository.ArticleRepository;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Comment repository. * Comment repository.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.8, Oct 18, 2011 * @version 1.0.0.8, Oct 18, 2011
* @since 0.3.1 * @since 0.3.1
*/ */
public final class CommentRepositoryImpl extends AbstractRepository implements CommentRepository { public final class CommentRepositoryImpl extends AbstractRepository implements CommentRepository {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(CommentRepositoryImpl.class.getName()); private static final Logger LOGGER = Logger.getLogger(CommentRepositoryImpl.class.getName());
/** /**
* Singleton. * Singleton.
*/ */
private static final CommentRepositoryImpl SINGLETON = new CommentRepositoryImpl(Comment.COMMENT); private static final CommentRepositoryImpl SINGLETON = new CommentRepositoryImpl(Comment.COMMENT);
/** /**
* Article repository. * Article repository.
*/ */
private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance(); private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance();
/** /**
* Recent comments query results cache key. * Recent comments query results cache key.
*/ */
public static final String RECENT_CMTS_CACHE_KEY = "recentCMTs"; public static final String RECENT_CMTS_CACHE_KEY = "recentCMTs";
@Override @Override
public int removeComments(final String onId) throws RepositoryException { public int removeComments(final String onId) throws RepositoryException {
final List<JSONObject> comments = getComments(onId, 1, Integer.MAX_VALUE); final List<JSONObject> comments = getComments(onId, 1, Integer.MAX_VALUE);
for (final JSONObject comment : comments) { for (final JSONObject comment : comments) {
final String commentId = comment.optString(Keys.OBJECT_ID); final String commentId = comment.optString(Keys.OBJECT_ID);
remove(commentId); remove(commentId);
} }
LOGGER.log(Level.FINER, "Removed comments[onId={0}, removedCnt={1}]", new Object[]{onId, comments.size()}); LOGGER.log(Level.FINER, "Removed comments[onId={0}, removedCnt={1}]", new Object[]{onId, comments.size()});
return comments.size(); return comments.size();
} }
@Override @Override
public List<JSONObject> getComments(final String onId, final int currentPageNum, final int pageSize) public List<JSONObject> getComments(final String onId, final int currentPageNum, final int pageSize)
throws RepositoryException { throws RepositoryException {
final Query query = new Query().addSort(Keys.OBJECT_ID, SortDirection.DESCENDING). final Query query = new Query().addSort(Keys.OBJECT_ID, SortDirection.DESCENDING).
addFilter(Comment.COMMENT_ON_ID, FilterOperator.EQUAL, onId). addFilter(Comment.COMMENT_ON_ID, FilterOperator.EQUAL, onId).
setCurrentPageNum(currentPageNum). setCurrentPageNum(currentPageNum).
setPageSize(pageSize). setPageSize(pageSize).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
return CollectionUtils.jsonArrayToList(array); return CollectionUtils.jsonArrayToList(array);
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public List<JSONObject> getRecentComments(final int num) throws RepositoryException { public List<JSONObject> getRecentComments(final int num) throws RepositoryException {
if (isCacheEnabled()) { if (isCacheEnabled()) {
final Cache<String, Serializable> cache = getCache(); final Cache<String, Serializable> cache = getCache();
final Object ret = cache.get(RECENT_CMTS_CACHE_KEY); final Object ret = cache.get(RECENT_CMTS_CACHE_KEY);
if (null != ret) { if (null != ret) {
return (List<JSONObject>) ret; return (List<JSONObject>) ret;
} }
} }
final Query query = new Query().addSort(Keys.OBJECT_ID, SortDirection.DESCENDING). final Query query = new Query().addSort(Keys.OBJECT_ID, SortDirection.DESCENDING).
setCurrentPageNum(1). setCurrentPageNum(1).
setPageSize(num).setPageCount(1); setPageSize(num).setPageCount(1);
List<JSONObject> ret; List<JSONObject> ret;
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
ret = CollectionUtils.jsonArrayToList(array); ret = CollectionUtils.jsonArrayToList(array);
// Removes unpublished article related comments // Removes unpublished article related comments
removeForUnpublishedArticles(ret); removeForUnpublishedArticles(ret);
if (isCacheEnabled()) { if (isCacheEnabled()) {
final Cache<String, Serializable> cache = getCache(); final Cache<String, Serializable> cache = getCache();
cache.put(RECENT_CMTS_CACHE_KEY, (Serializable) ret); cache.put(RECENT_CMTS_CACHE_KEY, (Serializable) ret);
} }
return ret; return ret;
} }
/** /**
* Removes comments of unpublished articles for the specified comments. * Removes comments of unpublished articles for the specified comments.
* *
* @param comments the specified comments * @param comments the specified comments
* @throws RepositoryException repository exception * @throws RepositoryException repository exception
*/ */
private void removeForUnpublishedArticles(final List<JSONObject> comments) throws RepositoryException { private void removeForUnpublishedArticles(final List<JSONObject> comments) throws RepositoryException {
LOGGER.finer("Removing unpublished articles' comments...."); LOGGER.finer("Removing unpublished articles' comments....");
final Iterator<JSONObject> iterator = comments.iterator(); final Iterator<JSONObject> iterator = comments.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final JSONObject comment = iterator.next(); final JSONObject comment = iterator.next();
final String commentOnType = comment.optString(Comment.COMMENT_ON_TYPE); final String commentOnType = comment.optString(Comment.COMMENT_ON_TYPE);
if (Article.ARTICLE.equals(commentOnType)) { if (Article.ARTICLE.equals(commentOnType)) {
final String articleId = comment.optString(Comment.COMMENT_ON_ID); final String articleId = comment.optString(Comment.COMMENT_ON_ID);
if (!articleRepository.isPublished(articleId)) { if (!articleRepository.isPublished(articleId)) {
iterator.remove(); iterator.remove();
} }
} }
} }
LOGGER.finer("Removed unpublished articles' comments...."); LOGGER.finer("Removed unpublished articles' comments....");
} }
/** /**
* Gets the {@link CommentRepositoryImpl} singleton. * Gets the {@link CommentRepositoryImpl} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static CommentRepositoryImpl getInstance() { public static CommentRepositoryImpl getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
* *
* @param name the specified name * @param name the specified name
*/ */
private CommentRepositoryImpl(final String name) { private CommentRepositoryImpl(final String name) {
super(name); super(name);
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.repository.AbstractRepository; import org.b3log.latke.repository.AbstractRepository;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.solo.model.Link; import org.b3log.solo.model.Link;
import org.b3log.solo.repository.LinkRepository; import org.b3log.solo.repository.LinkRepository;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Link repository. * Link repository.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.5, Nov 10, 2011 * @version 1.0.0.5, Nov 10, 2011
* @since 0.3.1 * @since 0.3.1
*/ */
public final class LinkRepositoryImpl extends AbstractRepository implements LinkRepository { public final class LinkRepositoryImpl extends AbstractRepository implements LinkRepository {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(LinkRepositoryImpl.class.getName()); private static final Logger LOGGER = Logger.getLogger(LinkRepositoryImpl.class.getName());
/** /**
* Singleton. * Singleton.
*/ */
private static final LinkRepositoryImpl SINGLETON = new LinkRepositoryImpl(Link.LINK); private static final LinkRepositoryImpl SINGLETON = new LinkRepositoryImpl(Link.LINK);
@Override @Override
public JSONObject getByAddress(final String address) throws RepositoryException { public JSONObject getByAddress(final String address) throws RepositoryException {
final Query query = new Query().addFilter(Link.LINK_ADDRESS, FilterOperator.EQUAL, address). final Query query = new Query().addFilter(Link.LINK_ADDRESS, FilterOperator.EQUAL, address).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public int getMaxOrder() throws RepositoryException { public int getMaxOrder() throws RepositoryException {
final Query query = new Query(); final Query query = new Query();
query.addSort(Link.LINK_ORDER, SortDirection.DESCENDING); query.addSort(Link.LINK_ORDER, SortDirection.DESCENDING);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return -1; return -1;
} }
return array.optJSONObject(0).optInt(Link.LINK_ORDER); return array.optJSONObject(0).optInt(Link.LINK_ORDER);
} }
@Override @Override
public JSONObject getByOrder(final int order) throws RepositoryException { public JSONObject getByOrder(final int order) throws RepositoryException {
final Query query = new Query(); final Query query = new Query();
query.addFilter(Link.LINK_ORDER, FilterOperator.EQUAL, order); query.addFilter(Link.LINK_ORDER, FilterOperator.EQUAL, order);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public JSONObject getUpper(final String id) throws RepositoryException { public JSONObject getUpper(final String id) throws RepositoryException {
final JSONObject link = get(id); final JSONObject link = get(id);
if (null == link) { if (null == link) {
return null; return null;
} }
final Query query = new Query(); final Query query = new Query();
query.addFilter(Link.LINK_ORDER, FilterOperator.LESS_THAN, link.optInt(Link.LINK_ORDER)). query.addFilter(Link.LINK_ORDER, FilterOperator.LESS_THAN, link.optInt(Link.LINK_ORDER)).
addSort(Link.LINK_ORDER, SortDirection.DESCENDING); addSort(Link.LINK_ORDER, SortDirection.DESCENDING);
query.setCurrentPageNum(1); query.setCurrentPageNum(1);
query.setPageSize(1); query.setPageSize(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (1 != array.length()) { if (1 != array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public JSONObject getUnder(final String id) throws RepositoryException { public JSONObject getUnder(final String id) throws RepositoryException {
final JSONObject link = get(id); final JSONObject link = get(id);
if (null == link) { if (null == link) {
return null; return null;
} }
final Query query = new Query(); final Query query = new Query();
query.addFilter(Link.LINK_ORDER, FilterOperator.GREATER_THAN, link.optInt(Link.LINK_ORDER)). query.addFilter(Link.LINK_ORDER, FilterOperator.GREATER_THAN, link.optInt(Link.LINK_ORDER)).
addSort(Link.LINK_ORDER, SortDirection.ASCENDING); addSort(Link.LINK_ORDER, SortDirection.ASCENDING);
query.setCurrentPageNum(1); query.setCurrentPageNum(1);
query.setPageSize(1); query.setPageSize(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (1 != array.length()) { if (1 != array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
/** /**
* Gets the {@link LinkRepositoryImpl} singleton. * Gets the {@link LinkRepositoryImpl} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static LinkRepositoryImpl getInstance() { public static LinkRepositoryImpl getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
* *
* @param name the specified name * @param name the specified name
*/ */
private LinkRepositoryImpl(final String name) { private LinkRepositoryImpl(final String name) {
super(name); super(name);
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.repository.AbstractRepository; import org.b3log.latke.repository.AbstractRepository;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.util.CollectionUtils; import org.b3log.latke.util.CollectionUtils;
import org.b3log.solo.model.Page; import org.b3log.solo.model.Page;
import org.b3log.solo.repository.PageRepository; import org.b3log.solo.repository.PageRepository;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Page repository. * Page repository.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.9, Dec 31, 2011 * @version 1.0.0.9, Dec 31, 2011
* @since 0.3.1 * @since 0.3.1
*/ */
public final class PageRepositoryImpl extends AbstractRepository implements PageRepository { public final class PageRepositoryImpl extends AbstractRepository implements PageRepository {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(PageRepositoryImpl.class.getName()); private static final Logger LOGGER = Logger.getLogger(PageRepositoryImpl.class.getName());
/** /**
* Singleton. * Singleton.
*/ */
private static final PageRepositoryImpl SINGLETON = new PageRepositoryImpl(Page.PAGE); private static final PageRepositoryImpl SINGLETON = new PageRepositoryImpl(Page.PAGE);
@Override @Override
public JSONObject getByPermalink(final String permalink) throws RepositoryException { public JSONObject getByPermalink(final String permalink) throws RepositoryException {
final Query query = new Query().addFilter(Page.PAGE_PERMALINK, FilterOperator.EQUAL, permalink). final Query query = new Query().addFilter(Page.PAGE_PERMALINK, FilterOperator.EQUAL, permalink).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public int getMaxOrder() throws RepositoryException { public int getMaxOrder() throws RepositoryException {
final Query query = new Query().addSort(Page.PAGE_ORDER, SortDirection.DESCENDING). final Query query = new Query().addSort(Page.PAGE_ORDER, SortDirection.DESCENDING).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return -1; return -1;
} }
return array.optJSONObject(0).optInt(Page.PAGE_ORDER); return array.optJSONObject(0).optInt(Page.PAGE_ORDER);
} }
@Override @Override
public JSONObject getUpper(final String id) throws RepositoryException { public JSONObject getUpper(final String id) throws RepositoryException {
final JSONObject page = get(id); final JSONObject page = get(id);
if (null == page) { if (null == page) {
return null; return null;
} }
final Query query = new Query().addFilter(Page.PAGE_ORDER, FilterOperator.LESS_THAN, page.optInt(Page.PAGE_ORDER)). final Query query = new Query().addFilter(Page.PAGE_ORDER, FilterOperator.LESS_THAN, page.optInt(Page.PAGE_ORDER)).
addSort(Page.PAGE_ORDER, SortDirection.DESCENDING). addSort(Page.PAGE_ORDER, SortDirection.DESCENDING).
setCurrentPageNum(1).setPageSize(1).setPageCount(1); setCurrentPageNum(1).setPageSize(1).setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (1 != array.length()) { if (1 != array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public JSONObject getUnder(final String id) throws RepositoryException { public JSONObject getUnder(final String id) throws RepositoryException {
final JSONObject page = get(id); final JSONObject page = get(id);
if (null == page) { if (null == page) {
return null; return null;
} }
final Query query = new Query().addFilter(Page.PAGE_ORDER, FilterOperator.GREATER_THAN, page.optInt(Page.PAGE_ORDER)). final Query query = new Query().addFilter(Page.PAGE_ORDER, FilterOperator.GREATER_THAN, page.optInt(Page.PAGE_ORDER)).
addSort(Page.PAGE_ORDER, SortDirection.ASCENDING).setCurrentPageNum(1). addSort(Page.PAGE_ORDER, SortDirection.ASCENDING).setCurrentPageNum(1).
setPageSize(1). setPageSize(1).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (1 != array.length()) { if (1 != array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public JSONObject getByOrder(final int order) throws RepositoryException { public JSONObject getByOrder(final int order) throws RepositoryException {
final Query query = new Query().addFilter(Page.PAGE_ORDER, FilterOperator.EQUAL, order). final Query query = new Query().addFilter(Page.PAGE_ORDER, FilterOperator.EQUAL, order).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public List<JSONObject> getPages() throws RepositoryException { public List<JSONObject> getPages() throws RepositoryException {
final Query query = new Query().addSort( final Query query = new Query().addSort(
Page.PAGE_ORDER, SortDirection.ASCENDING).setPageCount(1); Page.PAGE_ORDER, SortDirection.ASCENDING).setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
return CollectionUtils.jsonArrayToList(result.optJSONArray(Keys.RESULTS)); return CollectionUtils.jsonArrayToList(result.optJSONArray(Keys.RESULTS));
} }
/** /**
* Gets the {@link PageRepositoryImpl} singleton. * Gets the {@link PageRepositoryImpl} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static PageRepositoryImpl getInstance() { public static PageRepositoryImpl getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
* *
* @param name the specified name * @param name the specified name
*/ */
private PageRepositoryImpl(final String name) { private PageRepositoryImpl(final String name) {
super(name); super(name);
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.b3log.solo.model.Article; import org.b3log.solo.model.Article;
import org.b3log.solo.model.Tag; import org.b3log.solo.model.Tag;
import org.b3log.solo.repository.TagArticleRepository; import org.b3log.solo.repository.TagArticleRepository;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.repository.AbstractRepository; import org.b3log.latke.repository.AbstractRepository;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.util.CollectionUtils; import org.b3log.latke.util.CollectionUtils;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Tag-Article relation repository. * Tag-Article relation repository.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.9, Nov 9, 2011 * @version 1.0.0.9, Nov 9, 2011
* @since 0.3.1 * @since 0.3.1
*/ */
public final class TagArticleRepositoryImpl extends AbstractRepository implements TagArticleRepository { public final class TagArticleRepositoryImpl extends AbstractRepository implements TagArticleRepository {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(TagArticleRepositoryImpl.class.getName()); private static final Logger LOGGER = Logger.getLogger(TagArticleRepositoryImpl.class.getName());
/** /**
* Singleton. * Singleton.
*/ */
private static final TagArticleRepositoryImpl SINGLETON = new TagArticleRepositoryImpl(Tag.TAG + "_" + Article.ARTICLE); private static final TagArticleRepositoryImpl SINGLETON = new TagArticleRepositoryImpl(Tag.TAG + "_" + Article.ARTICLE);
@Override @Override
public List<JSONObject> getByArticleId(final String articleId) throws RepositoryException { public List<JSONObject> getByArticleId(final String articleId) throws RepositoryException {
final Query query = new Query().addFilter(Article.ARTICLE + "_" + Keys.OBJECT_ID, FilterOperator.EQUAL, articleId). final Query query = new Query().addFilter(Article.ARTICLE + "_" + Keys.OBJECT_ID, FilterOperator.EQUAL, articleId).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
return CollectionUtils.jsonArrayToList(array); return CollectionUtils.jsonArrayToList(array);
} }
@Override @Override
public JSONObject getByTagId(final String tagId, final int currentPageNum, final int pageSize) public JSONObject getByTagId(final String tagId, final int currentPageNum, final int pageSize)
throws RepositoryException { throws RepositoryException {
final Query query = new Query().addFilter(Tag.TAG + "_" + Keys.OBJECT_ID, FilterOperator.EQUAL, tagId). final Query query = new Query().addFilter(Tag.TAG + "_" + Keys.OBJECT_ID, FilterOperator.EQUAL, tagId).
addSort(Article.ARTICLE + "_" + Keys.OBJECT_ID, SortDirection.DESCENDING). addSort(Article.ARTICLE + "_" + Keys.OBJECT_ID, SortDirection.DESCENDING).
setCurrentPageNum(currentPageNum). setCurrentPageNum(currentPageNum).
setPageSize(pageSize). setPageSize(pageSize).
setPageCount(1); setPageCount(1);
return get(query); return get(query);
} }
/** /**
* Gets the {@link TagArticleRepositoryImpl} singleton. * Gets the {@link TagArticleRepositoryImpl} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static TagArticleRepositoryImpl getInstance() { public static TagArticleRepositoryImpl getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
* *
* @param name the specified name * @param name the specified name
*/ */
private TagArticleRepositoryImpl(final String name) { private TagArticleRepositoryImpl(final String name) {
super(name); super(name);
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.b3log.solo.model.Tag; import org.b3log.solo.model.Tag;
import org.b3log.solo.repository.TagRepository; import org.b3log.solo.repository.TagRepository;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.repository.AbstractRepository; import org.b3log.latke.repository.AbstractRepository;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.util.CollectionUtils; import org.b3log.latke.util.CollectionUtils;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Tag repository. * Tag repository.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.1.1, Nov 29, 2011 * @version 1.0.1.1, Nov 29, 2011
* @since 0.3.1 * @since 0.3.1
*/ */
public final class TagRepositoryImpl extends AbstractRepository implements TagRepository { public final class TagRepositoryImpl extends AbstractRepository implements TagRepository {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(TagRepositoryImpl.class.getName()); private static final Logger LOGGER = Logger.getLogger(TagRepositoryImpl.class.getName());
/** /**
* Singleton. * Singleton.
*/ */
private static final TagRepositoryImpl SINGLETON = new TagRepositoryImpl(Tag.TAG); private static final TagRepositoryImpl SINGLETON = new TagRepositoryImpl(Tag.TAG);
/** /**
* Tag-Article relation repository. * Tag-Article relation repository.
*/ */
private TagArticleRepositoryImpl tagArticleRepository = TagArticleRepositoryImpl.getInstance(); private TagArticleRepositoryImpl tagArticleRepository = TagArticleRepositoryImpl.getInstance();
@Override @Override
public JSONObject getByTitle(final String tagTitle) throws RepositoryException { public JSONObject getByTitle(final String tagTitle) throws RepositoryException {
final Query query = new Query().addFilter(Tag.TAG_TITLE, FilterOperator.EQUAL, tagTitle). final Query query = new Query().addFilter(Tag.TAG_TITLE, FilterOperator.EQUAL, tagTitle).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public List<JSONObject> getMostUsedTags(final int num) throws RepositoryException { public List<JSONObject> getMostUsedTags(final int num) throws RepositoryException {
final Query query = new Query().addSort(Tag.TAG_PUBLISHED_REFERENCE_COUNT, SortDirection.DESCENDING). final Query query = new Query().addSort(Tag.TAG_PUBLISHED_REFERENCE_COUNT, SortDirection.DESCENDING).
setCurrentPageNum(1). setCurrentPageNum(1).
setPageSize(num). setPageSize(num).
setPageCount(1); setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
return CollectionUtils.jsonArrayToList(array); return CollectionUtils.jsonArrayToList(array);
} }
@Override @Override
public List<JSONObject> getByArticleId(final String articleId) throws RepositoryException { public List<JSONObject> getByArticleId(final String articleId) throws RepositoryException {
final List<JSONObject> ret = new ArrayList<JSONObject>(); final List<JSONObject> ret = new ArrayList<JSONObject>();
final List<JSONObject> tagArticleRelations = tagArticleRepository.getByArticleId(articleId); final List<JSONObject> tagArticleRelations = tagArticleRepository.getByArticleId(articleId);
for (final JSONObject tagArticleRelation : tagArticleRelations) { for (final JSONObject tagArticleRelation : tagArticleRelations) {
final String tagId = tagArticleRelation.optString(Tag.TAG + "_" + Keys.OBJECT_ID); final String tagId = tagArticleRelation.optString(Tag.TAG + "_" + Keys.OBJECT_ID);
final JSONObject tag = get(tagId); final JSONObject tag = get(tagId);
ret.add(tag); ret.add(tag);
} }
return ret; return ret;
} }
/** /**
* Gets the {@link TagRepositoryImpl} singleton. * Gets the {@link TagRepositoryImpl} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static TagRepositoryImpl getInstance() { public static TagRepositoryImpl getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
* *
* @param name the specified name * @param name the specified name
*/ */
private TagRepositoryImpl(final String name) { private TagRepositoryImpl(final String name) {
super(name); super(name);
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.model.Role; import org.b3log.latke.model.Role;
import org.b3log.latke.model.User; import org.b3log.latke.model.User;
import org.b3log.latke.repository.AbstractRepository; import org.b3log.latke.repository.AbstractRepository;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.solo.repository.UserRepository; import org.b3log.solo.repository.UserRepository;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* User repository. * User repository.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.8, Nov 10, 2011 * @version 1.0.0.8, Nov 10, 2011
* @since 0.3.1 * @since 0.3.1
*/ */
public final class UserRepositoryImpl extends AbstractRepository implements UserRepository { public final class UserRepositoryImpl extends AbstractRepository implements UserRepository {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(UserRepositoryImpl.class.getName()); private static final Logger LOGGER = Logger.getLogger(UserRepositoryImpl.class.getName());
/** /**
* Singleton. * Singleton.
*/ */
private static final UserRepositoryImpl SINGLETON = new UserRepositoryImpl(User.USER); private static final UserRepositoryImpl SINGLETON = new UserRepositoryImpl(User.USER);
@Override @Override
public JSONObject getByEmail(final String email) throws RepositoryException { public JSONObject getByEmail(final String email) throws RepositoryException {
final Query query = new Query().setPageCount(1); final Query query = new Query().setPageCount(1);
query.addFilter(User.USER_EMAIL, FilterOperator.EQUAL, email.toLowerCase().trim()); query.addFilter(User.USER_EMAIL, FilterOperator.EQUAL, email.toLowerCase().trim());
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public JSONObject getAdmin() throws RepositoryException { public JSONObject getAdmin() throws RepositoryException {
final Query query = new Query().addFilter(User.USER_ROLE, FilterOperator.EQUAL, Role.ADMIN_ROLE).setPageCount(1); final Query query = new Query().addFilter(User.USER_ROLE, FilterOperator.EQUAL, Role.ADMIN_ROLE).setPageCount(1);
final JSONObject result = get(query); final JSONObject result = get(query);
final JSONArray array = result.optJSONArray(Keys.RESULTS); final JSONArray array = result.optJSONArray(Keys.RESULTS);
if (0 == array.length()) { if (0 == array.length()) {
return null; return null;
} }
return array.optJSONObject(0); return array.optJSONObject(0);
} }
@Override @Override
public boolean isAdminEmail(final String email) throws RepositoryException { public boolean isAdminEmail(final String email) throws RepositoryException {
final JSONObject user = getByEmail(email); final JSONObject user = getByEmail(email);
if (null == user) { if (null == user) {
return false; return false;
} }
return Role.ADMIN_ROLE.equals(user.optString(User.USER_ROLE)); return Role.ADMIN_ROLE.equals(user.optString(User.USER_ROLE));
} }
/** /**
* Gets the {@link UserRepositoryImpl} singleton. * Gets the {@link UserRepositoryImpl} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static UserRepositoryImpl getInstance() { public static UserRepositoryImpl getInstance() {
return SINGLETON; return SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
* *
* @param name the specified name * @param name the specified name
*/ */
private UserRepositoryImpl(final String name) { private UserRepositoryImpl(final String name) {
super(name); super(name);
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.service; package org.b3log.solo.service;
import org.b3log.solo.repository.ArchiveDateArticleRepository; import org.b3log.solo.repository.ArchiveDateArticleRepository;
import org.b3log.solo.repository.impl.ArchiveDateArticleRepositoryImpl; import org.b3log.solo.repository.impl.ArchiveDateArticleRepositoryImpl;
import java.util.Set; import java.util.Set;
import org.b3log.solo.model.Sign; import org.b3log.solo.model.Sign;
import org.b3log.solo.model.Tag; import org.b3log.solo.model.Tag;
import java.util.Date; import java.util.Date;
import org.b3log.latke.model.User; import org.b3log.latke.model.User;
import org.b3log.solo.model.Common; import org.b3log.solo.model.Common;
import org.b3log.solo.util.Articles; import org.b3log.solo.util.Articles;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.model.Pagination; import org.b3log.latke.model.Pagination;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.service.ServiceException; import org.b3log.latke.service.ServiceException;
import org.b3log.latke.util.CollectionUtils; import org.b3log.latke.util.CollectionUtils;
import org.b3log.latke.util.Paginator; import org.b3log.latke.util.Paginator;
import org.b3log.latke.util.Stopwatchs; import org.b3log.latke.util.Stopwatchs;
import org.b3log.latke.util.Strings; import org.b3log.latke.util.Strings;
import org.b3log.solo.model.Article; import org.b3log.solo.model.Article;
import org.b3log.solo.model.Preference; import org.b3log.solo.model.Preference;
import org.b3log.solo.repository.ArticleRepository; import org.b3log.solo.repository.ArticleRepository;
import org.b3log.solo.repository.TagArticleRepository; import org.b3log.solo.repository.TagArticleRepository;
import org.b3log.solo.repository.TagRepository; import org.b3log.solo.repository.TagRepository;
import org.b3log.solo.repository.impl.ArticleRepositoryImpl; import org.b3log.solo.repository.impl.ArticleRepositoryImpl;
import org.b3log.solo.repository.impl.TagArticleRepositoryImpl; import org.b3log.solo.repository.impl.TagArticleRepositoryImpl;
import org.b3log.solo.repository.impl.TagRepositoryImpl; import org.b3log.solo.repository.impl.TagRepositoryImpl;
import org.b3log.solo.util.Statistics; import org.b3log.solo.util.Statistics;
import org.b3log.solo.util.comparator.Comparators; import org.b3log.solo.util.comparator.Comparators;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import static org.b3log.solo.model.Article.*; import static org.b3log.solo.model.Article.*;
import org.b3log.solo.util.Markdowns; import org.b3log.solo.util.Markdowns;
/** /**
* Article query service. * Article query service.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.1.0, May 10, 2012 * @version 1.0.1.0, May 10, 2012
* @since 0.3.5 * @since 0.3.5
*/ */
public final class ArticleQueryService { public final class ArticleQueryService {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(ArticleQueryService.class.getName()); private static final Logger LOGGER = Logger.getLogger(ArticleQueryService.class.getName());
/** /**
* Article repository. * Article repository.
*/ */
private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance(); private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance();
/** /**
* Preference query service. * Preference query service.
*/ */
private PreferenceQueryService preferenceQueryService = PreferenceQueryService.getInstance(); private PreferenceQueryService preferenceQueryService = PreferenceQueryService.getInstance();
/** /**
* Tag repository. * Tag repository.
*/ */
private TagRepository tagRepository = TagRepositoryImpl.getInstance(); private TagRepository tagRepository = TagRepositoryImpl.getInstance();
/** /**
* Tag-Article repository. * Tag-Article repository.
*/ */
private TagArticleRepository tagArticleRepository = TagArticleRepositoryImpl.getInstance(); private TagArticleRepository tagArticleRepository = TagArticleRepositoryImpl.getInstance();
/** /**
* Archive date-Article repository. * Archive date-Article repository.
*/ */
private ArchiveDateArticleRepository archiveDateArticleRepository = ArchiveDateArticleRepositoryImpl.getInstance(); private ArchiveDateArticleRepository archiveDateArticleRepository = ArchiveDateArticleRepositoryImpl.getInstance();
/** /**
* Statistic utilities. * Statistic utilities.
*/ */
private Statistics statistics = Statistics.getInstance(); private Statistics statistics = Statistics.getInstance();
/** /**
* Article utilities. * Article utilities.
*/ */
private static Articles articleUtils = Articles.getInstance(); private static Articles articleUtils = Articles.getInstance();
/** /**
* Gets the recent articles with the specified fetch size. * Gets the recent articles with the specified fetch size.
* *
* @param fetchSize the specified fetch size * @param fetchSize the specified fetch size
* @return a list of json object, its size less or equal to the specified * @return a list of json object, its size less or equal to the specified
* fetch size * fetch size
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public List<JSONObject> getRecentArticles(final int fetchSize) throws ServiceException { public List<JSONObject> getRecentArticles(final int fetchSize) throws ServiceException {
try { try {
return articleRepository.getRecentArticles(fetchSize); return articleRepository.getRecentArticles(fetchSize);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Gets recent articles failed", e); LOGGER.log(Level.SEVERE, "Gets recent articles failed", e);
return Collections.emptyList(); return Collections.emptyList();
} }
} }
/** /**
* Gets an article by the specified article id. * Gets an article by the specified article id.
* *
* <p> * <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing). * <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p> * </p>
* *
* @param articleId the specified article id * @param articleId the specified article id
* @return for example, * @return for example,
* <pre> * <pre>
* { * {
* "article": { * "article": {
* "oId": "", * "oId": "",
* "articleTitle": "", * "articleTitle": "",
* "articleAbstract": "", * "articleAbstract": "",
* "articleContent": "", * "articleContent": "",
* "articlePermalink": "", * "articlePermalink": "",
* "articleHadBeenPublished": boolean, * "articleHadBeenPublished": boolean,
* "articleCreateDate": java.util.Date, * "articleCreateDate": java.util.Date,
* "articleTags": [{ * "articleTags": [{
* "oId": "", * "oId": "",
* "tagTitle": "" * "tagTitle": ""
* }, ....], * }, ....],
* "articleSignId": "", * "articleSignId": "",
* "articleViewPwd": "", * "articleViewPwd": "",
* "articleEditorType": "", * "articleEditorType": "",
* "signs": [{ * "signs": [{
* "oId": "", * "oId": "",
* "signHTML": "" * "signHTML": ""
* }, ....] * }, ....]
* } * }
* } * }
* </pre>, returns {@code null} if not found * </pre>, returns {@code null} if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public JSONObject getArticle(final String articleId) throws ServiceException { public JSONObject getArticle(final String articleId) throws ServiceException {
try { try {
final JSONObject ret = new JSONObject(); final JSONObject ret = new JSONObject();
final JSONObject article = articleRepository.get(articleId); final JSONObject article = articleRepository.get(articleId);
if (null == article) { if (null == article) {
return null; return null;
} }
ret.put(ARTICLE, article); ret.put(ARTICLE, article);
// Tags // Tags
final JSONArray tags = new JSONArray(); final JSONArray tags = new JSONArray();
final List<JSONObject> tagArticleRelations = tagArticleRepository.getByArticleId(articleId); final List<JSONObject> tagArticleRelations = tagArticleRepository.getByArticleId(articleId);
for (int i = 0; i < tagArticleRelations.size(); i++) { for (int i = 0; i < tagArticleRelations.size(); i++) {
final JSONObject tagArticleRelation = tagArticleRelations.get(i); final JSONObject tagArticleRelation = tagArticleRelations.get(i);
final String tagId = tagArticleRelation.getString(Tag.TAG + "_" + Keys.OBJECT_ID); final String tagId = tagArticleRelation.getString(Tag.TAG + "_" + Keys.OBJECT_ID);
final JSONObject tag = tagRepository.get(tagId); final JSONObject tag = tagRepository.get(tagId);
tags.put(tag); tags.put(tag);
} }
article.put(ARTICLE_TAGS_REF, tags); article.put(ARTICLE_TAGS_REF, tags);
// Signs // Signs
final JSONObject preference = preferenceQueryService.getPreference(); final JSONObject preference = preferenceQueryService.getPreference();
article.put(Sign.SIGNS, new JSONArray(preference.getString(Preference.SIGNS))); article.put(Sign.SIGNS, new JSONArray(preference.getString(Preference.SIGNS)));
// Remove unused properties // Remove unused properties
article.remove(ARTICLE_AUTHOR_EMAIL); article.remove(ARTICLE_AUTHOR_EMAIL);
article.remove(ARTICLE_COMMENT_COUNT); article.remove(ARTICLE_COMMENT_COUNT);
article.remove(ARTICLE_IS_PUBLISHED); article.remove(ARTICLE_IS_PUBLISHED);
article.remove(ARTICLE_PUT_TOP); article.remove(ARTICLE_PUT_TOP);
article.remove(ARTICLE_UPDATE_DATE); article.remove(ARTICLE_UPDATE_DATE);
article.remove(ARTICLE_VIEW_COUNT); article.remove(ARTICLE_VIEW_COUNT);
article.remove(ARTICLE_RANDOM_DOUBLE); article.remove(ARTICLE_RANDOM_DOUBLE);
LOGGER.log(Level.FINER, "Got an article[id={0}]", articleId); LOGGER.log(Level.FINER, "Got an article[id={0}]", articleId);
return ret; return ret;
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Gets an article failed", e); LOGGER.log(Level.SEVERE, "Gets an article failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets articles(by crate date descending) by the specified request json * Gets articles(by crate date descending) by the specified request json
* object. * object.
* *
* <p> * <p>
* If the property "articleIsPublished" of the specified request json object is {@code true}, the returned articles all are published, * If the property "articleIsPublished" of the specified request json object is {@code true}, the returned articles all are published,
* {@code false} otherwise. * {@code false} otherwise.
* </p> * </p>
* *
* <p> * <p>
* Specified the "excludes" for results properties exclusion. * Specified the "excludes" for results properties exclusion.
* </p> * </p>
* *
* @param requestJSONObject the specified request json object, for example, * @param requestJSONObject the specified request json object, for example,
* <pre> * <pre>
* { * {
* "paginationCurrentPageNum": 1, * "paginationCurrentPageNum": 1,
* "paginationPageSize": 20, * "paginationPageSize": 20,
* "paginationWindowSize": 10, * "paginationWindowSize": 10,
* "articleIsPublished": boolean, * "articleIsPublished": boolean,
* "excludes": ["", ....] // Optional * "excludes": ["", ....] // Optional
* }, see {@link Pagination} for more details * }, see {@link Pagination} for more details
* </pre> * </pre>
* @return for example, * @return for example,
* <pre> * <pre>
* { * {
* "pagination": { * "pagination": {
* "paginationPageCount": 100, * "paginationPageCount": 100,
* "paginationPageNums": [1, 2, 3, 4, 5] * "paginationPageNums": [1, 2, 3, 4, 5]
* }, * },
* "articles": [{ * "articles": [{
* "oId": "", * "oId": "",
* "articleTitle": "", * "articleTitle": "",
* "articleCommentCount": int, * "articleCommentCount": int,
* "articleCreateTime"; long, * "articleCreateTime"; long,
* "articleViewCount": int, * "articleViewCount": int,
* "articleTags": "tag1, tag2, ....", * "articleTags": "tag1, tag2, ....",
* "articlePutTop": boolean, * "articlePutTop": boolean,
* "articleSignId": "", * "articleSignId": "",
* "articleViewPwd": "", * "articleViewPwd": "",
* "articleEditorType": "", * "articleEditorType": "",
* .... // Specified by the "excludes" * .... // Specified by the "excludes"
* }, ....] * }, ....]
* } * }
* </pre>, order by article update date and sticky(put top). * </pre>, order by article update date and sticky(put top).
* @throws ServiceException service exception * @throws ServiceException service exception
* @see Pagination * @see Pagination
*/ */
public JSONObject getArticles(final JSONObject requestJSONObject) throws ServiceException { public JSONObject getArticles(final JSONObject requestJSONObject) throws ServiceException {
final JSONObject ret = new JSONObject(); final JSONObject ret = new JSONObject();
try { try {
final int currentPageNum = requestJSONObject.getInt(Pagination.PAGINATION_CURRENT_PAGE_NUM); final int currentPageNum = requestJSONObject.getInt(Pagination.PAGINATION_CURRENT_PAGE_NUM);
final int pageSize = requestJSONObject.getInt(Pagination.PAGINATION_PAGE_SIZE); final int pageSize = requestJSONObject.getInt(Pagination.PAGINATION_PAGE_SIZE);
final int windowSize = requestJSONObject.getInt(Pagination.PAGINATION_WINDOW_SIZE); final int windowSize = requestJSONObject.getInt(Pagination.PAGINATION_WINDOW_SIZE);
final boolean articleIsPublished = requestJSONObject.optBoolean(ARTICLE_IS_PUBLISHED, true); final boolean articleIsPublished = requestJSONObject.optBoolean(ARTICLE_IS_PUBLISHED, true);
final Query query = new Query().setCurrentPageNum(currentPageNum). final Query query = new Query().setCurrentPageNum(currentPageNum).
setPageSize(pageSize). setPageSize(pageSize).
addSort(ARTICLE_PUT_TOP, SortDirection.DESCENDING). addSort(ARTICLE_PUT_TOP, SortDirection.DESCENDING).
addSort(ARTICLE_CREATE_DATE, SortDirection.DESCENDING). addSort(ARTICLE_CREATE_DATE, SortDirection.DESCENDING).
addFilter(ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, articleIsPublished); addFilter(ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, articleIsPublished);
int articleCount = statistics.getBlogArticleCount(); int articleCount = statistics.getBlogArticleCount();
if (!articleIsPublished) { if (!articleIsPublished) {
articleCount -= statistics.getPublishedBlogArticleCount(); articleCount -= statistics.getPublishedBlogArticleCount();
} }
final int pageCount = (int) Math.ceil((double) articleCount / (double) pageSize); final int pageCount = (int) Math.ceil((double) articleCount / (double) pageSize);
query.setPageCount(pageCount); query.setPageCount(pageCount);
final JSONObject result = articleRepository.get(query); final JSONObject result = articleRepository.get(query);
final JSONObject pagination = new JSONObject(); final JSONObject pagination = new JSONObject();
ret.put(Pagination.PAGINATION, pagination); ret.put(Pagination.PAGINATION, pagination);
final List<Integer> pageNums = Paginator.paginate(currentPageNum, pageSize, pageCount, windowSize); final List<Integer> pageNums = Paginator.paginate(currentPageNum, pageSize, pageCount, windowSize);
pagination.put(Pagination.PAGINATION_PAGE_COUNT, pageCount); pagination.put(Pagination.PAGINATION_PAGE_COUNT, pageCount);
pagination.put(Pagination.PAGINATION_PAGE_NUMS, pageNums); pagination.put(Pagination.PAGINATION_PAGE_NUMS, pageNums);
final JSONArray articles = result.getJSONArray(Keys.RESULTS); final JSONArray articles = result.getJSONArray(Keys.RESULTS);
JSONArray excludes = requestJSONObject.optJSONArray(Keys.EXCLUDES); JSONArray excludes = requestJSONObject.optJSONArray(Keys.EXCLUDES);
excludes = null == excludes ? new JSONArray() : excludes; excludes = null == excludes ? new JSONArray() : excludes;
for (int i = 0; i < articles.length(); i++) { for (int i = 0; i < articles.length(); i++) {
final JSONObject article = articles.getJSONObject(i); final JSONObject article = articles.getJSONObject(i);
final JSONObject author = articleUtils.getAuthor(article); final JSONObject author = articleUtils.getAuthor(article);
final String authorName = author.getString(User.USER_NAME); final String authorName = author.getString(User.USER_NAME);
article.put(Common.AUTHOR_NAME, authorName); article.put(Common.AUTHOR_NAME, authorName);
article.put(ARTICLE_CREATE_TIME, ((Date) article.get(ARTICLE_CREATE_DATE)).getTime()); article.put(ARTICLE_CREATE_TIME, ((Date) article.get(ARTICLE_CREATE_DATE)).getTime());
// Markdown to HTML for content and abstract // Markdown to HTML for content and abstract
markdown(article); markdown(article);
// Remove unused properties // Remove unused properties
for (int j = 0; j < excludes.length(); j++) { for (int j = 0; j < excludes.length(); j++) {
article.remove(excludes.optString(j)); article.remove(excludes.optString(j));
} }
} }
ret.put(ARTICLES, articles); ret.put(ARTICLES, articles);
return ret; return ret;
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Gets articles failed", e); LOGGER.log(Level.SEVERE, "Gets articles failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets a list of published articles with the specified tag id, current page number and page size. * Gets a list of published articles with the specified tag id, current page number and page size.
* *
* @param tagId the specified tag id * @param tagId the specified tag id
* @param currentPageNum the specified current page number * @param currentPageNum the specified current page number
* @param pageSize the specified page size * @param pageSize the specified page size
* @return a list of articles, returns an empty list if not found * @return a list of articles, returns an empty list if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public List<JSONObject> getArticlesByTag(final String tagId, final int currentPageNum, final int pageSize) public List<JSONObject> getArticlesByTag(final String tagId, final int currentPageNum, final int pageSize)
throws ServiceException { throws ServiceException {
try { try {
JSONObject result = tagArticleRepository.getByTagId(tagId, currentPageNum, pageSize); JSONObject result = tagArticleRepository.getByTagId(tagId, currentPageNum, pageSize);
final JSONArray tagArticleRelations = result.getJSONArray(Keys.RESULTS); final JSONArray tagArticleRelations = result.getJSONArray(Keys.RESULTS);
if (0 == tagArticleRelations.length()) { if (0 == tagArticleRelations.length()) {
return Collections.emptyList(); return Collections.emptyList();
} }
final Set<String> articleIds = new HashSet<String>(); final Set<String> articleIds = new HashSet<String>();
for (int i = 0; i < tagArticleRelations.length(); i++) { for (int i = 0; i < tagArticleRelations.length(); i++) {
final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(i); final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(i);
final String articleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID); final String articleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID);
articleIds.add(articleId); articleIds.add(articleId);
} }
final List<JSONObject> ret = new ArrayList<JSONObject>(); final List<JSONObject> ret = new ArrayList<JSONObject>();
final Query query = new Query().addFilter(Keys.OBJECT_ID, FilterOperator.IN, articleIds). final Query query = new Query().addFilter(Keys.OBJECT_ID, FilterOperator.IN, articleIds).
setPageCount(1).index(Article.ARTICLE_PERMALINK); setPageCount(1).index(Article.ARTICLE_PERMALINK);
result = articleRepository.get(query); result = articleRepository.get(query);
final JSONArray articles = result.getJSONArray(Keys.RESULTS); final JSONArray articles = result.getJSONArray(Keys.RESULTS);
for (int i = 0; i < articles.length(); i++) { for (int i = 0; i < articles.length(); i++) {
final JSONObject article = articles.getJSONObject(i); final JSONObject article = articles.getJSONObject(i);
if (!article.getBoolean(Article.ARTICLE_IS_PUBLISHED)) { if (!article.getBoolean(Article.ARTICLE_IS_PUBLISHED)) {
// Skips the unpublished article // Skips the unpublished article
continue; continue;
} }
// Markdown to HTML for content and abstract // Markdown to HTML for content and abstract
markdown(article); markdown(article);
ret.add(article); ret.add(article);
} }
return ret; return ret;
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Gets articles by tag[id=" + tagId + "] failed", e); LOGGER.log(Level.SEVERE, "Gets articles by tag[id=" + tagId + "] failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets a list of published articles with the specified archive date id, current page number and page size. * Gets a list of published articles with the specified archive date id, current page number and page size.
* *
* @param archiveDateId the specified archive date id * @param archiveDateId the specified archive date id
* @param currentPageNum the specified current page number * @param currentPageNum the specified current page number
* @param pageSize the specified page size * @param pageSize the specified page size
* @return a list of articles, returns an empty list if not found * @return a list of articles, returns an empty list if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public List<JSONObject> getArticlesByArchiveDate(final String archiveDateId, final int currentPageNum, final int pageSize) public List<JSONObject> getArticlesByArchiveDate(final String archiveDateId, final int currentPageNum, final int pageSize)
throws ServiceException { throws ServiceException {
try { try {
JSONObject result = archiveDateArticleRepository.getByArchiveDateId(archiveDateId, currentPageNum, pageSize); JSONObject result = archiveDateArticleRepository.getByArchiveDateId(archiveDateId, currentPageNum, pageSize);
final JSONArray relations = result.getJSONArray(Keys.RESULTS); final JSONArray relations = result.getJSONArray(Keys.RESULTS);
if (0 == relations.length()) { if (0 == relations.length()) {
return Collections.emptyList(); return Collections.emptyList();
} }
final Set<String> articleIds = new HashSet<String>(); final Set<String> articleIds = new HashSet<String>();
for (int i = 0; i < relations.length(); i++) { for (int i = 0; i < relations.length(); i++) {
final JSONObject relation = relations.getJSONObject(i); final JSONObject relation = relations.getJSONObject(i);
final String articleId = relation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID); final String articleId = relation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID);
articleIds.add(articleId); articleIds.add(articleId);
} }
final List<JSONObject> ret = new ArrayList<JSONObject>(); final List<JSONObject> ret = new ArrayList<JSONObject>();
final Query query = new Query().addFilter(Keys.OBJECT_ID, FilterOperator.IN, articleIds). final Query query = new Query().addFilter(Keys.OBJECT_ID, FilterOperator.IN, articleIds).
setPageCount(1).index(Article.ARTICLE_PERMALINK); setPageCount(1).index(Article.ARTICLE_PERMALINK);
result = articleRepository.get(query); result = articleRepository.get(query);
final JSONArray articles = result.getJSONArray(Keys.RESULTS); final JSONArray articles = result.getJSONArray(Keys.RESULTS);
for (int i = 0; i < articles.length(); i++) { for (int i = 0; i < articles.length(); i++) {
final JSONObject article = articles.getJSONObject(i); final JSONObject article = articles.getJSONObject(i);
if (!article.getBoolean(Article.ARTICLE_IS_PUBLISHED)) { if (!article.getBoolean(Article.ARTICLE_IS_PUBLISHED)) {
// Skips the unpublished article // Skips the unpublished article
continue; continue;
} }
// Markdown to HTML for content and abstract // Markdown to HTML for content and abstract
markdown(article); markdown(article);
ret.add(article); ret.add(article);
} }
return ret; return ret;
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Gets articles by archive date[id=" + archiveDateId + "] failed", e); LOGGER.log(Level.SEVERE, "Gets articles by archive date[id=" + archiveDateId + "] failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets a list of articles randomly with the specified fetch size. * Gets a list of articles randomly with the specified fetch size.
* *
* <p> * <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing). * <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p> * </p>
* *
* @param fetchSize the specified fetch size * @param fetchSize the specified fetch size
* @return a list of json objects, its size less or equal to the specified * @return a list of json objects, its size less or equal to the specified
* fetch size * fetch size
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public List<JSONObject> getArticlesRandomly(final int fetchSize) throws ServiceException { public List<JSONObject> getArticlesRandomly(final int fetchSize) throws ServiceException {
try { try {
final List<JSONObject> ret = articleRepository.getRandomly(fetchSize); final List<JSONObject> ret = articleRepository.getRandomly(fetchSize);
removeUnusedProperties(ret); removeUnusedProperties(ret);
return ret; return ret;
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Gets articles randomly failed[fetchSize=" + fetchSize + "]", e); LOGGER.log(Level.SEVERE, "Gets articles randomly failed[fetchSize=" + fetchSize + "]", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets the relevant published articles of the specified article. * Gets the relevant published articles of the specified article.
* *
* <p> * <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing). * <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p> * </p>
* *
* @param article the specified article * @param article the specified article
* @param preference the specified preference * @param preference the specified preference
* @return a list of articles, returns an empty list if not found * @return a list of articles, returns an empty list if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public List<JSONObject> getRelevantArticles(final JSONObject article, final JSONObject preference) public List<JSONObject> getRelevantArticles(final JSONObject article, final JSONObject preference)
throws ServiceException { throws ServiceException {
try { try {
final int displayCnt = preference.getInt(Preference.RELEVANT_ARTICLES_DISPLAY_CNT); final int displayCnt = preference.getInt(Preference.RELEVANT_ARTICLES_DISPLAY_CNT);
final String[] tagTitles = article.getString(Article.ARTICLE_TAGS_REF).split(","); final String[] tagTitles = article.getString(Article.ARTICLE_TAGS_REF).split(",");
final int maxTagCnt = displayCnt > tagTitles.length ? tagTitles.length : displayCnt; final int maxTagCnt = displayCnt > tagTitles.length ? tagTitles.length : displayCnt;
final String articleId = article.getString(Keys.OBJECT_ID); final String articleId = article.getString(Keys.OBJECT_ID);
final List<JSONObject> articles = new ArrayList<JSONObject>(); final List<JSONObject> articles = new ArrayList<JSONObject>();
for (int i = 0; i < maxTagCnt; i++) { // XXX: should average by tag? for (int i = 0; i < maxTagCnt; i++) { // XXX: should average by tag?
final String tagTitle = tagTitles[i]; final String tagTitle = tagTitles[i];
final JSONObject tag = tagRepository.getByTitle(tagTitle); final JSONObject tag = tagRepository.getByTitle(tagTitle);
final String tagId = tag.getString(Keys.OBJECT_ID); final String tagId = tag.getString(Keys.OBJECT_ID);
final JSONObject result = tagArticleRepository.getByTagId(tagId, 1, displayCnt); final JSONObject result = tagArticleRepository.getByTagId(tagId, 1, displayCnt);
final JSONArray tagArticleRelations = result.getJSONArray(Keys.RESULTS); final JSONArray tagArticleRelations = result.getJSONArray(Keys.RESULTS);
final int relationSize = displayCnt < tagArticleRelations.length() ? displayCnt : tagArticleRelations.length(); final int relationSize = displayCnt < tagArticleRelations.length() ? displayCnt : tagArticleRelations.length();
for (int j = 0; j < relationSize; j++) { for (int j = 0; j < relationSize; j++) {
final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(j); final JSONObject tagArticleRelation = tagArticleRelations.getJSONObject(j);
final String relatedArticleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID); final String relatedArticleId = tagArticleRelation.getString(Article.ARTICLE + "_" + Keys.OBJECT_ID);
if (articleId.equals(relatedArticleId)) { if (articleId.equals(relatedArticleId)) {
continue; continue;
} }
final JSONObject relevant = articleRepository.get(relatedArticleId); final JSONObject relevant = articleRepository.get(relatedArticleId);
if (!relevant.getBoolean(Article.ARTICLE_IS_PUBLISHED)) { if (!relevant.getBoolean(Article.ARTICLE_IS_PUBLISHED)) {
continue; continue;
} }
boolean existed = false; boolean existed = false;
for (final JSONObject relevantArticle : articles) { for (final JSONObject relevantArticle : articles) {
if (relevantArticle.getString(Keys.OBJECT_ID). if (relevantArticle.getString(Keys.OBJECT_ID).
equals(relevant.getString(Keys.OBJECT_ID))) { equals(relevant.getString(Keys.OBJECT_ID))) {
existed = true; existed = true;
} }
} }
if (!existed) { if (!existed) {
articles.add(relevant); articles.add(relevant);
} }
} }
} }
Collections.sort(articles, Comparators.ARTICLE_UPDATE_DATE_COMPARATOR); Collections.sort(articles, Comparators.ARTICLE_UPDATE_DATE_COMPARATOR);
removeUnusedProperties(articles); removeUnusedProperties(articles);
if (displayCnt > articles.size()) { if (displayCnt > articles.size()) {
return articles; return articles;
} }
final List<Integer> randomIntegers = CollectionUtils.getRandomIntegers(0, articles.size() - 1, displayCnt); final List<Integer> randomIntegers = CollectionUtils.getRandomIntegers(0, articles.size() - 1, displayCnt);
final List<JSONObject> ret = new ArrayList<JSONObject>(); final List<JSONObject> ret = new ArrayList<JSONObject>();
for (final int index : randomIntegers) { for (final int index : randomIntegers) {
ret.add(articles.get(index)); ret.add(articles.get(index));
} }
return ret; return ret;
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Gets relevant articles failed", e); LOGGER.log(Level.SEVERE, "Gets relevant articles failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Determines an article specified by the given article id is published. * Determines an article specified by the given article id is published.
* *
* @param articleId the given article id * @param articleId the given article id
* @return {@code true} if it is published * @return {@code true} if it is published
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public boolean isArticlePublished(final String articleId) throws ServiceException { public boolean isArticlePublished(final String articleId) throws ServiceException {
try { try {
return articleRepository.isPublished(articleId); return articleRepository.isPublished(articleId);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Determines the article publish status failed[articleId=" + articleId + "]", e); LOGGER.log(Level.SEVERE, "Determines the article publish status failed[articleId=" + articleId + "]", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets the next article(by create date) by the specified article id. * Gets the next article(by create date) by the specified article id.
* *
* <p> * <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing). * <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p> * </p>
* *
* @param articleId the specified article id * @param articleId the specified article id
* @return the previous article, * @return the previous article,
* <pre> * <pre>
* { * {
* "articleTitle": "", * "articleTitle": "",
* "articlePermalink": "" * "articlePermalink": ""
* } * }
* </pre> * </pre>
* returns {@code null} if not found * returns {@code null} if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public JSONObject getNextArticle(final String articleId) throws ServiceException { public JSONObject getNextArticle(final String articleId) throws ServiceException {
try { try {
return articleRepository.getNextArticle(articleId); return articleRepository.getNextArticle(articleId);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Gets the next article failed[articleId=" + articleId + "]", e); LOGGER.log(Level.SEVERE, "Gets the next article failed[articleId=" + articleId + "]", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets the previous article(by create date) by the specified article id. * Gets the previous article(by create date) by the specified article id.
* *
* <p> * <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing). * <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p> * </p>
* *
* @param articleId the specified article id * @param articleId the specified article id
* @return the previous article, * @return the previous article,
* <pre> * <pre>
* { * {
* "articleTitle": "", * "articleTitle": "",
* "articlePermalink": "" * "articlePermalink": ""
* } * }
* </pre> * </pre>
* returns {@code null} if not found * returns {@code null} if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public JSONObject getPreviousArticle(final String articleId) throws ServiceException { public JSONObject getPreviousArticle(final String articleId) throws ServiceException {
try { try {
return articleRepository.getPreviousArticle(articleId); return articleRepository.getPreviousArticle(articleId);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Gets the previous article failed[articleId=" + articleId + "]", e); LOGGER.log(Level.SEVERE, "Gets the previous article failed[articleId=" + articleId + "]", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets an article by the specified article id. * Gets an article by the specified article id.
* *
* <p> * <p>
* <b>Note</b>: The article content and abstract is raw (no editor type processing). * <b>Note</b>: The article content and abstract is raw (no editor type processing).
* </p> * </p>
* *
* @param articleId the specified article id * @param articleId the specified article id
* @return an article, returns {@code null} if not found * @return an article, returns {@code null} if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public JSONObject getArticleById(final String articleId) throws ServiceException { public JSONObject getArticleById(final String articleId) throws ServiceException {
try { try {
return articleRepository.get(articleId); return articleRepository.get(articleId);
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Gets an article[articleId=" + articleId + "] failed", e); LOGGER.log(Level.SEVERE, "Gets an article[articleId=" + articleId + "] failed", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets <em>published</em> articles by the specified author email, current page number and page size. * Gets <em>published</em> articles by the specified author email, current page number and page size.
* *
* @param authorEmail the specified author email * @param authorEmail the specified author email
* @param currentPageNum the specified current page number * @param currentPageNum the specified current page number
* @param pageSize the specified page size * @param pageSize the specified page size
* @return a list of articles, returns an empty list if not found * @return a list of articles, returns an empty list if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public List<JSONObject> getArticlesByAuthorEmail(final String authorEmail, final int currentPageNum, final int pageSize) public List<JSONObject> getArticlesByAuthorEmail(final String authorEmail, final int currentPageNum, final int pageSize)
throws ServiceException { throws ServiceException {
try { try {
final JSONObject result = articleRepository.getByAuthorEmail(authorEmail, currentPageNum, pageSize); final JSONObject result = articleRepository.getByAuthorEmail(authorEmail, currentPageNum, pageSize);
final JSONArray articles = result.getJSONArray(Keys.RESULTS); final JSONArray articles = result.getJSONArray(Keys.RESULTS);
final List<JSONObject> ret = new ArrayList<JSONObject>(); final List<JSONObject> ret = new ArrayList<JSONObject>();
for (int i = 0; i < articles.length(); i++) { for (int i = 0; i < articles.length(); i++) {
final JSONObject article = articles.getJSONObject(i); final JSONObject article = articles.getJSONObject(i);
// Markdown to HTML for content and abstract // Markdown to HTML for content and abstract
markdown(article); markdown(article);
ret.add(article); ret.add(article);
} }
return ret; return ret;
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Gets articles by author email failed[authorEmail=" LOGGER.log(Level.SEVERE, "Gets articles by author email failed[authorEmail="
+ authorEmail + ", currentPageNum=" + currentPageNum + ", pageSize=" + pageSize + "]", e); + authorEmail + ", currentPageNum=" + currentPageNum + ", pageSize=" + pageSize + "]", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Gets article contents with the specified article id. * Gets article contents with the specified article id.
* *
* <p> * <p>
* Invoking this method dose not effect on article view count. * Invoking this method dose not effect on article view count.
* </p> * </p>
* *
* @param articleId the specified article id * @param articleId the specified article id
* @return article contents, returns {@code null} if not found * @return article contents, returns {@code null} if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public String getArticleContent(final String articleId) throws ServiceException { public String getArticleContent(final String articleId) throws ServiceException {
if (Strings.isEmptyOrNull(articleId)) { if (Strings.isEmptyOrNull(articleId)) {
return null; return null;
} }
try { try {
final JSONObject article = articleRepository.get(articleId); final JSONObject article = articleRepository.get(articleId);
if (null == article) { if (null == article) {
return null; return null;
} }
// Markdown to HTML for content and abstract // Markdown to HTML for content and abstract
if ("CodeMirror-Markdown".equals(article.optString(ARTICLE_EDITOR_TYPE))) { if ("CodeMirror-Markdown".equals(article.optString(ARTICLE_EDITOR_TYPE))) {
Stopwatchs.start("Get Article Content [Markdown]"); Stopwatchs.start("Get Article Content [Markdown]");
final String content = article.optString(ARTICLE_CONTENT); final String content = article.optString(ARTICLE_CONTENT);
article.put(ARTICLE_CONTENT, Markdowns.toHTML(content)); article.put(ARTICLE_CONTENT, Markdowns.toHTML(content));
Stopwatchs.end(); Stopwatchs.end();
} }
return article.getString(Article.ARTICLE_CONTENT); return article.getString(Article.ARTICLE_CONTENT);
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.SEVERE, "Gets article content failed[articleId=" + articleId + "]", e); LOGGER.log(Level.SEVERE, "Gets article content failed[articleId=" + articleId + "]", e);
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Converts the content and abstract for each of the specified articles to HTML if that is saved by Markdown editor. * Converts the content and abstract for each of the specified articles to HTML if that is saved by Markdown editor.
* *
* @param articles the specified articles * @param articles the specified articles
* @throws Exception exception * @throws Exception exception
*/ */
public void markdowns(final List<JSONObject> articles) throws Exception { public void markdowns(final List<JSONObject> articles) throws Exception {
for (final JSONObject article : articles) { for (final JSONObject article : articles) {
markdown(article); markdown(article);
} }
} }
/** /**
* Converts the content and abstract for the specified article to HTML if it is saved by Markdown editor. * Converts the content and abstract for the specified article to HTML if it is saved by Markdown editor.
* *
* @param article the specified article * @param article the specified article
* @throws Exception exception * @throws Exception exception
*/ */
public void markdown(final JSONObject article) throws Exception { public void markdown(final JSONObject article) throws Exception {
if ("CodeMirror-Markdown".equals(article.optString(ARTICLE_EDITOR_TYPE))) { if ("CodeMirror-Markdown".equals(article.optString(ARTICLE_EDITOR_TYPE))) {
Stopwatchs.start("Markdown Article[id=" + article.optString(Keys.OBJECT_ID) + "]"); Stopwatchs.start("Markdown Article[id=" + article.optString(Keys.OBJECT_ID) + "]");
Stopwatchs.start("Content"); Stopwatchs.start("Content");
final String content = article.optString(ARTICLE_CONTENT); final String content = article.optString(ARTICLE_CONTENT);
article.put(ARTICLE_CONTENT, Markdowns.toHTML(content)); article.put(ARTICLE_CONTENT, Markdowns.toHTML(content));
Stopwatchs.end(); Stopwatchs.end();
final String abstractContent = article.optString(ARTICLE_ABSTRACT); final String abstractContent = article.optString(ARTICLE_ABSTRACT);
if (!Strings.isEmptyOrNull(abstractContent)) { if (!Strings.isEmptyOrNull(abstractContent)) {
Stopwatchs.start("Abstract"); Stopwatchs.start("Abstract");
article.put(ARTICLE_ABSTRACT, Markdowns.toHTML(abstractContent)); article.put(ARTICLE_ABSTRACT, Markdowns.toHTML(abstractContent));
Stopwatchs.end(); Stopwatchs.end();
} }
Stopwatchs.end(); Stopwatchs.end();
} }
} }
/** /**
* Removes unused properties of each article in the specified articles. * Removes unused properties of each article in the specified articles.
* *
* <p> * <p>
* Remains the following properties: * Remains the following properties:
* <ul> * <ul>
* <li>{@link Article#ARTICLE_TITLE article title}</li> * <li>{@link Article#ARTICLE_TITLE article title}</li>
* <li>{@link Article#ARTICLE_PERMALINK article permalink}</li> * <li>{@link Article#ARTICLE_PERMALINK article permalink}</li>
* </ul> * </ul>
* </p> * </p>
* *
* <p> * <p>
* The batch version of method {@link #removeUnusedProperties(org.json.JSONObject)}. * The batch version of method {@link #removeUnusedProperties(org.json.JSONObject)}.
* </p> * </p>
* *
* @param articles the specified articles * @param articles the specified articles
* @see #removeUnusedProperties(org.json.JSONObject) * @see #removeUnusedProperties(org.json.JSONObject)
*/ */
public void removeUnusedProperties(final List<JSONObject> articles) { public void removeUnusedProperties(final List<JSONObject> articles) {
for (final JSONObject article : articles) { for (final JSONObject article : articles) {
removeUnusedProperties(article); removeUnusedProperties(article);
} }
} }
/** /**
* Removes unused properties of the specified article. * Removes unused properties of the specified article.
* *
* <p> * <p>
* Remains the following properties: * Remains the following properties:
* <ul> * <ul>
* <li>{@link Article#ARTICLE_TITLE article title}</li> * <li>{@link Article#ARTICLE_TITLE article title}</li>
* <li>{@link Article#ARTICLE_PERMALINK article permalink}</li> * <li>{@link Article#ARTICLE_PERMALINK article permalink}</li>
* </ul> * </ul>
* </p> * </p>
* *
* @param article the specified article * @param article the specified article
* @see #removeUnusedProperties(java.util.List) * @see #removeUnusedProperties(java.util.List)
*/ */
public void removeUnusedProperties(final JSONObject article) { public void removeUnusedProperties(final JSONObject article) {
article.remove(Keys.OBJECT_ID); article.remove(Keys.OBJECT_ID);
article.remove(Article.ARTICLE_AUTHOR_EMAIL); article.remove(Article.ARTICLE_AUTHOR_EMAIL);
article.remove(Article.ARTICLE_ABSTRACT); article.remove(Article.ARTICLE_ABSTRACT);
article.remove(Article.ARTICLE_COMMENT_COUNT); article.remove(Article.ARTICLE_COMMENT_COUNT);
article.remove(Article.ARTICLE_CONTENT); article.remove(Article.ARTICLE_CONTENT);
article.remove(Article.ARTICLE_CREATE_DATE); article.remove(Article.ARTICLE_CREATE_DATE);
article.remove(Article.ARTICLE_TAGS_REF); article.remove(Article.ARTICLE_TAGS_REF);
article.remove(Article.ARTICLE_UPDATE_DATE); article.remove(Article.ARTICLE_UPDATE_DATE);
article.remove(Article.ARTICLE_VIEW_COUNT); article.remove(Article.ARTICLE_VIEW_COUNT);
article.remove(Article.ARTICLE_RANDOM_DOUBLE); article.remove(Article.ARTICLE_RANDOM_DOUBLE);
article.remove(Article.ARTICLE_IS_PUBLISHED); article.remove(Article.ARTICLE_IS_PUBLISHED);
article.remove(Article.ARTICLE_PUT_TOP); article.remove(Article.ARTICLE_PUT_TOP);
article.remove(Article.ARTICLE_HAD_BEEN_PUBLISHED); article.remove(Article.ARTICLE_HAD_BEEN_PUBLISHED);
} }
/** /**
* Gets the {@link ArticleQueryService} singleton. * Gets the {@link ArticleQueryService} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static ArticleQueryService getInstance() { public static ArticleQueryService getInstance() {
return SingletonHolder.SINGLETON; return SingletonHolder.SINGLETON;
} }
/** /**
* Private constructor. * Private constructor.
*/ */
private ArticleQueryService() { private ArticleQueryService() {
} }
/** /**
* Singleton holder. * Singleton holder.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.0, Oct 3, 2011 * @version 1.0.0.0, Oct 3, 2011
*/ */
private static final class SingletonHolder { private static final class SingletonHolder {
/** /**
* Singleton. * Singleton.
*/ */
private static final ArticleQueryService SINGLETON = new ArticleQueryService(); private static final ArticleQueryService SINGLETON = new ArticleQueryService();
/** /**
* Private default constructor. * Private default constructor.
*/ */
private SingletonHolder() { private SingletonHolder() {
} }
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.util; package org.b3log.solo.util;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import org.b3log.solo.model.Article; import org.b3log.solo.model.Article;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.RepositoryException; import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.repository.SortDirection; import org.b3log.latke.repository.SortDirection;
import org.b3log.latke.service.ServiceException; import org.b3log.latke.service.ServiceException;
import org.b3log.latke.user.UserService; import org.b3log.latke.user.UserService;
import org.b3log.latke.user.UserServiceFactory; import org.b3log.latke.user.UserServiceFactory;
import org.b3log.latke.util.CollectionUtils; import org.b3log.latke.util.CollectionUtils;
import org.b3log.latke.util.Strings; import org.b3log.latke.util.Strings;
import org.b3log.solo.model.Common; import org.b3log.solo.model.Common;
import org.b3log.solo.model.Preference; import org.b3log.solo.model.Preference;
import org.b3log.solo.repository.ArticleRepository; import org.b3log.solo.repository.ArticleRepository;
import org.b3log.solo.repository.UserRepository; import org.b3log.solo.repository.UserRepository;
import org.b3log.solo.repository.impl.ArticleRepositoryImpl; import org.b3log.solo.repository.impl.ArticleRepositoryImpl;
import org.b3log.solo.repository.impl.UserRepositoryImpl; import org.b3log.solo.repository.impl.UserRepositoryImpl;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
/** /**
* Article utilities. * Article utilities.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.2.8, May 6, 2012 * @version 1.0.2.8, May 6, 2012
* @since 0.3.1 * @since 0.3.1
*/ */
public final class Articles { public final class Articles {
/** /**
* Logger. * Logger.
*/ */
private static final Logger LOGGER = Logger.getLogger(Articles.class.getName()); private static final Logger LOGGER = Logger.getLogger(Articles.class.getName());
/** /**
* Article repository. * Article repository.
*/ */
private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance(); private ArticleRepository articleRepository = ArticleRepositoryImpl.getInstance();
/** /**
* User repository. * User repository.
*/ */
private UserRepository userRepository = UserRepositoryImpl.getInstance(); private UserRepository userRepository = UserRepositoryImpl.getInstance();
/** /**
* User service. * User service.
*/ */
private UserService userService = UserServiceFactory.getUserService(); private UserService userService = UserServiceFactory.getUserService();
/** /**
* Builds article view password form parameters with the specified article. * Builds article view password form parameters with the specified article.
* *
* @param article the specified article * @param article the specified article
* @return parameters string, for example, * @return parameters string, for example,
* <pre> * <pre>
* "?articleId=xxx&articleTitle=xxx&articlePermalink=xxx&articleAbstract=xxx" * "?articleId=xxx&articleTitle=xxx&articlePermalink=xxx&articleAbstract=xxx"
* </pre> * </pre>
* @throws UnsupportedEncodingException if can not encode the arguments * @throws UnsupportedEncodingException if can not encode the arguments
*/ */
public String buildArticleViewPwdFormParameters(final JSONObject article) throws UnsupportedEncodingException { public String buildArticleViewPwdFormParameters(final JSONObject article) throws UnsupportedEncodingException {
final StringBuilder parametersBuilder = final StringBuilder parametersBuilder =
new StringBuilder("?articleId=").append(article.optString(Keys.OBJECT_ID)). new StringBuilder("?articleId=").append(article.optString(Keys.OBJECT_ID)).
append("&articleTitle=").append(URLEncoder.encode(article.optString(Article.ARTICLE_TITLE), "UTF-8")). append("&articleTitle=").append(URLEncoder.encode(article.optString(Article.ARTICLE_TITLE), "UTF-8")).
append("&articlePermalink=").append(URLEncoder.encode(article.optString(Article.ARTICLE_PERMALINK), "UTF-8")). append("&articlePermalink=").append(URLEncoder.encode(article.optString(Article.ARTICLE_PERMALINK), "UTF-8")).
append("&articleAbstract=").append(URLEncoder.encode(article.optString(Article.ARTICLE_ABSTRACT, " "), "UTF-8")); append("&articleAbstract=").append(URLEncoder.encode(article.optString(Article.ARTICLE_ABSTRACT, " "), "UTF-8"));
return parametersBuilder.toString(); return parametersBuilder.toString();
} }
/** /**
* Checks whether need password to view the specified article with the specified request. * Checks whether need password to view the specified article with the specified request.
* *
* <p> * <p>
* Checks session, if not represents, checks article property {@link Article#ARTICLE_VIEW_PWD view password}. * Checks session, if not represents, checks article property {@link Article#ARTICLE_VIEW_PWD view password}.
* </p> * </p>
* *
* <p> * <p>
* The blogger itself dose not need view password never. * The blogger itself dose not need view password never.
* </p> * </p>
* *
* @param request the specified request * @param request the specified request
* @param article the specified article * @param article the specified article
* @return {@code true} if need, returns {@code false} otherwise * @return {@code true} if need, returns {@code false} otherwise
*/ */
public boolean needViewPwd(final HttpServletRequest request, final JSONObject article) { public boolean needViewPwd(final HttpServletRequest request, final JSONObject article) {
final String articleViewPwd = article.optString(Article.ARTICLE_VIEW_PWD); final String articleViewPwd = article.optString(Article.ARTICLE_VIEW_PWD);
if (Strings.isEmptyOrNull(articleViewPwd)) { if (Strings.isEmptyOrNull(articleViewPwd)) {
return false; return false;
} }
final HttpSession session = request.getSession(false); final HttpSession session = request.getSession(false);
if (null != session) { if (null != session) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, String> viewPwds = (Map<String, String>) session.getAttribute(Common.ARTICLES_VIEW_PWD); Map<String, String> viewPwds = (Map<String, String>) session.getAttribute(Common.ARTICLES_VIEW_PWD);
if (null == viewPwds) { if (null == viewPwds) {
viewPwds = new HashMap<String, String>(); viewPwds = new HashMap<String, String>();
} }
if (articleViewPwd.equals(viewPwds.get(article.optString(Keys.OBJECT_ID)))) { if (articleViewPwd.equals(viewPwds.get(article.optString(Keys.OBJECT_ID)))) {
return false; return false;
} }
} }
if (null != userService.getCurrentUser(request)) { if (null != userService.getCurrentUser(request)) {
return false; return false;
} }
return true; return true;
} }
/** /**
* Gets the specified article's author. * Gets the specified article's author.
* *
* <p> * <p>
* The specified article has a property * The specified article has a property
* {@value Article#ARTICLE_AUTHOR_EMAIL}, this method will use this property * {@value Article#ARTICLE_AUTHOR_EMAIL}, this method will use this property
* to get a user from users. * to get a user from users.
* </p> * </p>
* *
* <p> * <p>
* If can't find the specified article's author (i.e. the author has been * If can't find the specified article's author (i.e. the author has been
* removed by administrator), returns administrator. * removed by administrator), returns administrator.
* </p> * </p>
* *
* @param article the specified article * @param article the specified article
* @return user, {@code null} if not found * @return user, {@code null} if not found
* @throws ServiceException service exception * @throws ServiceException service exception
*/ */
public JSONObject getAuthor(final JSONObject article) throws ServiceException { public JSONObject getAuthor(final JSONObject article) throws ServiceException {
try { try {
final String email = article.getString(Article.ARTICLE_AUTHOR_EMAIL); final String email = article.getString(Article.ARTICLE_AUTHOR_EMAIL);
JSONObject ret = userRepository.getByEmail(email); JSONObject ret = userRepository.getByEmail(email);
if (null == ret) { if (null == ret) {
LOGGER.log(Level.WARNING, LOGGER.log(Level.WARNING,
"Gets author of article failed, assumes the administrator is the author of this article[id={0}]", "Gets author of article failed, assumes the administrator is the author of this article[id={0}]",
article.getString(Keys.OBJECT_ID)); article.getString(Keys.OBJECT_ID));
// This author may be deleted by admin, use admin as the author // This author may be deleted by admin, use admin as the author
// of this article // of this article
ret = userRepository.getAdmin(); ret = userRepository.getAdmin();
} }
return ret; return ret;
} catch (final RepositoryException e) { } catch (final RepositoryException e) {
LOGGER.log(Level.SEVERE, "Gets author of article[id={0}] failed", article.optString(Keys.OBJECT_ID)); LOGGER.log(Level.SEVERE, "Gets author of article[id={0}] failed", article.optString(Keys.OBJECT_ID));
throw new ServiceException(e); throw new ServiceException(e);
} catch (final JSONException e) { } catch (final JSONException e) {
LOGGER.log(Level.SEVERE, "Gets author of article[id={0}] failed", article.optString(Keys.OBJECT_ID)); LOGGER.log(Level.SEVERE, "Gets author of article[id={0}] failed", article.optString(Keys.OBJECT_ID));
throw new ServiceException(e); throw new ServiceException(e);
} }
} }
/** /**
* Article comment count +1 for an article specified by the given article id. * Article comment count +1 for an article specified by the given article id.
* *
* @param articleId the given article id * @param articleId the given article id
* @throws JSONException json exception * @throws JSONException json exception
* @throws RepositoryException repository exception * @throws RepositoryException repository exception
*/ */
public void incArticleCommentCount(final String articleId) throws JSONException, RepositoryException { public void incArticleCommentCount(final String articleId) throws JSONException, RepositoryException {
final JSONObject article = articleRepository.get(articleId); final JSONObject article = articleRepository.get(articleId);
final JSONObject newArticle = new JSONObject(article, JSONObject.getNames(article)); final JSONObject newArticle = new JSONObject(article, JSONObject.getNames(article));
final int commentCnt = article.getInt(Article.ARTICLE_COMMENT_COUNT); final int commentCnt = article.getInt(Article.ARTICLE_COMMENT_COUNT);
newArticle.put(Article.ARTICLE_COMMENT_COUNT, commentCnt + 1); newArticle.put(Article.ARTICLE_COMMENT_COUNT, commentCnt + 1);
articleRepository.update(articleId, newArticle); articleRepository.update(articleId, newArticle);
} }
/** /**
* Gets the sign of an article specified by the sign id. * Gets the sign of an article specified by the sign id.
* *
* @param signId the specified article id * @param signId the specified article id
* @param preference the specified preference * @param preference the specified preference
* @return article sign, returns the default sign (which oId is "1") if not found * @return article sign, returns the default sign (which oId is "1") if not found
* @throws RepositoryException repository exception * @throws RepositoryException repository exception
* @throws JSONException json exception * @throws JSONException json exception
*/ */
public JSONObject getSign(final String signId, final JSONObject preference) throws JSONException, RepositoryException { public JSONObject getSign(final String signId, final JSONObject preference) throws JSONException, RepositoryException {
final JSONArray signs = new JSONArray(preference.getString(Preference.SIGNS)); final JSONArray signs = new JSONArray(preference.getString(Preference.SIGNS));
JSONObject defaultSign = null; JSONObject defaultSign = null;
for (int i = 0; i < signs.length(); i++) { for (int i = 0; i < signs.length(); i++) {
final JSONObject ret = signs.getJSONObject(i); final JSONObject ret = signs.getJSONObject(i);
if (signId.equals(ret.optString(Keys.OBJECT_ID))) { if (signId.equals(ret.optString(Keys.OBJECT_ID))) {
return ret; return ret;
} }
if ("1".equals(ret.optString(Keys.OBJECT_ID))) { if ("1".equals(ret.optString(Keys.OBJECT_ID))) {
defaultSign = ret; defaultSign = ret;
} }
} }
LOGGER.log(Level.WARNING, "Can not find the sign[id={0}], returns a default sign[id=1]", signId); LOGGER.log(Level.WARNING, "Can not find the sign[id={0}], returns a default sign[id=1]", signId);
if (null == defaultSign) { if (null == defaultSign) {
throw new IllegalStateException("Can not find the default sign which id equals to 1"); throw new IllegalStateException("Can not find the default sign which id equals to 1");
} }
return defaultSign; return defaultSign;
} }
/** /**
* Determines the specified article has updated. * Determines the specified article has updated.
* *
* @param article the specified article * @param article the specified article
* @return {@code true} if it has updated, {@code false} otherwise * @return {@code true} if it has updated, {@code false} otherwise
* @throws JSONException json exception * @throws JSONException json exception
*/ */
public boolean hasUpdated(final JSONObject article) throws JSONException { public boolean hasUpdated(final JSONObject article) throws JSONException {
final Date updateDate = (Date) article.get(Article.ARTICLE_UPDATE_DATE); final Date updateDate = (Date) article.get(Article.ARTICLE_UPDATE_DATE);
final Date createDate = (Date) article.get(Article.ARTICLE_CREATE_DATE); final Date createDate = (Date) article.get(Article.ARTICLE_CREATE_DATE);
return !createDate.equals(updateDate); return !createDate.equals(updateDate);
} }
/** /**
* Determines the specified article had been published. * Determines the specified article had been published.
* *
* @param article the specified article * @param article the specified article
* @return {@code true} if it had been published, {@code false} otherwise * @return {@code true} if it had been published, {@code false} otherwise
* @throws JSONException json exception * @throws JSONException json exception
*/ */
public boolean hadBeenPublished(final JSONObject article) throws JSONException { public boolean hadBeenPublished(final JSONObject article) throws JSONException {
return article.getBoolean(Article.ARTICLE_HAD_BEEN_PUBLISHED); return article.getBoolean(Article.ARTICLE_HAD_BEEN_PUBLISHED);
} }
/** /**
* Gets all unpublished articles. * Gets all unpublished articles.
* *
* @return articles all unpublished articles * @return articles all unpublished articles
* @throws RepositoryException repository exception * @throws RepositoryException repository exception
* @throws JSONException json exception * @throws JSONException json exception
*/ */
public List<JSONObject> getUnpublishedArticles() throws RepositoryException, JSONException { public List<JSONObject> getUnpublishedArticles() throws RepositoryException, JSONException {
final Map<String, SortDirection> sorts = new HashMap<String, SortDirection>(); final Map<String, SortDirection> sorts = new HashMap<String, SortDirection>();
sorts.put(Article.ARTICLE_CREATE_DATE, SortDirection.DESCENDING); sorts.put(Article.ARTICLE_CREATE_DATE, SortDirection.DESCENDING);
sorts.put(Article.ARTICLE_PUT_TOP, SortDirection.DESCENDING); sorts.put(Article.ARTICLE_PUT_TOP, SortDirection.DESCENDING);
final Query query = new Query().addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true); final Query query = new Query().addFilter(Article.ARTICLE_IS_PUBLISHED, FilterOperator.EQUAL, true);
final JSONObject result = articleRepository.get(query); final JSONObject result = articleRepository.get(query);
final JSONArray articles = result.getJSONArray(Keys.RESULTS); final JSONArray articles = result.getJSONArray(Keys.RESULTS);
return CollectionUtils.jsonArrayToList(articles); return CollectionUtils.jsonArrayToList(articles);
} }
/** /**
* Gets the {@link Articles} singleton. * Gets the {@link Articles} singleton.
* *
* @return the singleton * @return the singleton
*/ */
public static Articles getInstance() { public static Articles getInstance() {
return SingletonHolder.SINGLETON; return SingletonHolder.SINGLETON;
} }
/** /**
* Private default constructor. * Private default constructor.
*/ */
private Articles() { private Articles() {
} }
/** /**
* Singleton holder. * Singleton holder.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.0, Jan 12, 2011 * @version 1.0.0.0, Jan 12, 2011
*/ */
private static final class SingletonHolder { private static final class SingletonHolder {
/** /**
* Singleton. * Singleton.
*/ */
private static final Articles SINGLETON = new Articles(); private static final Articles SINGLETON = new Articles();
/** /**
* Private default constructor. * Private default constructor.
*/ */
private SingletonHolder() { private SingletonHolder() {
} }
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.b3log.solo.repository.impl; package org.b3log.solo.repository.impl;
import org.b3log.latke.Keys; import org.b3log.latke.Keys;
import org.b3log.latke.model.Role; import org.b3log.latke.model.Role;
import org.b3log.latke.model.User; import org.b3log.latke.model.User;
import org.b3log.latke.repository.FilterOperator; import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.Query; import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.Transaction; import org.b3log.latke.repository.Transaction;
import org.b3log.solo.AbstractTestCase; import org.b3log.solo.AbstractTestCase;
import org.b3log.solo.model.UserExt; import org.b3log.solo.model.UserExt;
import org.b3log.solo.repository.UserRepository; import org.b3log.solo.repository.UserRepository;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.Test; import org.testng.annotations.Test;
/** /**
* {@link UserRepositoryImpl} test case. * {@link UserRepositoryImpl} test case.
* *
* @author <a href="mailto:DL88250@gmail.com">Liang Ding</a> * @author <a href="mailto:DL88250@gmail.com">Liang Ding</a>
* @version 1.0.0.1, Feb 21, 2012 * @version 1.0.0.1, Feb 21, 2012
*/ */
@Test(suiteName = "repository") @Test(suiteName = "repository")
public final class UserRepositoryImplTestCase extends AbstractTestCase { public final class UserRepositoryImplTestCase extends AbstractTestCase {
/** /**
* Tests. * Tests.
* *
* @throws Exception exception * @throws Exception exception
*/ */
@Test @Test
public void test() throws Exception { public void test() throws Exception {
final UserRepository userRepository = getUserRepository(); final UserRepository userRepository = getUserRepository();
final JSONObject another = new JSONObject(); final JSONObject another = new JSONObject();
another.put(User.USER_NAME, "test1"); another.put(User.USER_NAME, "test1");
another.put(User.USER_EMAIL, "test1@gmail.com"); another.put(User.USER_EMAIL, "test1@gmail.com");
another.put(User.USER_PASSWORD, "pass1"); another.put(User.USER_PASSWORD, "pass1");
another.put(User.USER_ROLE, Role.DEFAULT_ROLE); another.put(User.USER_ROLE, Role.DEFAULT_ROLE);
another.put(UserExt.USER_ARTICLE_COUNT, 0); another.put(UserExt.USER_ARTICLE_COUNT, 0);
another.put(UserExt.USER_PUBLISHED_ARTICLE_COUNT, 0); another.put(UserExt.USER_PUBLISHED_ARTICLE_COUNT, 0);
Transaction transaction = userRepository.beginTransaction(); Transaction transaction = userRepository.beginTransaction();
userRepository.add(another); userRepository.add(another);
transaction.commit(); transaction.commit();
Assert.assertNull(userRepository.getAdmin()); Assert.assertNull(userRepository.getAdmin());
JSONObject admin = new JSONObject(); JSONObject admin = new JSONObject();
admin.put(User.USER_NAME, "test"); admin.put(User.USER_NAME, "test");
admin.put(User.USER_EMAIL, "test@gmail.com"); admin.put(User.USER_EMAIL, "test@gmail.com");
admin.put(User.USER_PASSWORD, "pass"); admin.put(User.USER_PASSWORD, "pass");
admin.put(User.USER_ROLE, Role.ADMIN_ROLE); admin.put(User.USER_ROLE, Role.ADMIN_ROLE);
admin.put(UserExt.USER_ARTICLE_COUNT, 0); admin.put(UserExt.USER_ARTICLE_COUNT, 0);
admin.put(UserExt.USER_PUBLISHED_ARTICLE_COUNT, 0); admin.put(UserExt.USER_PUBLISHED_ARTICLE_COUNT, 0);
transaction = userRepository.beginTransaction(); transaction = userRepository.beginTransaction();
userRepository.add(admin); userRepository.add(admin);
transaction.commit(); transaction.commit();
Assert.assertTrue(userRepository.isAdminEmail("test@gmail.com")); Assert.assertTrue(userRepository.isAdminEmail("test@gmail.com"));
Assert.assertFalse(userRepository.isAdminEmail("notFound@gmail.com")); Assert.assertFalse(userRepository.isAdminEmail("notFound@gmail.com"));
admin = userRepository.getAdmin(); admin = userRepository.getAdmin();
Assert.assertNotNull(admin); Assert.assertNotNull(admin);
Assert.assertEquals("test", admin.optString(User.USER_NAME)); Assert.assertEquals("test", admin.optString(User.USER_NAME));
final JSONObject result = final JSONObject result =
userRepository.get(new Query().addFilter(User.USER_NAME, userRepository.get(new Query().addFilter(User.USER_NAME,
FilterOperator.EQUAL, FilterOperator.EQUAL,
"test1")); "test1"));
final JSONArray users = result.getJSONArray(Keys.RESULTS); final JSONArray users = result.getJSONArray(Keys.RESULTS);
Assert.assertEquals(users.length(), 1); Assert.assertEquals(users.length(), 1);
Assert.assertEquals(users.getJSONObject(0).getString(User.USER_EMAIL), Assert.assertEquals(users.getJSONObject(0).getString(User.USER_EMAIL),
"test1@gmail.com"); "test1@gmail.com");
final JSONObject notFound = final JSONObject notFound =
userRepository.getByEmail("not.found@gmail.com"); userRepository.getByEmail("not.found@gmail.com");
Assert.assertNull(notFound); Assert.assertNull(notFound);
final JSONObject found = userRepository.getByEmail("test1@gmail.com"); final JSONObject found = userRepository.getByEmail("test1@gmail.com");
Assert.assertNotNull(found); Assert.assertNotNull(found);
Assert.assertEquals(found.getString(User.USER_PASSWORD), "pass1"); Assert.assertEquals(found.getString(User.USER_PASSWORD), "pass1");
} }
} }
/* /*
* Copyright (c) 2009, 2010, 2011, 2012, B3log Team * Copyright (c) 2009, 2010, 2011, 2012, B3log Team
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/* /*
* skin ease style * skin ease style
* *
* @author <a href="mailto:LLY219@gmail.com">Liyuan Li</a> * @author <a href="mailto:LLY219@gmail.com">Liyuan Li</a>
* @version 1.0.1.3, Jun 27, 2012 * @version 1.0.1.3, Jun 27, 2012
*/ */
/* start base */ /* start base */
html, body, div, ul, li { html, body, div, ul, li {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
body { body {
font-family: Verdana,arial,\5fae\8f6f\96c5\9ed1; font-family: Verdana,arial,\5fae\8f6f\96c5\9ed1;
font-size: 12px; font-size: 12px;
} }
a { a {
outline: none; outline: none;
color: #009EB8; color: #009EB8;
text-decoration: none; text-decoration: none;
} }
a:visited { a:visited {
color: #00b4d2; color: #00b4d2;
} }
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
} }
a:active { a:active {
color: #00889f; color: #00889f;
} }
img { img {
vertical-align: middle; vertical-align: middle;
border: 0; border: 0;
} }
textarea, input { textarea, input {
outline: none; outline: none;
} }
sup { sup {
font-size: 10px; font-size: 10px;
font-weight: normal; font-weight: normal;
-webkit-text-size-adjust: none; -webkit-text-size-adjust: none;
} }
.left { .left {
float: left; float: left;
} }
.right { .right {
float: right; float: right;
} }
.clear { .clear {
background-color: transparent; background-color: transparent;
border: 0; border: 0;
clear: both; clear: both;
display: block; display: block;
font-size: 0; font-size: 0;
height: 0; height: 0;
line-height: 0; line-height: 0;
overflow: hidden; overflow: hidden;
} }
.none { .none {
display: none; display: none;
} }
.ft-gray { .ft-gray {
color: #808080; color: #808080;
} }
.ft-gray:visited { .ft-gray:visited {
color: #9a9a9a color: #9a9a9a
} }
.ft-gray:hover { .ft-gray:hover {
color: #676767; color: #676767;
text-decoration: none; text-decoration: none;
} }
.logo { .logo {
padding: 0 5px; padding: 0 5px;
text-shadow: 0 0 1px #D5D5D5; text-shadow: 0 0 1px #D5D5D5;
} }
.em00, .em01, .em02, .em03, .em04, .em05, .em06, .em07, .em08, .em09, .em00, .em01, .em02, .em03, .em04, .em05, .em06, .em07, .em08, .em09,
.em10, .em11, .em12, .em13, .em14 { .em10, .em11, .em12, .em13, .em14 {
cursor: pointer; cursor: pointer;
background-image: url("../../ease/images/emotions/emotions-ease.png"); background-image: url("../../ease/images/emotions/emotions-ease.png");
float: left; float: left;
height: 24px; height: 24px;
margin-right: 5px; margin-right: 5px;
width: 24px; width: 24px;
transition: all .2s ease-out; transition: all .2s ease-out;
-webkit-transition: all .2s ease-out; -webkit-transition: all .2s ease-out;
-moz-transition: all .2s ease-out; -moz-transition: all .2s ease-out;
} }
#emotions span:hover { #emotions span:hover {
transform: scale(1.2) rotate(360deg); transform: scale(1.2) rotate(360deg);
-webkit-transform: scale(1.2) rotate(360deg); -webkit-transform: scale(1.2) rotate(360deg);
-moz-transform: scale(1.2) rotate(360deg); -moz-transform: scale(1.2) rotate(360deg);
} }
.em01 { .em01 {
background-position: -24px 0; background-position: -24px 0;
} }
.em02 { .em02 {
background-position: -48px 0; background-position: -48px 0;
} }
.em03 { .em03 {
background-position: -72px 0; background-position: -72px 0;
} }
.em04 { .em04 {
background-position: -96px 1px; background-position: -96px 1px;
} }
.em05 { .em05 {
background-position: 0 -24px; background-position: 0 -24px;
} }
.em06 { .em06 {
background-position: -24px -24px; background-position: -24px -24px;
} }
.em07 { .em07 {
background-position: -48px -24px; background-position: -48px -24px;
} }
.em08 { .em08 {
background-position: -72px -24px; background-position: -72px -24px;
} }
.em09 { .em09 {
background-position: -96px -24px; background-position: -96px -24px;
} }
.em10 { .em10 {
background-position: 0 -48px; background-position: 0 -48px;
} }
.em11 { .em11 {
background-position: -24px -48px ; background-position: -24px -48px ;
} }
.em12 { .em12 {
background-position: -48px -48px; background-position: -48px -48px;
} }
.em13 { .em13 {
background-position: -72px -48px; background-position: -72px -48px;
} }
.em14 { .em14 {
background-position: -96px -48px; background-position: -96px -48px;
} }
/* end base */ /* end base */
/* start ico */ /* start ico */
#search, #search,
.translate-ico { .translate-ico {
background-image: url("/skins/ease/images/icons.png"); background-image: url("/skins/ease/images/icons.png");
} }
.translate-ico { .translate-ico {
background-position: 1px -125px; background-position: 1px -125px;
background-repeat: no-repeat; background-repeat: no-repeat;
border: 1px solid #D5D5D5; border: 1px solid #D5D5D5;
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
float: right; float: right;
height: 16px; height: 16px;
margin: -3px 0 0 15px;; margin: -3px 0 0 15px;;
padding: 1px; padding: 1px;
width: 16px; width: 16px;
} }
.translate-ico:hover { .translate-ico:hover {
border-color: #9a9a9a; border-color: #9a9a9a;
box-shadow: 0 0 1px #808080; box-shadow: 0 0 1px #808080;
background-color: #FCFCFC; background-color: #FCFCFC;
} }
/* end ico */ /* end ico */
/* start frame */ /* start frame */
.wrapper { .wrapper {
margin: 0 50px; margin: 0 50px;
} }
.body { .body {
margin: 0 auto 50px; margin: 0 auto 50px;
width: 990px; width: 990px;
} }
/* end frame */ /* end frame */
/* start header */ /* start header */
.header { .header {
width: 100%; width: 100%;
background-color: #FCFCFC; background-color: #FCFCFC;
z-index: 1; z-index: 1;
} }
.header .title { .header .title {
border-bottom: 1px solid #808080; border-bottom: 1px solid #808080;
font-size: 26px; font-size: 26px;
font-weight: normal; font-weight: normal;
} }
.header .sub-title { .header .sub-title {
font-size: 11px; font-size: 11px;
} }
#search { #search {
background-position: 7px -99px; background-position: 7px -99px;
background-repeat: no-repeat; background-repeat: no-repeat;
background-color: #FFF; background-color: #FFF;
border: 1px solid #D5D5D5; border: 1px solid #D5D5D5;
border-radius: 2px 2px 2px 2px; border-radius: 2px 2px 2px 2px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1) inset; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1) inset;
color: #808080; color: #808080;
float: right; float: right;
font-size: 14px; font-size: 14px;
height: 19px; height: 19px;
line-height: 145%; line-height: 145%;
padding: 4px 10px 4px 28px; padding: 4px 10px 4px 28px;
width: 24px; width: 24px;
-moz-transition: width 0.4s ease, background 0.4s ease; -moz-transition: width 0.4s ease, background 0.4s ease;
-webkit-transition: width 0.4s ease, background 0.4s ease; -webkit-transition: width 0.4s ease, background 0.4s ease;
transition: width 0.4s ease, background 0.4s ease; transition: width 0.4s ease, background 0.4s ease;
} }
#search:focus { #search:focus {
background-color: #FCFCFC; background-color: #FCFCFC;
width: 196px; width: 196px;
} }
.banner { .banner {
margin-bottom: 20px; margin-bottom: 20px;
} }
.banner .notice { .banner .notice {
float: left; float: left;
margin: 20px 0 0 50px; margin: 20px 0 0 50px;
} }
.nav { .nav {
background-color: #2C2C2C; background-color: #2C2C2C;
box-shadow: 0 2px 2px #D5D5D5, 0 3px 3px -3px #D5D5D5 inset; box-shadow: 0 2px 2px #D5D5D5, 0 3px 3px -3px #D5D5D5 inset;
height: 29px; height: 29px;
top: 0; top: 0;
width: 100%; width: 100%;
} }
.nav ul { .nav ul {
list-style: none; list-style: none;
float: left; float: left;
} }
.nav li { .nav li {
float: left; float: left;
} }
.nav a { .nav a {
color: #C9C9C9; color: #C9C9C9;
display: block; display: block;
float: left; float: left;
font-size: 14px; font-size: 14px;
font-weight: 700; font-weight: 700;
line-height: 29px; line-height: 29px;
margin-right: 20px; margin-right: 20px;
padding: 0 10px; padding: 0 10px;
text-decoration: none; text-decoration: none;
transition: color .4s ease, background-color .8s ease; transition: color .4s ease, background-color .8s ease;
-webkit-transition: color .4s ease, background-color .8s ease; -webkit-transition: color .4s ease, background-color .8s ease;
-moz-transition: color .4s ease, background-color .8s ease; -moz-transition: color .4s ease, background-color .8s ease;
} }
.nav a:hover { .nav a:hover {
color: #FFF; color: #FFF;
background-color: #535353; background-color: #535353;
} }
.nav a.current { .nav a.current {
background-color: #FFF; background-color: #FFF;
box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.1) inset, 0 1px 1px rgba(0, 0, 0, 0.1) inset; box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.1) inset, 0 1px 1px rgba(0, 0, 0, 0.1) inset;
color: #808080; color: #808080;
} }
.nav img { .nav img {
margin-left: 3px; margin-left: 3px;
} }
/* end header */ /* end header */
/* start footer */ /* start footer */
.footer { .footer {
background-color: #FCFCFC; background-color: #FCFCFC;
border-top: 2px solid #D5D5D5; border-top: 2px solid #D5D5D5;
font-size: 11px; font-size: 11px;
padding: 12px 0; padding: 12px 0;
-webkit-text-size-adjust: none; -webkit-text-size-adjust: none;
} }
#goTop { #goTop {
background: url("/skins/ease/images/icons.png") no-repeat scroll 5px -51px #D5D5D5; background: url("/skins/ease/images/icons.png") no-repeat scroll 5px -51px #D5D5D5;
border-radius: 2px 2px 0 0; border-radius: 2px 2px 0 0;
cursor: pointer; cursor: pointer;
font-size: 11px; font-size: 11px;
height: 21px; height: 21px;
line-height: 21px; line-height: 21px;
padding: 0 10px 0 23px; padding: 0 10px 0 23px;
position: absolute; position: absolute;
right: 50px; right: 50px;
display: none; display: none;
} }
#goTop:hover { #goTop:hover {
background-color: #D5D5D5; background-color: #D5D5D5;
} }
/* end footer*/ /* end footer*/
/* start article list */ /* start article list */
.body > ul { .body > ul {
list-style: none; list-style: none;
} }
.article { .article {
border-bottom: 1px solid #f6f6f6; border-bottom: 1px solid #f6f6f6;
padding: 20px 70px 20px; padding: 20px 70px 20px;
} }
.article:hover { .article:hover {
border-bottom-color: #D5D5D5; border-bottom-color: #D5D5D5;
box-shadow: 0 0 1px #f6f6f6 inset; box-shadow: 0 0 1px #f6f6f6 inset;
background-color: #FCFCFC; background-color: #FCFCFC;
} }
.article-body { .article-body {
line-height: 145%; line-height: 145%;
overflow: hidden; overflow: hidden;
word-wrap: break-word; word-wrap: break-word;
} }
.article-body ol, .article-body ul { .article-body ol, .article-body ul {
margin-left: 40px; margin-left: 40px;
} }
.article-body a { .article-body a {
color: #808080; color: #808080;
text-decoration: underline; text-decoration: underline;
} }
.article-body a:vidited { .article-body a:vidited {
color: #9a9a9a color: #9a9a9a
} }
.article-body a:hover { .article-body a:hover {
color: #676767; color: #676767;
} }
.article-body>div { .article-body>div {
min-height: 32px; min-height: 32px;
} }
.article-title h2 { .article-title h2 {
display: inline; display: inline;
} }
.article-title span { .article-title span {
cursor: pointer; cursor: pointer;
} }
.article-next { .article-next {
background-color: #FCFCFC; background-color: #FCFCFC;
box-shadow: 0 0 3px #D5D5D5 inset; box-shadow: 0 0 3px #D5D5D5 inset;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
line-height: 36px; line-height: 36px;
text-align: center; text-align: center;
} }
.article-next:hover { .article-next:hover {
box-shadow: 0 0 3px #D5D5D5, 0 0 3px #D5D5D5 inset; box-shadow: 0 0 3px #D5D5D5, 0 0 3px #D5D5D5 inset;
} }
/* end article list */ /* end article list */
/* start article */ /* start article */
.article-relative { .article-relative {
background-color: #FCFCFC; background-color: #FCFCFC;
box-shadow: 0 0 2px #D5D5D5; box-shadow: 0 0 2px #D5D5D5;
float: left; float: left;
margin: 20px 0; margin: 20px 0;
padding: 0 20px 10px; padding: 0 20px 10px;
width: 365px; width: 365px;
} }
.article-relative:hover { .article-relative:hover {
background-color: #fff; background-color: #fff;
box-shadow: 0 0 4px #D5D5D5; box-shadow: 0 0 4px #D5D5D5;
} }
.article-relative ul { .article-relative ul {
list-style: none; list-style: none;
} }
#relevantArticles { #relevantArticles {
margin-right: 40px; margin-right: 40px;
} }
#externalRelevantArticles { #externalRelevantArticles {
width: 297px; width: 297px;
margin-top: 0; margin-top: 0;
} }
/* end article*/ /* end article*/
/* start comment */ /* start comment */
#comments { #comments {
padding: 20px 70px 20px; padding: 20px 70px 20px;
position: relative; position: relative;
} }
.comment-header { .comment-header {
float: left; float: left;
background-color: #FFF; background-color: #FFF;
border: 1px solid #DEDEDE; border: 1px solid #DEDEDE;
padding: 2px; padding: 2px;
height: 60px; height: 60px;
width: 60px; width: 60px;
} }
.comment-panel { .comment-panel {
float: left; float: left;
line-height: 16px; line-height: 16px;
margin: 0 10px 20px; margin: 0 10px 20px;
min-height: 64px; min-height: 64px;
overflow: hidden; overflow: hidden;
width: 760px; width: 760px;
} }
.comment-body-ref { .comment-body-ref {
background-color: #FCFCFC; background-color: #FCFCFC;
box-shadow: 0 0 2px #D5D5D5; box-shadow: 0 0 2px #D5D5D5;
display: block; display: block;
left: 140px; left: 140px;
padding: 10px; padding: 10px;
opacity: 0.9; opacity: 0.9;
filter: alpha(opacity=90); filter: alpha(opacity=90);
position: absolute; position: absolute;
} }
.comment-body-ref > .comment-header { .comment-body-ref > .comment-header {
border: 0 none; border: 0 none;
float: right; float: right;
height: 48px; height: 48px;
width: 48px; width: 48px;
padding: 0; padding: 0;
} }
.comment-body-ref > .comment-panel { .comment-body-ref > .comment-panel {
margin: 0 20px 0 0; margin: 0 20px 0 0;
min-height: 48px; min-height: 48px;
width: 756px; width: 756px;
} }
.form { .form {
padding: 0 70px 20px; padding: 0 70px 20px;
float: left; float: left;
} }
.form th { .form th {
text-align: right; text-align: right;
white-space: nowrap; white-space: nowrap;
} }
.form input[type="text"], .form input[type="text"],
.form input[type="password"], .form input[type="password"],
.form textarea { .form textarea {
background-color: #FCFCFC; background-color: #FCFCFC;
border-width: 0; border-width: 0;
box-shadow: 0 0 2px #BCBCBC; box-shadow: 0 0 2px #BCBCBC;
font-family: Verdana,arial,\5fae\8f6f\96c5\9ed1; font-family: Verdana,arial,\5fae\8f6f\96c5\9ed1;
font-size: 12px; font-size: 12px;
margin: 0 5px 10px 0; margin: 0 5px 10px 0;
outline: medium none; outline: medium none;
padding: 0 3px; padding: 0 3px;
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
width: 260px; width: 260px;
} }
.form input:focus, .form input:focus,
.form textarea:focus { .form textarea:focus {
box-shadow: 0 0 4px #D5D5D5; box-shadow: 0 0 4px #D5D5D5;
} }
.form textarea { .form textarea {
height: 120px; height: 120px;
overflow: auto; overflow: auto;
width: 426px; width: 426px;
line-height: 16px; line-height: 16px;
} }
.form button { .form button {
background-color: #F6F6F6; background-color: #F6F6F6;
border: 0 none; border: 0 none;
box-shadow: 0 0 2px #D5D5D5; box-shadow: 0 0 2px #D5D5D5;
height: 28px; height: 28px;
line-height: 28px; line-height: 28px;
padding: 0 12px; padding: 0 12px;
color: #A7A7A7; color: #A7A7A7;
} }
.form button:hover { .form button:hover {
box-shadow: 0 0 4px #D5D5D5; box-shadow: 0 0 4px #D5D5D5;
text-shadow: 0 0 2px; text-shadow: 0 0 2px;
} }
.form img { .form img {
background-color: #FCFCFC; background-color: #FCFCFC;
box-shadow: 0 0 2px #D5D5D5; box-shadow: 0 0 2px #D5D5D5;
cursor: pointer; cursor: pointer;
height: 24px; height: 24px;
padding: 1px; padding: 1px;
} }
/* end comment */ /* end comment */
/* start tags */ /* start tags */
#tags li { #tags li {
float: left; float: left;
height: 38px; height: 38px;
} }
#tags a { #tags a {
margin: 3px 6px; margin: 3px 6px;
padding: 3px 12px; padding: 3px 12px;
background-color: #FCFCFC; background-color: #FCFCFC;
box-shadow: 0 0 2px #D5D5D5; box-shadow: 0 0 2px #D5D5D5;
float: left; float: left;
} }
#tags a:hover { #tags a:hover {
text-shadow: 0 0 2px; text-shadow: 0 0 2px;
text-decoration: none; text-decoration: none;
box-shadow: 0 0 4px #D5D5D5; box-shadow: 0 0 4px #D5D5D5;
} }
.tags1, .tags1:visited { .tags1, .tags1:visited {
font-size: 12px; font-size: 12px;
color: #a7a7a7; color: #a7a7a7;
} }
.tags2, .tags2:visited { .tags2, .tags2:visited {
font-size: 14px; font-size: 14px;
color: #808080; color: #808080;
} }
.tags3, .tags3:visited { .tags3, .tags3:visited {
font-size: 16px; font-size: 16px;
color: #595959; color: #595959;
} }
.tags4, .tags4:visited { .tags4, .tags4:visited {
font-size: 18px; font-size: 18px;
color: #323232; color: #323232;
} }
.tags5, .tags5:visited { .tags5, .tags5:visited {
font-size: 20px; font-size: 20px;
color: #0a0a0a; color: #0a0a0a;
} }
/* end tags */ /* end tags */
/* start archives */ /* start archives */
.archives { .archives {
position: relative; position: relative;
} }
.archives > div { .archives > div {
background-color: #FCFCFC; background-color: #FCFCFC;
box-shadow: 0 0 2px #D5D5D5; box-shadow: 0 0 2px #D5D5D5;
padding-bottom: 20px; padding-bottom: 20px;
line-height: 20px; line-height: 20px;
margin: 20px; margin: 20px;
width: 276px; width: 276px;
position: absolute; position: absolute;
top: 0; top: 0;
} }
.archives h3 { .archives h3 {
text-align: center; text-align: center;
} }
.archives > div:hover { .archives > div:hover {
box-shadow: 0 0 4px #D5D5D5; box-shadow: 0 0 4px #D5D5D5;
} }
.archives a { .archives a {
display: block; display: block;
margin-left: 80px; margin-left: 80px;
} }
/* end archives */ /* end archives */
/* start dynamic */ /* start dynamic */
.dynamic ul { .dynamic ul {
list-style: none; list-style: none;
} }
.dynamic h3 { .dynamic h3 {
text-align: center; text-align: center;
} }
.dynamic .module { .dynamic .module {
background-color: #FCFCFC; background-color: #FCFCFC;
box-shadow: 0 0 2px #D5D5D5; box-shadow: 0 0 2px #D5D5D5;
float: left; float: left;
line-height: 20px; line-height: 20px;
margin: 20px; margin: 20px;
padding: 10px 20px 20px; padding: 10px 20px 20px;
width: 393px; width: 393px;
} }
.dynamic .module:hover { .dynamic .module:hover {
box-shadow: 0 0 4px #D5D5D5; box-shadow: 0 0 4px #D5D5D5;
} }
.dynamic .side-comments { .dynamic .side-comments {
width: 600px; width: 600px;
} }
.dynamic .side-comments .comment-panel { .dynamic .side-comments .comment-panel {
width: 510px; width: 510px;
} }
.dynamic .side-tags { .dynamic .side-tags {
width: 186px; width: 186px;
} }
.dynamic .side-tags li { .dynamic .side-tags li {
float: left; float: left;
margin: 0 5px; margin: 0 5px;
} }
/* end dynamic */ /* end dynamic */
/* start links */ /* start links */
.links li { .links li {
float: left; float: left;
margin: 0 20px 10px; margin: 0 20px 10px;
width: 273px; width: 273px;
} }
/* end links */ /* end links */
/* start not list */ /* start not list */
.other-main { .other-main {
display: inline-block; display: inline-block;
margin: 50px 20px 0; margin: 50px 20px 0;
width: 950px; width: 950px;
} }
/* end not list */ /* end not list */
\ No newline at end of file
html,body,div,ul,li{margin:0;padding:0;} html,body,div,ul,li{margin:0;padding:0;}
body{font-family:Verdana,arial,\5fae\8f6f\96c5\9ed1;font-size:12px;} body{font-family:Verdana,arial,\5fae\8f6f\96c5\9ed1;font-size:12px;}
a{outline:none;color:#009EB8;text-decoration:none;} a{outline:none;color:#009EB8;text-decoration:none;}
a:visited{color:#00b4d2;} a:visited{color:#00b4d2;}
a:hover{text-decoration:underline;} a:hover{text-decoration:underline;}
a:active{color:#00889f;} a:active{color:#00889f;}
img{vertical-align:middle;border:0;} img{vertical-align:middle;border:0;}
textarea,input{outline:none;} textarea,input{outline:none;}
sup{font-size:10px;font-weight:normal;-webkit-text-size-adjust:none;} sup{font-size:10px;font-weight:normal;-webkit-text-size-adjust:none;}
.left{float:left;} .left{float:left;}
.right{float:right;} .right{float:right;}
.clear{background-color:transparent;border:0;clear:both;display:block;font-size:0;height:0;line-height:0;overflow:hidden;} .clear{background-color:transparent;border:0;clear:both;display:block;font-size:0;height:0;line-height:0;overflow:hidden;}
.none{display:none;} .none{display:none;}
.ft-gray{color:#808080;} .ft-gray{color:#808080;}
.ft-gray:visited{color:#9a9a9a;} .ft-gray:visited{color:#9a9a9a;}
.ft-gray:hover{color:#676767;text-decoration:none;} .ft-gray:hover{color:#676767;text-decoration:none;}
.logo{padding:0 5px;text-shadow:0 0 1px #D5D5D5;} .logo{padding:0 5px;text-shadow:0 0 1px #D5D5D5;}
.em00,.em01,.em02,.em03,.em04,.em05,.em06,.em07,.em08,.em09,.em10,.em11,.em12,.em13,.em14{cursor:pointer;background-image:url(../../ease/images/emotions/emotions-ease.png);float:left;height:24px;margin-right:5px;width:24px;transition:all .2s ease-out;-webkit-transition:all .2s ease-out;-moz-transition:all .2s ease-out;} .em00,.em01,.em02,.em03,.em04,.em05,.em06,.em07,.em08,.em09,.em10,.em11,.em12,.em13,.em14{cursor:pointer;background-image:url(../../ease/images/emotions/emotions-ease.png);float:left;height:24px;margin-right:5px;width:24px;transition:all .2s ease-out;-webkit-transition:all .2s ease-out;-moz-transition:all .2s ease-out;}
#emotions span:hover{transform:scale(1.2) rotate(360deg);-webkit-transform:scale(1.2) rotate(360deg);-moz-transform:scale(1.2) rotate(360deg);} #emotions span:hover{transform:scale(1.2) rotate(360deg);-webkit-transform:scale(1.2) rotate(360deg);-moz-transform:scale(1.2) rotate(360deg);}
.em01{background-position:-24px 0;} .em01{background-position:-24px 0;}
.em02{background-position:-48px 0;} .em02{background-position:-48px 0;}
.em03{background-position:-72px 0;} .em03{background-position:-72px 0;}
.em04{background-position:-96px 1px;} .em04{background-position:-96px 1px;}
.em05{background-position:0 -24px;} .em05{background-position:0 -24px;}
.em06{background-position:-24px -24px;} .em06{background-position:-24px -24px;}
.em07{background-position:-48px -24px;} .em07{background-position:-48px -24px;}
.em08{background-position:-72px -24px;} .em08{background-position:-72px -24px;}
.em09{background-position:-96px -24px;} .em09{background-position:-96px -24px;}
.em10{background-position:0 -48px;} .em10{background-position:0 -48px;}
.em11{background-position:-24px -48px;} .em11{background-position:-24px -48px;}
.em12{background-position:-48px -48px;} .em12{background-position:-48px -48px;}
.em13{background-position:-72px -48px;} .em13{background-position:-72px -48px;}
.em14{background-position:-96px -48px;} .em14{background-position:-96px -48px;}
#search,.translate-ico{background-image:url(/skins/ease/images/icons.png);} #search,.translate-ico{background-image:url(/skins/ease/images/icons.png);}
.translate-ico{background-position:1px -125px;background-repeat:no-repeat;border:1px solid #D5D5D5;border-radius:3px;cursor:pointer;float:right;height:16px;margin:-3px 0 0 15px;padding:1px;width:16px;} .translate-ico{background-position:1px -125px;background-repeat:no-repeat;border:1px solid #D5D5D5;border-radius:3px;cursor:pointer;float:right;height:16px;margin:-3px 0 0 15px;padding:1px;width:16px;}
.translate-ico:hover{border-color:#9a9a9a;box-shadow:0 0 1px #808080;background-color:#FCFCFC;} .translate-ico:hover{border-color:#9a9a9a;box-shadow:0 0 1px #808080;background-color:#FCFCFC;}
.wrapper{margin:0 50px;} .wrapper{margin:0 50px;}
.body{margin:0 auto 50px;width:990px;} .body{margin:0 auto 50px;width:990px;}
.header{width:100%;background-color:#FCFCFC;z-index:1;} .header{width:100%;background-color:#FCFCFC;z-index:1;}
.header .title{border-bottom:1px solid #808080;font-size:26px;font-weight:normal;} .header .title{border-bottom:1px solid #808080;font-size:26px;font-weight:normal;}
.header .sub-title{font-size:11px;} .header .sub-title{font-size:11px;}
#search{background-position:7px -99px;background-repeat:no-repeat;background-color:#FFF;border:1px solid #D5D5D5;border-radius:2px 2px 2px 2px;box-shadow:0 1px 1px rgba(0,0,0,0.1) inset;color:#808080;float:right;font-size:14px;height:19px;line-height:145%;padding:4px 10px 4px 28px;width:24px;-moz-transition:width .4s ease, background .4s ease;-webkit-transition:width .4s ease, background .4s ease;transition:width .4s ease, background .4s ease;} #search{background-position:7px -99px;background-repeat:no-repeat;background-color:#FFF;border:1px solid #D5D5D5;border-radius:2px 2px 2px 2px;box-shadow:0 1px 1px rgba(0,0,0,0.1) inset;color:#808080;float:right;font-size:14px;height:19px;line-height:145%;padding:4px 10px 4px 28px;width:24px;-moz-transition:width .4s ease, background .4s ease;-webkit-transition:width .4s ease, background .4s ease;transition:width .4s ease, background .4s ease;}
#search:focus{background-color:#FCFCFC;width:196px;} #search:focus{background-color:#FCFCFC;width:196px;}
.banner{margin-bottom:20px;} .banner{margin-bottom:20px;}
.banner .notice{float:left;margin:20px 0 0 50px;} .banner .notice{float:left;margin:20px 0 0 50px;}
.nav{background-color:#2C2C2C;box-shadow:0 2px 2px #D5D5D5, 0 3px 3px -3px #D5D5D5 inset;height:29px;top:0;width:100%;} .nav{background-color:#2C2C2C;box-shadow:0 2px 2px #D5D5D5, 0 3px 3px -3px #D5D5D5 inset;height:29px;top:0;width:100%;}
.nav ul{list-style:none;float:left;} .nav ul{list-style:none;float:left;}
.nav li{float:left;} .nav li{float:left;}
.nav a{color:#C9C9C9;display:block;float:left;font-size:14px;font-weight:700;line-height:29px;margin-right:20px;padding:0 10px;text-decoration:none;transition:color .4s ease, background-color .8s ease;-webkit-transition:color .4s ease, background-color .8s ease;-moz-transition:color .4s ease, background-color .8s ease;} .nav a{color:#C9C9C9;display:block;float:left;font-size:14px;font-weight:700;line-height:29px;margin-right:20px;padding:0 10px;text-decoration:none;transition:color .4s ease, background-color .8s ease;-webkit-transition:color .4s ease, background-color .8s ease;-moz-transition:color .4s ease, background-color .8s ease;}
.nav a:hover{color:#FFF;background-color:#535353;} .nav a:hover{color:#FFF;background-color:#535353;}
.nav a.current{background-color:#FFF;box-shadow:0 -1px 1px rgba(0,0,0,0.1) inset, 0 1px 1px rgba(0,0,0,0.1) inset;color:#808080;} .nav a.current{background-color:#FFF;box-shadow:0 -1px 1px rgba(0,0,0,0.1) inset, 0 1px 1px rgba(0,0,0,0.1) inset;color:#808080;}
.nav img{margin-left:3px;} .nav img{margin-left:3px;}
.footer{background-color:#FCFCFC;border-top:2px solid #D5D5D5;font-size:11px;padding:12px 0;-webkit-text-size-adjust:none;} .footer{background-color:#FCFCFC;border-top:2px solid #D5D5D5;font-size:11px;padding:12px 0;-webkit-text-size-adjust:none;}
#goTop{background:url(/skins/ease/images/icons.png) no-repeat scroll 5px -51px #D5D5D5;border-radius:2px 2px 0 0;cursor:pointer;font-size:11px;height:21px;line-height:21px;padding:0 10px 0 23px;position:absolute;right:50px;display:none;} #goTop{background:url(/skins/ease/images/icons.png) no-repeat scroll 5px -51px #D5D5D5;border-radius:2px 2px 0 0;cursor:pointer;font-size:11px;height:21px;line-height:21px;padding:0 10px 0 23px;position:absolute;right:50px;display:none;}
#goTop:hover{background-color:#D5D5D5;} #goTop:hover{background-color:#D5D5D5;}
.body > ul{list-style:none;} .body > ul{list-style:none;}
.article{border-bottom:1px solid #f6f6f6;padding:20px 70px;} .article{border-bottom:1px solid #f6f6f6;padding:20px 70px;}
.article:hover{border-bottom-color:#D5D5D5;box-shadow:0 0 1px #f6f6f6 inset;background-color:#FCFCFC;} .article:hover{border-bottom-color:#D5D5D5;box-shadow:0 0 1px #f6f6f6 inset;background-color:#FCFCFC;}
.article-body{line-height:145%;overflow:hidden;word-wrap:break-word;} .article-body{line-height:145%;overflow:hidden;word-wrap:break-word;}
.article-body ol,.article-body ul{margin-left:40px;} .article-body ol,.article-body ul{margin-left:40px;}
.article-body a{color:#808080;text-decoration:underline;} .article-body a{color:#808080;text-decoration:underline;}
.article-body a:vidited{color:#9a9a9a;} .article-body a:vidited{color:#9a9a9a;}
.article-body a:hover{color:#676767;} .article-body a:hover{color:#676767;}
.article-body>div{min-height:32px;} .article-body>div{min-height:32px;}
.article-title h2{display:inline;} .article-title h2{display:inline;}
.article-title span{cursor:pointer;} .article-title span{cursor:pointer;}
.article-next{background-color:#FCFCFC;box-shadow:0 0 3px #D5D5D5 inset;cursor:pointer;font-size:14px;line-height:36px;text-align:center;} .article-next{background-color:#FCFCFC;box-shadow:0 0 3px #D5D5D5 inset;cursor:pointer;font-size:14px;line-height:36px;text-align:center;}
.article-next:hover{box-shadow:0 0 3px #D5D5D5, 0 0 3px #D5D5D5 inset;} .article-next:hover{box-shadow:0 0 3px #D5D5D5, 0 0 3px #D5D5D5 inset;}
.article-relative{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;float:left;margin:20px 0;padding:0 20px 10px;width:365px;} .article-relative{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;float:left;margin:20px 0;padding:0 20px 10px;width:365px;}
.article-relative:hover{background-color:#fff;box-shadow:0 0 4px #D5D5D5;} .article-relative:hover{background-color:#fff;box-shadow:0 0 4px #D5D5D5;}
.article-relative ul{list-style:none;} .article-relative ul{list-style:none;}
#relevantArticles{margin-right:40px;} #relevantArticles{margin-right:40px;}
#externalRelevantArticles{width:297px;margin-top:0;} #externalRelevantArticles{width:297px;margin-top:0;}
#comments{padding:20px 70px;position:relative;} #comments{padding:20px 70px;position:relative;}
.comment-header{float:left;background-color:#FFF;border:1px solid #DEDEDE;padding:2px;height:60px;width:60px;} .comment-header{float:left;background-color:#FFF;border:1px solid #DEDEDE;padding:2px;height:60px;width:60px;}
.comment-panel{float:left;line-height:16px;margin:0 10px 20px;min-height:64px;overflow:hidden;width:760px;} .comment-panel{float:left;line-height:16px;margin:0 10px 20px;min-height:64px;overflow:hidden;width:760px;}
.comment-body-ref{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;display:block;left:140px;padding:10px;opacity:0.9;filter:alpha(opacity=90);position:absolute;} .comment-body-ref{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;display:block;left:140px;padding:10px;opacity:0.9;filter:alpha(opacity=90);position:absolute;}
.comment-body-ref > .comment-header{border:0 none;float:right;height:48px;width:48px;padding:0;} .comment-body-ref > .comment-header{border:0 none;float:right;height:48px;width:48px;padding:0;}
.comment-body-ref > .comment-panel{margin:0 20px 0 0;min-height:48px;width:756px;} .comment-body-ref > .comment-panel{margin:0 20px 0 0;min-height:48px;width:756px;}
.form{padding:0 70px 20px;float:left;} .form{padding:0 70px 20px;float:left;}
.form th{text-align:right;white-space:nowrap;} .form th{text-align:right;white-space:nowrap;}
.form input[type=text],.form input[type=password],.form textarea{background-color:#FCFCFC;border-width:0;box-shadow:0 0 2px #BCBCBC;font-family:Verdana,arial,\5fae\8f6f\96c5\9ed1;font-size:12px;margin:0 5px 10px 0;outline:medium none;padding:0 3px;height:28px;line-height:28px;width:260px;} .form input[type=text],.form input[type=password],.form textarea{background-color:#FCFCFC;border-width:0;box-shadow:0 0 2px #BCBCBC;font-family:Verdana,arial,\5fae\8f6f\96c5\9ed1;font-size:12px;margin:0 5px 10px 0;outline:medium none;padding:0 3px;height:28px;line-height:28px;width:260px;}
.form input:focus,.form textarea:focus{box-shadow:0 0 4px #D5D5D5;} .form input:focus,.form textarea:focus{box-shadow:0 0 4px #D5D5D5;}
.form textarea{height:120px;overflow:auto;width:426px;line-height:16px;} .form textarea{height:120px;overflow:auto;width:426px;line-height:16px;}
.form button{background-color:#F6F6F6;border:0 none;box-shadow:0 0 2px #D5D5D5;height:28px;line-height:28px;padding:0 12px;color:#A7A7A7;} .form button{background-color:#F6F6F6;border:0 none;box-shadow:0 0 2px #D5D5D5;height:28px;line-height:28px;padding:0 12px;color:#A7A7A7;}
.form button:hover{box-shadow:0 0 4px #D5D5D5;text-shadow:0 0 2px;} .form button:hover{box-shadow:0 0 4px #D5D5D5;text-shadow:0 0 2px;}
.form img{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;cursor:pointer;height:24px;padding:1px;} .form img{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;cursor:pointer;height:24px;padding:1px;}
#tags li{float:left;height:38px;} #tags li{float:left;height:38px;}
#tags a{margin:3px 6px;padding:3px 12px;background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;float:left;} #tags a{margin:3px 6px;padding:3px 12px;background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;float:left;}
#tags a:hover{text-shadow:0 0 2px;text-decoration:none;box-shadow:0 0 4px #D5D5D5;} #tags a:hover{text-shadow:0 0 2px;text-decoration:none;box-shadow:0 0 4px #D5D5D5;}
.tags1,.tags1:visited{font-size:12px;color:#a7a7a7;} .tags1,.tags1:visited{font-size:12px;color:#a7a7a7;}
.tags2,.tags2:visited{font-size:14px;color:#808080;} .tags2,.tags2:visited{font-size:14px;color:#808080;}
.tags3,.tags3:visited{font-size:16px;color:#595959;} .tags3,.tags3:visited{font-size:16px;color:#595959;}
.tags4,.tags4:visited{font-size:18px;color:#323232;} .tags4,.tags4:visited{font-size:18px;color:#323232;}
.tags5,.tags5:visited{font-size:20px;color:#0a0a0a;} .tags5,.tags5:visited{font-size:20px;color:#0a0a0a;}
.archives{position:relative;} .archives{position:relative;}
.archives > div{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;padding-bottom:20px;line-height:20px;margin:20px;width:276px;position:absolute;top:0;} .archives > div{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;padding-bottom:20px;line-height:20px;margin:20px;width:276px;position:absolute;top:0;}
.archives h3{text-align:center;} .archives h3{text-align:center;}
.archives > div:hover{box-shadow:0 0 4px #D5D5D5;} .archives > div:hover{box-shadow:0 0 4px #D5D5D5;}
.archives a{display:block;margin-left:80px;} .archives a{display:block;margin-left:80px;}
.dynamic ul{list-style:none;} .dynamic ul{list-style:none;}
.dynamic h3{text-align:center;} .dynamic h3{text-align:center;}
.dynamic .module{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;float:left;line-height:20px;margin:20px;padding:10px 20px 20px;width:393px;} .dynamic .module{background-color:#FCFCFC;box-shadow:0 0 2px #D5D5D5;float:left;line-height:20px;margin:20px;padding:10px 20px 20px;width:393px;}
.dynamic .module:hover{box-shadow:0 0 4px #D5D5D5;} .dynamic .module:hover{box-shadow:0 0 4px #D5D5D5;}
.dynamic .side-comments{width:600px;} .dynamic .side-comments{width:600px;}
.dynamic .side-comments .comment-panel{width:510px;} .dynamic .side-comments .comment-panel{width:510px;}
.dynamic .side-tags{width:186px;} .dynamic .side-tags{width:186px;}
.dynamic .side-tags li{float:left;margin:0 5px;} .dynamic .side-tags li{float:left;margin:0 5px;}
.links li{float:left;margin:0 20px 10px;width:273px;} .links li{float:left;margin:0 20px 10px;width:273px;}
.other-main{display:inline-block;margin:50px 20px 0;width:950px;} .other-main{display:inline-block;margin:50px 20px 0;width:950px;}
\ No newline at end of file
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