mirror of
https://github.com/sismics/docs.git
synced 2024-12-25 12:43:49 +01:00
#254: display documents in grid + concept of main file
This commit is contained in:
parent
eb9e0e0543
commit
cee82f39c2
@ -189,20 +189,35 @@ public class DocumentDao {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a document.
|
||||
* Update a document and log the action.
|
||||
*
|
||||
* @param document Document to update
|
||||
* @param userId User ID
|
||||
* @return Updated document
|
||||
*/
|
||||
public Document update(Document document, String userId) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
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();
|
||||
|
||||
// Get the document
|
||||
Query q = em.createQuery("select d from Document d where d.id = :id and d.deleteDate is null");
|
||||
q.setParameter("id", document.getId());
|
||||
Document documentDb = (Document) q.getSingleResult();
|
||||
|
||||
|
||||
// Update the document
|
||||
documentDb.setTitle(document.getTitle());
|
||||
documentDb.setDescription(document.getDescription());
|
||||
@ -216,11 +231,9 @@ public class DocumentDao {
|
||||
documentDb.setRights(document.getRights());
|
||||
documentDb.setCreateDate(document.getCreateDate());
|
||||
documentDb.setLanguage(document.getLanguage());
|
||||
documentDb.setFileId(document.getFileId());
|
||||
documentDb.setUpdateDate(new Date());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(documentDb, AuditLogType.UPDATE, userId);
|
||||
|
||||
|
||||
return documentDb;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,12 @@ public class DocumentDto {
|
||||
* Document ID.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
|
||||
/**
|
||||
* Main file ID.
|
||||
*/
|
||||
private String fileId;
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*/
|
||||
@ -114,6 +119,15 @@ public class DocumentDto {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFileId() {
|
||||
return fileId;
|
||||
}
|
||||
|
||||
public DocumentDto setFileId(String fileId) {
|
||||
this.fileId = fileId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
@ -4,10 +4,12 @@ import com.google.common.eventbus.AllowConcurrentEvents;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sismics.docs.core.dao.ContributorDao;
|
||||
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.model.context.AppContext;
|
||||
import com.sismics.docs.core.model.jpa.Contributor;
|
||||
import com.sismics.docs.core.model.jpa.Document;
|
||||
import com.sismics.docs.core.model.jpa.File;
|
||||
import com.sismics.docs.core.util.TransactionUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -38,13 +40,25 @@ public class DocumentUpdatedAsyncListener {
|
||||
}
|
||||
|
||||
TransactionUtil.handle(() -> {
|
||||
// Update index
|
||||
// Get the document
|
||||
DocumentDao documentDao = new DocumentDao();
|
||||
Document document = documentDao.getById(event.getDocumentId());
|
||||
if (document == null) {
|
||||
// Document deleted since event fired
|
||||
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);
|
||||
|
||||
// Update contributors list
|
||||
|
@ -29,6 +29,12 @@ public class Document implements Loggable {
|
||||
@Column(name = "DOC_IDUSER_C", nullable = false, length = 36)
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* Main file ID.
|
||||
*/
|
||||
@Column(name = "DOC_IDFILE_C", length = 36)
|
||||
private String fileId;
|
||||
|
||||
/**
|
||||
* Language (ISO 639-9).
|
||||
*/
|
||||
@ -137,6 +143,15 @@ public class Document implements Loggable {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getFileId() {
|
||||
return fileId;
|
||||
}
|
||||
|
||||
public Document setFileId(String fileId) {
|
||||
this.fileId = fileId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
@ -219,7 +219,7 @@ public class LuceneIndexingHandler implements IndexingHandler {
|
||||
List<String> criteriaList = new ArrayList<>();
|
||||
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(" f.count c6, ");
|
||||
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.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||
documentDto.setLanguage((String) o[i++]);
|
||||
documentDto.setFileId((String) o[i++]);
|
||||
Number shareCount = (Number) o[i++];
|
||||
documentDto.setShared(shareCount != null && shareCount.intValue() > 0);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
try (InputStream is = Resources.getResource(file).openStream()) {
|
||||
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, filename);
|
||||
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
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 {String} documents.id ID
|
||||
* @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.description Description
|
||||
* @apiSuccess {Number} documents.create_date Create date (timestamp)
|
||||
@ -395,6 +396,7 @@ public class DocumentResource extends BaseResource {
|
||||
documents.add(Json.createObjectBuilder()
|
||||
.add("id", documentDto.getId())
|
||||
.add("highlight", JsonUtil.nullable(documentDto.getHighlight()))
|
||||
.add("file_id", JsonUtil.nullable(documentDto.getFileId()))
|
||||
.add("title", documentDto.getTitle())
|
||||
.add("description", JsonUtil.nullable(documentDto.getDescription()))
|
||||
.add("create_date", documentDto.getCreateTimestamp())
|
||||
|
@ -367,6 +367,12 @@ public class FileResource extends BaseResource {
|
||||
file.setOrder(order);
|
||||
}
|
||||
}
|
||||
|
||||
// Raise a document updated event
|
||||
DocumentUpdatedAsyncEvent event = new DocumentUpdatedAsyncEvent();
|
||||
event.setUserId(principal.getId());
|
||||
event.setDocumentId(documentId);
|
||||
ThreadLocalContext.get().addAsyncEvent(event);
|
||||
|
||||
// Always return OK
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
|
@ -12,6 +12,7 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
||||
$scope.offset = 0;
|
||||
$scope.currentPage = 1;
|
||||
$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.setSearch = function (search) { $scope.search = search };
|
||||
$scope.searchOpened = false;
|
||||
@ -113,7 +114,14 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
||||
}
|
||||
$scope.loadDocuments();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Watch for display mode change.
|
||||
*/
|
||||
$scope.$watch('displayMode', function (next) {
|
||||
localStorage.displayMode = next;
|
||||
});
|
||||
|
||||
/**
|
||||
* Display a document.
|
||||
*/
|
||||
|
@ -41,6 +41,8 @@
|
||||
"document": {
|
||||
"navigation_up": "Go up one level",
|
||||
"toggle_navigation": "Toggle folder navigation",
|
||||
"display_mode_list": "Display documents in list",
|
||||
"display_mode_grid": "Display documents in grid",
|
||||
"search_simple": "Simple search",
|
||||
"search_fulltext": "Fulltext search",
|
||||
"search_creator": "Creator",
|
||||
|
@ -169,6 +169,22 @@
|
||||
</li>
|
||||
</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">
|
||||
<!-- Go up in the navigation -->
|
||||
<button class="btn btn-default" ng-click="navigateUp()"
|
||||
@ -209,7 +225,7 @@
|
||||
</table>
|
||||
|
||||
<!-- Document list -->
|
||||
<table class="row table table-hover table-documents">
|
||||
<table class="row table table-hover table-documents" ng-show="displayMode == 'list'">
|
||||
<thead>
|
||||
<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>
|
||||
@ -253,6 +269,39 @@
|
||||
</tbody>
|
||||
</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">
|
||||
<ul uib-pagination
|
||||
ng-if="paginationShown"
|
||||
|
@ -187,6 +187,10 @@ ul.tag-tree {
|
||||
|
||||
// File thumbnails
|
||||
.thumbnail {
|
||||
&.active {
|
||||
border: 1px solid #2ab2dc;
|
||||
}
|
||||
|
||||
.file-processing-indicator {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=20
|
||||
db.version=21
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
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.assertEquals(document1Id, documents.getJsonObject(0).getString("id"));
|
||||
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(2, tags.size());
|
||||
Assert.assertEquals(tag2Id, tags.getJsonObject(0).getString("id"));
|
||||
|
Loading…
Reference in New Issue
Block a user