Skip to content
This repository has been archived by the owner on Sep 12, 2018. It is now read-only.

Commit

Permalink
Merge branch fix/unintentionallyAttachingFiles of doortts/yobi
Browse files Browse the repository at this point in the history
from pull request 568
  • Loading branch information
채수원 committed Feb 13, 2014
2 parents 882d203 + 396857e commit 9972fc6
Show file tree
Hide file tree
Showing 13 changed files with 204 additions and 18 deletions.
59 changes: 59 additions & 0 deletions app/Global.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,19 @@
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import controllers.AttachmentApp;
import models.*;

import com.avaje.ebean.Ebean;

import controllers.UserApp;
import controllers.routes;
import models.enumeration.ResourceType;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.cookie.DateUtils;
import play.Application;
Expand All @@ -45,14 +49,17 @@
import play.Play;
import play.api.mvc.Handler;
import play.data.Form;
import play.libs.Akka;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Http.RequestHeader;
import play.mvc.Result;
import play.mvc.Results;

import scala.concurrent.duration.Duration;
import utils.AccessLogger;
import utils.ErrorViews;
import utils.JodaDateUtil;
import utils.YamlUtil;

import views.html.welcome.secret;
Expand Down Expand Up @@ -110,6 +117,58 @@ public void onStart(Application app) {
}

NotificationEvent.scheduleDeleteOldNotifications();
cleanupTemporaryUploadFilesWithSchedule();
}

/**
* Remove all of temporary files uploaded by users
*/
private void cleanupTemporaryUploadFilesWithSchedule() {
Akka.system().scheduler().schedule(
Duration.create(0, TimeUnit.SECONDS),
Duration.create(AttachmentApp.TEMPORARYFILES_KEEPUP_TIME_MILLIS, TimeUnit.MILLISECONDS),
new Runnable() {
@Override
public void run() {
try {
String result = removeUserTemporaryFiles();
play.Logger.info("User uploaded temporary files are cleaned up..." + result);
} catch (Exception e) {
play.Logger.warn("Failed!! User uploaded temporary files clean-up action failed!", e);
}
}

/**
* 사용자 임시 첨부 파일을 삭제
*
* 사용자에 의해 업로드 된지 {@code application.temporaryfiles.keep-up.time}초 이상 경과한
* 임시파일들은 서버에서 삭제한다.
*
* @return 전체 대상 파일 중 지운 파일 ex> (10 of 10)
*/
private String removeUserTemporaryFiles() {
List<Attachment> attachmentList = Attachment.find.where()
.eq("containerType", ResourceType.USER)
.ge("createdDate", JodaDateUtil.beforeByMillis(AttachmentApp.TEMPORARYFILES_KEEPUP_TIME_MILLIS))
.findList();
int deletedFileCount = 0;
for (Attachment attachment : attachmentList) {
attachment.delete();
deletedFileCount++;
}
if( attachmentList.size() != deletedFileCount) {
play.Logger.error(
String.format("Failed to delete user temporary files.\nExpected: %d Actual: %d",
attachmentList.size(), deletedFileCount)
);
}
return "(" + attachmentList.size() + "of" + deletedFileCount + ")";
}
},
Akka.system().dispatcher()
);


}

private boolean notificationEnabled() {
Expand Down
39 changes: 37 additions & 2 deletions app/controllers/AbstractPostingApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import models.enumeration.Operation;
import models.resource.Resource;

import org.apache.commons.lang3.StringUtils;
import play.data.Form;
import play.db.ebean.Model;
import play.i18n.Messages;
import play.mvc.Call;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;
import utils.*;

Expand Down Expand Up @@ -65,7 +68,7 @@ public static Result newComment(final Comment comment, Form<? extends Comment> c
comment.save();

// Attach all of the files in the current user's temporary storage.
Attachment.moveAll(UserApp.currentUser().asResource(), comment.asResource());
attachUploadFilesToPost(comment.asResource());

String urlToView = toView + "#comment-" + comment.id;
NotificationEvent.afterNewComment(comment, urlToView);
Expand Down Expand Up @@ -141,9 +144,41 @@ protected static Result editPosting(AbstractPosting original, AbstractPosting po
posting.updateProperties();

// Attach the files in the current user's temporary storage.
Attachment.moveAll(UserApp.currentUser().asResource(), original.asResource());
attachUploadFilesToPost(original.asResource());

return redirect(redirectTo);
}

/**
* 특정 리소스(게시글이나 댓글)에 사용자가 이전 폼에서 업로드한 임시파일을 첨부시킨다
*
* when: 이슈등록/수정, 게시판에 글 등록/수정, 댓글쓰기 등에서 업로드한 파일을 해당 리소스에 연결할 때
*
* {code AttachmentApp.TAG_NAME_FOR_TEMPORARY_UPLOAD_FILES}에 지정된 이름의 input tag에
* 업로드한 임시파일의 file id 값이 ,(comma) separator로 구분되어 들어가 있다.
*
* @param resource 이슈글,게시판글,댓글
*/
protected static void attachUploadFilesToPost(Resource resource) {
final String[] temporaryUploadFiles = getTemporaryFileListFromHiddenForm();
if(StringUtils.isNotBlank(temporaryUploadFiles[0])){
int attachedFileCount = Attachment.moveOnlySelected(UserApp.currentUser().asResource(), resource,
temporaryUploadFiles);
if( attachedFileCount != temporaryUploadFiles.length){
flash("failed", Messages.get("post.popup.fileAttach.hasMissing",
temporaryUploadFiles.length - attachedFileCount, getTemporaryFilesServerKeepUpTimeOfMinuntes()));
}
}
}

private static long getTemporaryFilesServerKeepUpTimeOfMinuntes() {
return AttachmentApp.TEMPORARYFILES_KEEPUP_TIME_MILLIS/(60*1000l);
}

private static String[] getTemporaryFileListFromHiddenForm() {
Http.MultipartFormData body = request().body().asMultipartFormData();
final String CSV_DELEMETER = ",";
return body.asFormUrlEncoded()
.get(AttachmentApp.TAG_NAME_FOR_TEMPORARY_UPLOAD_FILES)[0].split(CSV_DELEMETER);
}
}
13 changes: 6 additions & 7 deletions app/controllers/AttachmentApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.codehaus.jackson.JsonNode;

import org.h2.util.StringUtils;
import play.Configuration;
import play.Logger;
import play.mvc.Controller;
import play.mvc.Http.MultipartFormData.FilePart;
Expand All @@ -27,6 +28,11 @@

public class AttachmentApp extends Controller {

public static final String TAG_NAME_FOR_TEMPORARY_UPLOAD_FILES = "temporaryUploadFiles";
//사용자 임시 첨부 파일의 서버내 보관시간. 임시파일은 글 작성시 파일은 업로드 한 파일 중 글 작성이 완료 되지 않은 상태의 파일을 말한다.
public static final long TEMPORARYFILES_KEEPUP_TIME_MILLIS = Configuration.root()
.getMilliseconds("application.temporaryfiles.keep-up.time", 24 * 60 * 60 * 1000L);

/**
* 사용자 첨부파일로 업로드한다
*
Expand Down Expand Up @@ -256,13 +262,6 @@ public static Result getFileList() {
Map<String, List<Map<String, String>>> files =
new HashMap<>();

// Get files from the user's area.
List<Map<String, String>> userFiles = new ArrayList<>();
for (Attachment attach : Attachment.findByContainer(UserApp.currentUser().asResource())) {
userFiles.add(extractFileMetaDataFromAttachementAsMap(attach));
}
files.put("tempFiles", userFiles);

// Get attached files only if the user has permission to read it.
Map<String, String[]> query = request().queryString();
String containerType = HttpUtil.getFirstValueFromQuery(query, "containerType");
Expand Down
3 changes: 1 addition & 2 deletions app/controllers/BoardApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ public static Result newPost(String userName, String projectName) {

post.save();

// Attach all of the files in the current user's temporary storage.
Attachment.moveAll(UserApp.currentUser().asResource(), post.asResource());
attachUploadFilesToPost(post.asResource());

NotificationEvent.afterNewPost(post);

Expand Down
8 changes: 4 additions & 4 deletions app/controllers/IssueApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
import models.enumeration.Operation;
import models.enumeration.ResourceType;
import models.enumeration.State;
import org.apache.commons.lang.StringUtils;
import models.resource.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.tika.Tika;
import org.codehaus.jackson.node.ObjectNode;
import play.data.Form;
Expand Down Expand Up @@ -527,8 +528,7 @@ public static Result newIssue(String ownerName, String projectName) {

newIssue.save();

// Attach all of the files in the current user's temporary storage.
Attachment.moveAll(UserApp.currentUser().asResource(), newIssue.asResource());
attachUploadFilesToPost(newIssue.asResource());

NotificationEvent.afterNewIssue(newIssue);

Expand Down Expand Up @@ -599,7 +599,7 @@ public static Result nextState(String ownerName, String projectName, Long number
* @param number 이슈 번호
* @return
* @throws IOException
* @see {@link AbstractPostingApp#editPosting(models.AbstractPosting, models.AbstractPosting, play.data.Form}
* @see {@link AbstractPostingApp#editPosting}
*/
@With(NullProjectCheckAction.class)
public static Result editIssue(String ownerName, String projectName, Long number) {
Expand Down
32 changes: 30 additions & 2 deletions app/models/Attachment.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Date;
import java.util.Formatter;
import java.util.List;

Expand All @@ -28,11 +30,13 @@
import play.db.ebean.Model;
import scalax.file.NotDirectoryException;
import utils.FileUtil;
import utils.JodaDateUtil;

@Entity
public class Attachment extends Model implements ResourceConvertible {
private static final long serialVersionUID = 7856282252495067924L;
public static final Finder<Long, Attachment> find = new Finder<>(Long.class, Attachment.class);
public static final int NOTHING_TO_ATTACH = 0;
private static String uploadDirectory = "uploads";
@Id
public Long id;
Expand All @@ -47,11 +51,11 @@ public class Attachment extends Model implements ResourceConvertible {
public ResourceType containerType;

public String mimeType;

public Long size;

public String containerId;

private Date createdDate;

/**
* 주어진 {@code attach}와 내용이 같은 첨부 파일을 찾는다.
*
Expand Down Expand Up @@ -138,6 +142,29 @@ public static int moveAll(Resource from, Resource to) {
return attachments.size();
}

/**
* {@code from}에 첨부된 파일중 파일 아이디가{@code selectedFileIds}에 해당하는 첨부 파일을 {@code to}로 옮긴다.
*
* when: 업로드 직후 일시적으로 사용자에게 첨부되었던 첨부 파일들을, 특정 리소스(이슈, 게시물 등)으로 옮기려 할 때
*
* @param from 첨부 파일이 원래 있었던 리소스
* @param to 첨부 파일이 새로 옮겨질 리소스
* @return
*/
public static int moveOnlySelected(Resource from, Resource to, String[] selectedFileIds) {
if(selectedFileIds.length == 0){
return NOTHING_TO_ATTACH;
}
List<Attachment> attachments = Attachment.find.where().idIn(Arrays.asList(selectedFileIds)).findList();
for (Attachment attachment : attachments) {
if(attachment.containerId.equals(from.getId())
&& attachment.containerType == from.getType()){
attachment.moveTo(to);
}
}
return attachments.size();
}

/**
* 이 첨부 파일을 {@code to}로 옮긴다.
*
Expand Down Expand Up @@ -222,6 +249,7 @@ public boolean store(File file, String name, Resource container) throws IOExcept
// metadata - containerType, containerId, size and hash - in Database.
this.containerType = container.getType();
this.containerId = container.getId();
this.createdDate = JodaDateUtil.now();

if (name == null) {
this.name = file.getName();
Expand Down
4 changes: 4 additions & 0 deletions app/utils/JodaDateUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public static Date before(int days){
return new DateTime(today()).minusDays(days).toDate();
}

public static Date beforeByMillis(long millis){
return new DateTime(today()).minus(millis).toDate();
}

public static String momentFromNow(Long time) {
return momentFromNow(time, Constants.DEFAULT_LANGUAGE);
}
Expand Down
3 changes: 3 additions & 0 deletions conf/application.conf.default
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ smtp.archive.size = 5
#if you want to use sign-up confirm, uncomment below
#signup.require.confirm = true

# User uploaded temporary files cleanup schedule (sec, default 24hour: 24*60*60 = 86400)
# application.temporaryfiles.keep-up.time = 86400

# Notification
# ~~~~~~~~~~~~
# Check mails to send every this seconds.
Expand Down
7 changes: 7 additions & 0 deletions conf/evolutions/default/61.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# --- !Ups

ALTER TABLE attachment ADD COLUMN created_date DATE;

# --- !Downs

ALTER TABLE attachment DROP COLUMN IF EXISTS created_date;
1 change: 1 addition & 0 deletions conf/messages
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ post.notice = notice
post.notice.label = Set this post to notice
post.popup.fileAttach.contents = Please, select file to attach
post.popup.fileAttach.title = Select file
post.popup.fileAttach.hasMissing = There are {0} missing attachment files. It may caused by server keep time of temporary upload file ({1} min). Please upload again.
post.unwatch.start = Notifications of this post has muted
post.update.error = Errors on Input Value
post.watch.start = You will receive notifications of this post
Expand Down
1 change: 1 addition & 0 deletions conf/messages.ko
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ post.notice = 공지
post.notice.label = 이 글을 공지사항으로 설정합니다
post.popup.fileAttach.contents = 첨부할 파일을 선택해주세요.
post.popup.fileAttach.title = 첨부파일 선택
post.popup.fileAttach.hasMissing = 첨부되지 못한 파일이 {0}개 있습니다. (첨부 파일 업로드 후 일정기간({1}분)을 초과 할때까지 글 작성을 완료하지 않았을 경우 발생할 수 있습니다. 다시 파일을 첨부해 주세요.)
post.unwatch.start = 이제 이 글에 관한 알림을 받지 않습니다
post.update.error = 입력값 오류
post.watch.start = 이제 이 글에 관한 알림을 받습니다
Expand Down
Loading

0 comments on commit 9972fc6

Please sign in to comment.