feat(forums): attach upload & download is finished

This commit is contained in:
OldHawk
2017-07-14 12:30:42 +08:00
parent 5419d2eaa0
commit 9aec36d161
10 changed files with 336 additions and 14 deletions

View File

@@ -78,9 +78,11 @@ module.exports.createUploadAttachFilename = function (req, file, cb) {
}
if (fs.existsSync(config.uploads.attach.file.dest + filename)) {
var err = new Error();
err.code = 'FILE_ALREADY_EXISTS';
cb(err, null);
var ext = file.originalname.replace(/^.+\./, '');
var regex = new RegExp(ext, 'g');
filename = filename.replace(regex, Date.now() + '.' + ext);
cb(null, filename);
} else {
cb(null, filename);
}

View File

@@ -680,6 +680,15 @@
TODAY_NEW_COUNT_ALL: '(Today: <mark class="text-danger">{{topic}}</mark> topics, <mark class="text-danger">{{reply}}</mark> replies)',
TODAY_NEW_COUNT_TOPIC: '(Today: <mark class="text-danger">{{topic}}</mark> topics)',
TODAY_NEW_COUNT_REPLY: '(Today: <mark class="text-danger">{{reply}}</mark> replies)',
UPLOAD_ATTACH_SUCCESSFULLY: 'Upload attach file successfully',
UPLOAD_ATTACH_FAILED: 'Upload attach file failed',
ATTACH_UPLOAD_TOOLTIP: 'Attach files by dragging & dropping to here, The picture file will be displayed automatically, Others file can only be downloaded.',
ATTACH_UPLOADING: 'Uploading',
ATTACH_LIST_HEADER: 'Attach files list:',
ATTACH_FILE_SIZE: 'Size:',
ATTACH_DOWN_COUNT: 'Downloaded:',
ATTACHE_DOWNLOAD_SUCCESSFULLY: 'Attach file download successfully',
ATTACHE_DOWNLOAD_FAILED: 'Attach file download failed',
CATEGORY: {
AFFAIRS: 'Affairs',

View File

@@ -680,6 +680,15 @@
TODAY_NEW_COUNT_ALL: '(今日: <mark class="text-danger">{{topic}}</mark> 话题, <mark class="text-danger">{{reply}}</mark> 回复)',
TODAY_NEW_COUNT_TOPIC: '(今日: <mark class="text-danger">{{topic}}</mark> 话题)',
TODAY_NEW_COUNT_REPLY: '(今日: <mark class="text-danger">{{reply}}</mark> 回复)',
UPLOAD_ATTACH_SUCCESSFULLY: '文件上传成功',
UPLOAD_ATTACH_FAILED: '文件上传失败',
ATTACH_UPLOAD_TOOLTIP: '上传附件请将文件拖到这里面, 图片文件将会被自动显示, 其它类文件只能被下载.',
ATTACH_UPLOADING: '正在上传',
ATTACH_LIST_HEADER: '附件清单:',
ATTACH_FILE_SIZE: '文件大小:',
ATTACH_DOWN_COUNT: '下载次数:',
ATTACHE_DOWNLOAD_SUCCESSFULLY: '附件文件下载成功',
ATTACHE_DOWNLOAD_FAILED: '附件文件下载失败',
CATEGORY: {
AFFAIRS: '站务区',

View File

@@ -4,12 +4,13 @@
angular.module('core')
.directive('mtMarkdownEditor', mtMarkdownEditor);
mtMarkdownEditor.$inject = ['localStorageService'];
mtMarkdownEditor.$inject = ['localStorageService', '$compile', 'NotifycationService', '$timeout'];
function mtMarkdownEditor(localStorageService) {
function mtMarkdownEditor(localStorageService, $compile, NotifycationService, $timeout) {
var directive = {
restrict: 'A',
require: 'ngModel',
replace: true,
link: link
};
@@ -25,6 +26,83 @@
fullscreen: {enable: false},
onChange: function (e) {
ngModel.$setViewValue($('#' + attrs.mtMarkdownEditor)[0].value);
},
onShow: function (e) {
scope.uFile = undefined;
scope.uProgress = 0;
scope.uFiles = [];
scope.uImages = [];
var eleUploadTip = angular.element('<div class="attach-info text-long" ng-show="!uFile"><div class="attach-upload-tooltip">{{\'FORUMS.ATTACH_UPLOAD_TOOLTIP\' | translate}}</div></div>');
var eleUploadBegin = angular.element('<div class="upload-info" ng-show="uFile"><i class="fa fa-cog fa-spin fa-lg fa-fw"></i> <div class="attach-upload-progress" style="width: {{uProgress}}%"></div><div class="attach-upload-filename">{{\'FORUMS.ATTACH_UPLOADING\' | translate}}: {{uFile.name}}</div></div>');
var eleUploadList = angular.element('<div class="attach-list" ng-show="uFiles.length"><div><ol><li ng-repeat="f in uFiles track by $index">{{f.name}} <i class="fa fa-times" ng-click="removeAttach($index)"></i></li></ol></div></div>');
//$compile(eleUploadTip)(scope);
//$compile(eleUploadBegin)(scope);
//$compile(eleUploadList)(scope);
$('.md-editor').append(eleUploadTip);
$('.md-editor').append(eleUploadBegin);
$('.md-editor').append(eleUploadList);
scope.removeAttach = function (idx) {
scope.uFiles.splice(idx, 1);
};
$('.md-editor').bind('dragenter', function (evt) {
evt.stopPropagation();
evt.preventDefault();
});
$('.md-editor').bind('dragover', function (evt) {
evt.stopPropagation();
evt.preventDefault();
});
$('.md-editor').bind('drop', function (evt) {
evt.stopPropagation();
evt.preventDefault();
if (attrs.uploadMethod) {
scope.uFile = evt.originalEvent.dataTransfer.files[0];
scope.uProgress = 0;
scope.$eval(attrs.uploadMethod, {
editor: e,
ufile: scope.uFile,
progressback: function (pr) {
scope.uProgress = pr;
},
callback: function (fn) {
var uFile = {
name: fn,
size: scope.uFile.size
};
var status = '';
var ext = uFile.name.replace(/^.+\./, '').toLowerCase();
if (ext === 'jpg' || ext === 'jpeg' || ext === 'bmp' || ext === 'gif' || ext === 'png') {
status = '\n![' + uFile.name + '](/modules/forums/client/attach/temp/' + uFile.name + ')\n';
e.replaceSelection(status);
ngModel.$setViewValue($('#' + attrs.mtMarkdownEditor)[0].value);
scope.uImages.push(uFile);
} else {
scope.uFiles.push(uFile);
}
scope.uFile = undefined;
scope.uProgress = 0;
NotifycationService.showSuccessNotify('FORUMS.UPLOAD_ATTACH_SUCCESSFULLY');
},
errback: function (err) {
scope.uFile = undefined;
scope.uProgress = 0;
NotifycationService.showErrorNotify(err.data, 'FORUMS.UPLOAD_ATTACH_FAILED');
}
});
}
return false;
});
$compile($('.md-editor').contents())(scope);
}
});
}

View File

@@ -5,10 +5,10 @@
.module('forums')
.controller('ForumsPostController', ForumsPostController);
ForumsPostController.$inject = ['$scope', '$state', '$translate', 'Authentication', 'MeanTorrentConfig', 'ForumsService', 'SideOverlay', '$filter', 'NotifycationService',
ForumsPostController.$inject = ['$scope', '$state', '$translate', 'Authentication', 'MeanTorrentConfig', 'ForumsService', 'Upload', '$timeout', 'NotifycationService',
'marked', 'ModalConfirmService', '$stateParams', 'TopicsService'];
function ForumsPostController($scope, $state, $translate, Authentication, MeanTorrentConfig, ForumsService, SideOverlay, $filter, NotifycationService,
function ForumsPostController($scope, $state, $translate, Authentication, MeanTorrentConfig, ForumsService, Upload, $timeout, NotifycationService,
marked, ModalConfirmService, $stateParams, TopicsService) {
var vm = this;
vm.forumsConfig = MeanTorrentConfig.meanTorrentConfig.forumsConfig;
@@ -49,8 +49,25 @@
return false;
}
var uf = [];
angular.forEach($scope.uFiles, function (f) {
uf.push({
filename: f.name,
filesize: f.size
});
});
var uimg = [];
angular.forEach($scope.uImages, function (f) {
uimg.push({
filename: f.name
});
});
var post = new TopicsService(vm.postFields);
post.forum = vm.forum._id;
post._attach = uf;
post._uImage = uimg;
post.$save(function (response) {
successCallback(response);
@@ -60,6 +77,9 @@
function successCallback(res) {
vm.postFields = {};
$scope.uFiles = [];
$scope.uImages = [];
$scope.$broadcast('show-errors-reset', 'vm.postForm');
NotifycationService.showSuccessNotify('FORUMS.POST_TOPIC_SUCCESSFULLY');
$state.go('forums.topic', {forumId: vm.forum._id, topicId: res._id});
@@ -70,5 +90,31 @@
}
};
/**
* uploadAttach
* @param editor
* @param ufile
* @param callback
*/
vm.uploadAttach = function (editor, ufile, progressback, callback, errback) {
Upload.upload({
url: '/api/attach/upload',
data: {
newAttachFile: ufile
}
}).then(function (res) {
if (callback) {
callback(res.data.filename);
}
}, function (res) {
if (errback && res.status > 0) {
errback(res);
}
}, function (evt) {
if (progressback) {
progressback(parseInt(100.0 * evt.loaded / evt.total, 10));
}
});
};
}
}());

View File

@@ -6,12 +6,13 @@
.controller('ForumsTopicController', ForumsTopicController);
ForumsTopicController.$inject = ['$scope', '$state', '$translate', 'Authentication', 'MeanTorrentConfig', 'ForumsService', 'ScoreLevelService', '$timeout', 'NotifycationService',
'marked', 'ModalConfirmService', '$stateParams', 'TopicsService', 'localStorageService', '$compile', 'RepliesService', '$filter'];
'marked', 'ModalConfirmService', '$stateParams', 'TopicsService', 'localStorageService', '$compile', 'RepliesService', '$filter', 'Upload', 'DownloadService'];
function ForumsTopicController($scope, $state, $translate, Authentication, MeanTorrentConfig, ForumsService, ScoreLevelService, $timeout, NotifycationService,
marked, ModalConfirmService, $stateParams, TopicsService, localStorageService, $compile, RepliesService, $filter) {
marked, ModalConfirmService, $stateParams, TopicsService, localStorageService, $compile, RepliesService, $filter, Upload, DownloadService) {
var vm = this;
vm.forumsConfig = MeanTorrentConfig.meanTorrentConfig.forumsConfig;
vm.appConfig = MeanTorrentConfig.meanTorrentConfig.app;
vm.user = Authentication.user;
vm.forumPath = [];
vm.postReplyFields = {};
@@ -333,9 +334,26 @@
return false;
}
var uf = [];
angular.forEach($scope.uFiles, function (f) {
uf.push({
filename: f.name,
filesize: f.size
});
});
var uimg = [];
angular.forEach($scope.uImages, function (f) {
uimg.push({
filename: f.name
});
});
var reply = new RepliesService(vm.postReplyFields);
reply.forum = vm.forum._id;
reply.topic = vm.topic._id;
reply._attach = uf;
reply._uImage = uimg;
reply.$save(function (response) {
successCallback(response);
@@ -346,6 +364,9 @@
function successCallback(res) {
vm.postReplyFields = {};
vm.topic = res;
$scope.uFiles = [];
$scope.uImages = [];
$scope.$broadcast('show-errors-reset', 'vm.replyForm');
NotifycationService.showSuccessNotify('FORUMS.POST_REPLY_SUCCESSFULLY');
}
@@ -391,5 +412,53 @@
vm.topic = res;
});
};
/**
* uploadAttach
* @param editor
* @param ufile
* @param callback
*/
vm.uploadAttach = function (editor, ufile, progressback, callback, errback) {
Upload.upload({
url: '/api/attach/upload',
data: {
newAttachFile: ufile
}
}).then(function (res) {
if (callback) {
callback(res.data.filename);
}
}, function (res) {
if (errback && res.status > 0) {
errback(res);
}
}, function (evt) {
if (progressback) {
progressback(parseInt(100.0 * evt.loaded / evt.total, 10));
}
});
};
/**
* downloadAttach
* @param t
* @param r
* @param af
*/
vm.downloadAttach = function (t, r, af) {
var url = '/api/attach/' + vm.topic._id.toString();
url += r ? '/' + r._id.toString() : '';
url += '?attachId=' + af._id.toString();
DownloadService.downloadFile(url, null, function (status) {
if (status === 200) {
NotifycationService.showSuccessNotify('FORUMS.ATTACHE_DOWNLOAD_SUCCESSFULLY');
}
}, function (err) {
console.log(err);
NotifycationService.showErrorNotify(err.data.message, 'FORUMS.ATTACHE_DOWNLOAD_FAILED');
});
};
}
}());

View File

@@ -424,6 +424,29 @@
max-width: 100%;
height: auto;
}
.attach-list {
border: solid 1px #ddd;
margin-top: 20px;
.attach-header {
height: 40px;
background-color: #f5f5f5;
border-bottom: 1px dashed #ddd;
padding: 8px 15px;
font-weight: 500;
}
ol {
margin-bottom: 0;
li {
font-size: 12px;
margin: 10px;
span {
&:not(:first-child) {
color: #999;
}
}
}
}
}
}
}
.reply-avatar {
@@ -459,15 +482,74 @@
}
.md-editor {
background-color: #f5f5f5;
textarea {
border-bottom: none;
background-color: #fff;
min-height: 250px;
min-height: 300px;
max-height: 800px;
padding: 5px;
}
.md-preview {
padding: 5px;
border-bottom: none;
img {
max-width: 100%;
height: auto;
}
}
.attach-info {
color: #666;
height: 40px;
background-color: #f5f5f5;
border-top: 1px dashed #ddd;
.attach-upload-tooltip {
margin: 10px;
}
}
.attach-list {
color: #666;
min-height: 40px;
background-color: #f5f5f5;
border-top: 1px dashed #ddd;
ol {
margin-bottom: 0;
li {
font-size: 12px;
color: @brand-primary;
margin: 10px;
i {
cursor: pointer;
&:hover {
color: @mt-base-color;
}
}
}
}
}
.upload-info {
position: relative;
color: #666;
height: 40px;
background-color: #f5f5f5;
border-top: 1px dashed #ddd;
i {
color: @mt-base-color;
float: left;
margin: 11px 5px;
}
.attach-upload-progress {
height: 30px;
margin: 5px 30px;
max-width: ~"calc(100% - 40px)";
background-color: @brand-info;
}
.attach-upload-filename {
position: absolute;
top: 10px;
margin-left: 35px;
background-color: transparent;
}
}
}

View File

@@ -52,7 +52,8 @@
</div>
<div class="form-group" show-errors>
<textarea id="postContent" name="postContent" ng-model="vm.postFields.content" mt-markdown-editor="postContent" required></textarea>
<textarea id="postContent" name="postContent" ng-model="vm.postFields.content"
mt-markdown-editor="postContent" upload-method="vm.uploadAttach(editor, ufile, progressback, callback, errback);" required></textarea>
<div ng-messages="vm.postForm.postContent.$error" role="alert">
<p class="help-block error-text" ng-message="required">{{ 'FORUMS.PC_REQUIRED' | translate}}</p>

View File

@@ -54,7 +54,8 @@
<a class="btn btn-success btn-width-100"
href="#" ng-click="vm.beginPostReply();">{{'FORUMS.BTN_POST_NEW_REPLY' | translate}}</a>
<a class="btn btn-default btn-width-100"
href="#" ng-click="vm.toggleReadonly(vm.topic);">{{ vm.topic.readOnly ? 'FORUMS.BTN_UNSET_READONLY' : 'FORUMS.BTN_SET_READONLY' | translate}}</a>
href="#"
ng-click="vm.toggleReadonly(vm.topic);">{{ vm.topic.readOnly ? 'FORUMS.BTN_UNSET_READONLY' : 'FORUMS.BTN_SET_READONLY' | translate}}</a>
</div>
<h5 class="text-danger text-uppercase" translate="FORUMS.READ_ONLY_REPLY"
@@ -126,6 +127,16 @@
<div id="{{vm.topic._id}}" data-provide="markdown" class="reply-comment-body-text"
ng-bind-html="vm.getTopicContent(vm.topic)">
</div>
<div class="attach-list" ng-if="vm.topic._attach.length">
<div class="attach-header" translate="FORUMS.ATTACH_LIST_HEADER"></div>
<ol>
<li ng-repeat="af in vm.topic._attach track by $index">
<span><a ng-href="#" ng-click="vm.downloadAttach(vm.topic, null, af);">{{af.filename}}</a></span> (
<span>{{'FORUMS.ATTACH_FILE_SIZE' | translate}} {{af.filesize | bytes}}</span>
<span>{{'FORUMS.ATTACH_DOWN_COUNT' | translate}} {{af.downCount}}</span> )
</li>
</ol>
</div>
<div class="row text-right margin-top-10" ng-show="vm.topic.updatedAt">
<p class="reply-comment-body-edited-text">[ {{vm.topic.updatedBy.displayName}} {{ 'COMMENT_EDITED_INFO' | translate}} {{vm.topic.updatedAt | date:'yyyy-MM-dd HH:mm:ss'}} ]</p>
</div>
@@ -176,6 +187,16 @@
<div id="{{rep._id}}" data-provide="markdown" class="reply-comment-body-text"
ng-bind-html="vm.getTopicContent(rep)">
</div>
<div class="attach-list" ng-if="rep._attach.length">
<div class="attach-header" translate="FORUMS.ATTACH_LIST_HEADER"></div>
<ol>
<li ng-repeat="af in rep._attach track by $index">
<span><a ng-href="#" ng-click="vm.downloadAttach(vm.topic, rep, af);">{{af.filename}}</a></span> (
<span>{{'FORUMS.ATTACH_FILE_SIZE' | translate}} {{af.filesize | bytes}}</span>
<span>{{'FORUMS.ATTACH_DOWN_COUNT' | translate}} {{af.downCount}}</span> )
</li>
</ol>
</div>
<div class="row text-right margin-top-10" ng-show="rep.updatedAt">
<p class="reply-comment-body-edited-text">[ {{rep.updatedBy.displayName}} {{ 'COMMENT_EDITED_INFO' | translate}} {{rep.updatedAt | date:'yyyy-MM-dd HH:mm:ss'}} ]</p>
</div>
@@ -201,13 +222,14 @@
</table>
</div>
<div class="reply-post margin-bottom-30" ng-if="!vm.topic.readOnly || vm.canEdit(vm.topic)">
<div class="reply-post margin-bottom-30" ng-show="!vm.topic.readOnly || vm.canEdit(vm.topic)">
<div class="row">
<div class="col-md-12">
<form name="vm.replyForm" ng-submit="vm.postReply(vm.replyForm.$valid)" novalidate autocomplete="off">
<div class="form-group" show-errors>
<textarea id="postReplyContent" name="postReplyContent" ng-model="vm.postReplyFields.content"
mt-markdown-editor="postReplyContent" required></textarea>
mt-markdown-editor="postReplyContent"
upload-method="vm.uploadAttach(editor, ufile, progressback, callback, errback);" required></textarea>
<div ng-messages="vm.replyForm.postReplyContent.$error" role="alert">
<p class="help-block error-text" ng-message="required">{{ 'FORUMS.PRC_REQUIRED' | translate}}</p>

View File

@@ -15,6 +15,10 @@ var AttachSchema = new Schema({
default: '',
trim: true
},
filesize: {
type: Number,
default: 0
},
downCount: {
type: Number,
default: 0