/*
* Symphony - A modern community (forum/SNS/blog) platform written in Java.
* Copyright (C) 2012-2017, b3log.org & hacpai.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.b3log.symphony.service;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import jodd.io.ZipUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.b3log.latke.Keys;
import org.b3log.latke.Latkes;
import org.b3log.latke.ioc.inject.Inject;
import org.b3log.latke.logging.Level;
import org.b3log.latke.logging.Logger;
import org.b3log.latke.repository.FilterOperator;
import org.b3log.latke.repository.PropertyFilter;
import org.b3log.latke.repository.Query;
import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.service.annotation.Service;
import org.b3log.symphony.model.Article;
import org.b3log.symphony.model.Comment;
import org.b3log.symphony.model.Pointtransfer;
import org.b3log.symphony.model.UserExt;
import org.b3log.symphony.repository.ArticleRepository;
import org.b3log.symphony.repository.CommentRepository;
import org.b3log.symphony.repository.UserRepository;
import org.b3log.symphony.util.Symphonys;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.UUID;
/**
* Post (article/comment) export service.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.1, Jul 21, 2016
* @since 1.4.0
*/
@Service
public class PostExportService {
/**
* Logger.
*/
private static final Logger LOGGER = Logger.getLogger(PostExportService.class.getName());
/**
* User repository.
*/
@Inject
private UserRepository userRepository;
/**
* Article repository.
*/
@Inject
private ArticleRepository articleRepository;
/**
* Comment repository.
*/
@Inject
private CommentRepository commentRepository;
/**
* Pointtransfer management service.
*/
@Inject
private PointtransferMgmtService pointtransferMgmtService;
/**
* Exports all posts of a user's specified with the given user id.
*
* @param userId the given user id
* @return download URL, returns {@code "-1"} if in sufficient balance, returns {@code null} if other exceptions
*/
public String exportPosts(final String userId) {
final int pointDataExport = Symphonys.getInt("pointDataExport");
try {
final JSONObject user = userRepository.get(userId);
final int balance = user.optInt(UserExt.USER_POINT);
if (balance - pointDataExport < 0) {
return "-1";
}
} catch (final RepositoryException e) {
LOGGER.log(Level.ERROR, "Checks user failed", e);
return null;
}
final JSONArray posts = new JSONArray();
Query query = new Query().setFilter(
new PropertyFilter(Article.ARTICLE_AUTHOR_ID, FilterOperator.EQUAL, userId)).
addProjection(Keys.OBJECT_ID, String.class).
addProjection(Article.ARTICLE_TITLE, String.class).
addProjection(Article.ARTICLE_TAGS, String.class).
addProjection(Article.ARTICLE_CONTENT, String.class).
addProjection(Article.ARTICLE_CREATE_TIME, Long.class);
try {
final JSONArray articles = articleRepository.get(query).optJSONArray(Keys.RESULTS);
for (int i = 0; i < articles.length(); i++) {
final JSONObject article = articles.getJSONObject(i);
final JSONObject post = new JSONObject();
post.put("id", article.optString(Keys.OBJECT_ID));
final JSONObject content = new JSONObject();
content.put("title", article.optString(Article.ARTICLE_TITLE));
content.put("tags", article.optString(Article.ARTICLE_TAGS));
content.put("body", article.optString(Article.ARTICLE_CONTENT));
post.put("content", content.toString());
post.put("created", Article.ARTICLE_CREATE_TIME);
post.put("type", "article");
posts.put(post);
}
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "Export articles failed", e);
return null;
}
query = new Query().setFilter(
new PropertyFilter(Comment.COMMENT_AUTHOR_ID, FilterOperator.EQUAL, userId)).
addProjection(Keys.OBJECT_ID, String.class).
addProjection(Comment.COMMENT_CONTENT, String.class).
addProjection(Comment.COMMENT_CREATE_TIME, Long.class);
try {
final JSONArray comments = commentRepository.get(query).optJSONArray(Keys.RESULTS);
for (int i = 0; i < comments.length(); i++) {
final JSONObject comment = comments.getJSONObject(i);
final JSONObject post = new JSONObject();
post.put("id", comment.optString(Keys.OBJECT_ID));
final JSONObject content = new JSONObject();
content.put("title", "");
content.put("tags", "");
content.put("body", comment.optString(Comment.COMMENT_CONTENT));
post.put("content", content.toString());
post.put("created", Comment.COMMENT_CREATE_TIME);
post.put("type", "comment");
posts.put(post);
}
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "Export comments failed", e);
return null;
}
LOGGER.info("Exporting posts [size=" + posts.length() + "]");
final boolean succ = null != pointtransferMgmtService.transfer(userId, Pointtransfer.ID_C_SYS,
Pointtransfer.TRANSFER_TYPE_C_DATA_EXPORT, Pointtransfer.TRANSFER_SUM_C_DATA_EXPORT,
String.valueOf(posts.length()), System.currentTimeMillis());
if (!succ) {
return null;
}
final String uuid = UUID.randomUUID().toString().replaceAll("-", "");
String fileKey = "export/" + userId + "/" + uuid + ".zip";
final String tmpDir = System.getProperty("java.io.tmpdir");
String localFilePath = tmpDir + "/" + uuid + ".json";
LOGGER.info(localFilePath);
final File localFile = new File(localFilePath);
try {
final byte[] data = posts.toString(2).getBytes("UTF-8");
OutputStream output = new FileOutputStream(localFile);
IOUtils.write(data, output);
IOUtils.closeQuietly(output);
final File zipFile = ZipUtil.zip(localFile);
final FileInputStream inputStream = new FileInputStream(zipFile);
final byte[] zipData = IOUtils.toByteArray(inputStream);
if (Symphonys.getBoolean("qiniu.enabled")) {
final Auth auth = Auth.create(Symphonys.get("qiniu.accessKey"), Symphonys.get("qiniu.secretKey"));
final UploadManager uploadManager = new UploadManager();
uploadManager.put(zipData, fileKey, auth.uploadToken(Symphonys.get("qiniu.bucket")),
null, "application/zip", false);
return Symphonys.get("qiniu.domain") + "/" + fileKey;
} else {
final String filePath = Symphonys.get("upload.dir") + fileKey;
FileUtils.copyFile(zipFile, new File(filePath));
return Latkes.getServePath() + "/upload/" + fileKey;
}
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "Uploading exprted data failed", e);
return null;
}
}
}