mirror of
https://github.com/sismics/docs.git
synced 2024-11-24 22:57:56 +01:00
#254: display documents in grid + concept of main file
This commit is contained in:
parent
eb9e0e0543
commit
cee82f39c2
@ -189,13 +189,28 @@ public class DocumentDao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a document.
|
* Update a document and log the action.
|
||||||
*
|
*
|
||||||
* @param document Document to update
|
* @param document Document to update
|
||||||
* @param userId User ID
|
* @param userId User ID
|
||||||
* @return Updated document
|
* @return Updated document
|
||||||
*/
|
*/
|
||||||
public Document update(Document document, String userId) {
|
public Document update(Document document, String userId) {
|
||||||
|
Document documentDb = updateSilently(document);
|
||||||
|
|
||||||
|
// Create audit log
|
||||||
|
AuditLogUtil.create(documentDb, AuditLogType.UPDATE, userId);
|
||||||
|
|
||||||
|
return documentDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a document without audit log.
|
||||||
|
*
|
||||||
|
* @param document Document to update
|
||||||
|
* @return Updated document
|
||||||
|
*/
|
||||||
|
public Document updateSilently(Document document) {
|
||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
|
|
||||||
// Get the document
|
// Get the document
|
||||||
@ -216,11 +231,9 @@ public class DocumentDao {
|
|||||||
documentDb.setRights(document.getRights());
|
documentDb.setRights(document.getRights());
|
||||||
documentDb.setCreateDate(document.getCreateDate());
|
documentDb.setCreateDate(document.getCreateDate());
|
||||||
documentDb.setLanguage(document.getLanguage());
|
documentDb.setLanguage(document.getLanguage());
|
||||||
|
documentDb.setFileId(document.getFileId());
|
||||||
documentDb.setUpdateDate(new Date());
|
documentDb.setUpdateDate(new Date());
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
AuditLogUtil.create(documentDb, AuditLogType.UPDATE, userId);
|
|
||||||
|
|
||||||
return documentDb;
|
return documentDb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,11 @@ public class DocumentDto {
|
|||||||
*/
|
*/
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main file ID.
|
||||||
|
*/
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Title.
|
* Title.
|
||||||
*/
|
*/
|
||||||
@ -114,6 +119,15 @@ public class DocumentDto {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFileId() {
|
||||||
|
return fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DocumentDto setFileId(String fileId) {
|
||||||
|
this.fileId = fileId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,12 @@ import com.google.common.eventbus.AllowConcurrentEvents;
|
|||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.sismics.docs.core.dao.ContributorDao;
|
import com.sismics.docs.core.dao.ContributorDao;
|
||||||
import com.sismics.docs.core.dao.DocumentDao;
|
import com.sismics.docs.core.dao.DocumentDao;
|
||||||
|
import com.sismics.docs.core.dao.FileDao;
|
||||||
import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
|
import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent;
|
||||||
import com.sismics.docs.core.model.context.AppContext;
|
import com.sismics.docs.core.model.context.AppContext;
|
||||||
import com.sismics.docs.core.model.jpa.Contributor;
|
import com.sismics.docs.core.model.jpa.Contributor;
|
||||||
import com.sismics.docs.core.model.jpa.Document;
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import com.sismics.docs.core.util.TransactionUtil;
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -38,13 +40,25 @@ public class DocumentUpdatedAsyncListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionUtil.handle(() -> {
|
TransactionUtil.handle(() -> {
|
||||||
// Update index
|
// Get the document
|
||||||
DocumentDao documentDao = new DocumentDao();
|
DocumentDao documentDao = new DocumentDao();
|
||||||
Document document = documentDao.getById(event.getDocumentId());
|
Document document = documentDao.getById(event.getDocumentId());
|
||||||
if (document == null) {
|
if (document == null) {
|
||||||
// Document deleted since event fired
|
// Document deleted since event fired
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the main file
|
||||||
|
FileDao fileDao = new FileDao();
|
||||||
|
List<File> fileList = fileDao.getByDocumentId(null, event.getDocumentId());
|
||||||
|
if (fileList.isEmpty()) {
|
||||||
|
document.setFileId(null);
|
||||||
|
} else {
|
||||||
|
document.setFileId(fileList.get(0).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update database and index
|
||||||
|
documentDao.updateSilently(document);
|
||||||
AppContext.getInstance().getIndexingHandler().updateDocument(document);
|
AppContext.getInstance().getIndexingHandler().updateDocument(document);
|
||||||
|
|
||||||
// Update contributors list
|
// Update contributors list
|
||||||
|
@ -29,6 +29,12 @@ public class Document implements Loggable {
|
|||||||
@Column(name = "DOC_IDUSER_C", nullable = false, length = 36)
|
@Column(name = "DOC_IDUSER_C", nullable = false, length = 36)
|
||||||
private String userId;
|
private String userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main file ID.
|
||||||
|
*/
|
||||||
|
@Column(name = "DOC_IDFILE_C", length = 36)
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Language (ISO 639-9).
|
* Language (ISO 639-9).
|
||||||
*/
|
*/
|
||||||
@ -137,6 +143,15 @@ public class Document implements Loggable {
|
|||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFileId() {
|
||||||
|
return fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Document setFileId(String fileId) {
|
||||||
|
this.fileId = fileId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ public class LuceneIndexingHandler implements IndexingHandler {
|
|||||||
List<String> criteriaList = new ArrayList<>();
|
List<String> criteriaList = new ArrayList<>();
|
||||||
Map<String, String> documentSearchMap = Maps.newHashMap();
|
Map<String, String> documentSearchMap = Maps.newHashMap();
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C c0, d.DOC_TITLE_C c1, d.DOC_DESCRIPTION_C c2, d.DOC_CREATEDATE_D c3, d.DOC_LANGUAGE_C c4, ");
|
StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C c0, d.DOC_TITLE_C c1, d.DOC_DESCRIPTION_C c2, d.DOC_CREATEDATE_D c3, d.DOC_LANGUAGE_C c4, d.DOC_IDFILE_C, ");
|
||||||
sb.append(" s.count c5, ");
|
sb.append(" s.count c5, ");
|
||||||
sb.append(" f.count c6, ");
|
sb.append(" f.count c6, ");
|
||||||
sb.append(" rs2.RTP_ID_C c7, rs2.RTP_NAME_C, d.DOC_UPDATEDATE_D c8 ");
|
sb.append(" rs2.RTP_ID_C c7, rs2.RTP_NAME_C, d.DOC_UPDATEDATE_D c8 ");
|
||||||
@ -323,6 +323,7 @@ public class LuceneIndexingHandler implements IndexingHandler {
|
|||||||
documentDto.setDescription((String) o[i++]);
|
documentDto.setDescription((String) o[i++]);
|
||||||
documentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
documentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||||
documentDto.setLanguage((String) o[i++]);
|
documentDto.setLanguage((String) o[i++]);
|
||||||
|
documentDto.setFileId((String) o[i++]);
|
||||||
Number shareCount = (Number) o[i++];
|
Number shareCount = (Number) o[i++];
|
||||||
documentDto.setShared(shareCount != null && shareCount.intValue() > 0);
|
documentDto.setShared(shareCount != null && shareCount.intValue() > 0);
|
||||||
Number fileCount = (Number) o[i++];
|
Number fileCount = (Number) o[i++];
|
||||||
|
@ -1 +1 @@
|
|||||||
db.version=20
|
db.version=21
|
@ -0,0 +1,4 @@
|
|||||||
|
alter table T_DOCUMENT add column DOC_IDFILE_C varchar(36);
|
||||||
|
alter table T_DOCUMENT add constraint FK_DOC_IDFILE_C foreign key (DOC_IDFILE_C) references T_FILE (FIL_ID_C) on delete restrict on update restrict;
|
||||||
|
|
||||||
|
update T_CONFIG set CFG_VALUE_C = '21' where CFG_ID_C = 'DB_VERSION';
|
@ -154,6 +154,16 @@ public class ClientUtil {
|
|||||||
return authToken;
|
return authToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a file to a document.
|
||||||
|
*
|
||||||
|
* @param file File path
|
||||||
|
* @param filename Filename
|
||||||
|
* @param token Authentication token
|
||||||
|
* @param documentId Document ID
|
||||||
|
* @return File ID
|
||||||
|
* @throws IOException e
|
||||||
|
*/
|
||||||
public String addFileToDocument(String file, String filename, String token, String documentId) throws IOException {
|
public String addFileToDocument(String file, String filename, String token, String documentId) throws IOException {
|
||||||
try (InputStream is = Resources.getResource(file).openStream()) {
|
try (InputStream is = Resources.getResource(file).openStream()) {
|
||||||
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, filename);
|
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, filename);
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=20
|
db.version=21
|
@ -326,6 +326,7 @@ public class DocumentResource extends BaseResource {
|
|||||||
* @apiSuccess {Object[]} documents List of documents
|
* @apiSuccess {Object[]} documents List of documents
|
||||||
* @apiSuccess {String} documents.id ID
|
* @apiSuccess {String} documents.id ID
|
||||||
* @apiSuccess {String} documents.highlight Search highlight (for fulltext search)
|
* @apiSuccess {String} documents.highlight Search highlight (for fulltext search)
|
||||||
|
* @apiSuccess {String} documents.file_id Main file ID
|
||||||
* @apiSuccess {String} documents.title Title
|
* @apiSuccess {String} documents.title Title
|
||||||
* @apiSuccess {String} documents.description Description
|
* @apiSuccess {String} documents.description Description
|
||||||
* @apiSuccess {Number} documents.create_date Create date (timestamp)
|
* @apiSuccess {Number} documents.create_date Create date (timestamp)
|
||||||
@ -395,6 +396,7 @@ public class DocumentResource extends BaseResource {
|
|||||||
documents.add(Json.createObjectBuilder()
|
documents.add(Json.createObjectBuilder()
|
||||||
.add("id", documentDto.getId())
|
.add("id", documentDto.getId())
|
||||||
.add("highlight", JsonUtil.nullable(documentDto.getHighlight()))
|
.add("highlight", JsonUtil.nullable(documentDto.getHighlight()))
|
||||||
|
.add("file_id", JsonUtil.nullable(documentDto.getFileId()))
|
||||||
.add("title", documentDto.getTitle())
|
.add("title", documentDto.getTitle())
|
||||||
.add("description", JsonUtil.nullable(documentDto.getDescription()))
|
.add("description", JsonUtil.nullable(documentDto.getDescription()))
|
||||||
.add("create_date", documentDto.getCreateTimestamp())
|
.add("create_date", documentDto.getCreateTimestamp())
|
||||||
|
@ -368,6 +368,12 @@ public class FileResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raise a document updated event
|
||||||
|
DocumentUpdatedAsyncEvent event = new DocumentUpdatedAsyncEvent();
|
||||||
|
event.setUserId(principal.getId());
|
||||||
|
event.setDocumentId(documentId);
|
||||||
|
ThreadLocalContext.get().addAsyncEvent(event);
|
||||||
|
|
||||||
// Always return OK
|
// Always return OK
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
.add("status", "ok");
|
.add("status", "ok");
|
||||||
|
@ -12,6 +12,7 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
|||||||
$scope.offset = 0;
|
$scope.offset = 0;
|
||||||
$scope.currentPage = 1;
|
$scope.currentPage = 1;
|
||||||
$scope.limit = _.isUndefined(localStorage.documentsPageSize) ? '10' : localStorage.documentsPageSize;
|
$scope.limit = _.isUndefined(localStorage.documentsPageSize) ? '10' : localStorage.documentsPageSize;
|
||||||
|
$scope.displayMode = _.isUndefined(localStorage.displayMode) ? 'list' : localStorage.displayMode;
|
||||||
$scope.search = $state.params.search ? $state.params.search : '';
|
$scope.search = $state.params.search ? $state.params.search : '';
|
||||||
$scope.setSearch = function (search) { $scope.search = search };
|
$scope.setSearch = function (search) { $scope.search = search };
|
||||||
$scope.searchOpened = false;
|
$scope.searchOpened = false;
|
||||||
@ -114,6 +115,13 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
|||||||
$scope.loadDocuments();
|
$scope.loadDocuments();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watch for display mode change.
|
||||||
|
*/
|
||||||
|
$scope.$watch('displayMode', function (next) {
|
||||||
|
localStorage.displayMode = next;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a document.
|
* Display a document.
|
||||||
*/
|
*/
|
||||||
|
@ -41,6 +41,8 @@
|
|||||||
"document": {
|
"document": {
|
||||||
"navigation_up": "Go up one level",
|
"navigation_up": "Go up one level",
|
||||||
"toggle_navigation": "Toggle folder navigation",
|
"toggle_navigation": "Toggle folder navigation",
|
||||||
|
"display_mode_list": "Display documents in list",
|
||||||
|
"display_mode_grid": "Display documents in grid",
|
||||||
"search_simple": "Simple search",
|
"search_simple": "Simple search",
|
||||||
"search_fulltext": "Fulltext search",
|
"search_fulltext": "Fulltext search",
|
||||||
"search_creator": "Creator",
|
"search_creator": "Creator",
|
||||||
|
@ -169,6 +169,22 @@
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
<!-- Display mode (list or grid) -->
|
||||||
|
<div class="btn-group mt-10 ml-10 pull-right">
|
||||||
|
<span class="btn btn-default" ng-class="{ active: displayMode == 'list' }"
|
||||||
|
uib-tooltip="{{ 'document.display_mode_list' | translate }}"
|
||||||
|
tooltip-append-to-body="true"
|
||||||
|
ng-click="displayMode = 'list'">
|
||||||
|
<span class="fas fa-list"></span>
|
||||||
|
</span>
|
||||||
|
<span class="btn btn-default" ng-class="{ active: displayMode == 'grid' }"
|
||||||
|
uib-tooltip="{{ 'document.display_mode_grid' | translate }}"
|
||||||
|
tooltip-append-to-body="true"
|
||||||
|
ng-click="displayMode = 'grid'">
|
||||||
|
<span class="fas fa-th"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="btn-group mt-10 pull-right">
|
<div class="btn-group mt-10 pull-right">
|
||||||
<!-- Go up in the navigation -->
|
<!-- Go up in the navigation -->
|
||||||
<button class="btn btn-default" ng-click="navigateUp()"
|
<button class="btn btn-default" ng-click="navigateUp()"
|
||||||
@ -209,7 +225,7 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Document list -->
|
<!-- Document list -->
|
||||||
<table class="row table table-hover table-documents">
|
<table class="row table table-hover table-documents" ng-show="displayMode == 'list'">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-xs-6" ng-click="sortDocuments(1)">{{ 'document.title' | translate }} <span class="fas fa-chevron-{{ sortColumn == 1 ? (asc ? 'down' : 'up') : '' }}"></span></th>
|
<th class="col-xs-6" ng-click="sortDocuments(1)">{{ 'document.title' | translate }} <span class="fas fa-chevron-{{ sortColumn == 1 ? (asc ? 'down' : 'up') : '' }}"></span></th>
|
||||||
@ -253,6 +269,39 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<!-- Document grid -->
|
||||||
|
<div class="row table-documents" ng-show="displayMode == 'grid'">
|
||||||
|
<div ng-if="!documents" class="col-md-12 text-center">
|
||||||
|
<span class="fas fa-circle-notch fa-spin"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="totalDocuments == 0" class=" col-md-12 text-center">
|
||||||
|
<span ng-if="search.length == 0">{{ 'document.no_documents' | translate }}</span>
|
||||||
|
<span ng-if="search.length > 0" translate="document.search_empty" translate-values="{ search: search }"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12 row">
|
||||||
|
<div ng-repeat-start="document in documents" class="col-md-4">
|
||||||
|
<div class="thumbnail" ng-class="{ active: $stateParams.id == document.id }">
|
||||||
|
<a href="#/document/view/{{ document.id }}" class="file-thumbnail">
|
||||||
|
<img class="img-responsive" ng-src="../api/file/{{ document.file_id }}/data?size=thumb" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="file-info">
|
||||||
|
<div></div>
|
||||||
|
<div class="v-align file-name text-center">
|
||||||
|
{{ document.title }} ({{ document.file_count }})
|
||||||
|
<span class="fas fa-share" ng-if="document.shared" uib-tooltip="{{ 'document.shared' | translate }}"></span>
|
||||||
|
<span class="fas fa-random" ng-if="document.active_route" uib-tooltip="{{ document.current_step_name }}"></span>
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix" ng-repeat-end ng-if="($index + 1) % 3 == 0"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="text-center pagination-box">
|
<div class="text-center pagination-box">
|
||||||
<ul uib-pagination
|
<ul uib-pagination
|
||||||
ng-if="paginationShown"
|
ng-if="paginationShown"
|
||||||
|
@ -187,6 +187,10 @@ ul.tag-tree {
|
|||||||
|
|
||||||
// File thumbnails
|
// File thumbnails
|
||||||
.thumbnail {
|
.thumbnail {
|
||||||
|
&.active {
|
||||||
|
border: 1px solid #2ab2dc;
|
||||||
|
}
|
||||||
|
|
||||||
.file-processing-indicator {
|
.file-processing-indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=20
|
db.version=21
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=20
|
db.version=21
|
@ -117,6 +117,7 @@ public class TestDocumentResource extends BaseJerseyTest {
|
|||||||
Assert.assertNotNull(documents.getJsonObject(0).get("update_date"));
|
Assert.assertNotNull(documents.getJsonObject(0).get("update_date"));
|
||||||
Assert.assertEquals(document1Id, documents.getJsonObject(0).getString("id"));
|
Assert.assertEquals(document1Id, documents.getJsonObject(0).getString("id"));
|
||||||
Assert.assertEquals("eng", documents.getJsonObject(0).getString("language"));
|
Assert.assertEquals("eng", documents.getJsonObject(0).getString("language"));
|
||||||
|
Assert.assertEquals(file1Id, documents.getJsonObject(0).getString("file_id"));
|
||||||
Assert.assertEquals(1, documents.getJsonObject(0).getInt("file_count"));
|
Assert.assertEquals(1, documents.getJsonObject(0).getInt("file_count"));
|
||||||
Assert.assertEquals(2, tags.size());
|
Assert.assertEquals(2, tags.size());
|
||||||
Assert.assertEquals(tag2Id, tags.getJsonObject(0).getString("id"));
|
Assert.assertEquals(tag2Id, tags.getJsonObject(0).getString("id"));
|
||||||
|
Loading…
Reference in New Issue
Block a user