mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 14:07:55 +01:00
#176: navigation by tag
This commit is contained in:
parent
a0e89103af
commit
77311f42cd
@ -247,7 +247,7 @@ public class DocumentDao {
|
|||||||
tagCriteriaList.add(String.format("dt%d.DOT_ID_C is not null", index));
|
tagCriteriaList.add(String.format("dt%d.DOT_ID_C is not null", index));
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
criteriaList.add(Joiner.on(" OR ").join(tagCriteriaList));
|
criteriaList.add("(" + Joiner.on(" OR ").join(tagCriteriaList) + ")");
|
||||||
}
|
}
|
||||||
if (criteria.getShared() != null && criteria.getShared()) {
|
if (criteria.getShared() != null && criteria.getShared()) {
|
||||||
criteriaList.add("(select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null) > 0");
|
criteriaList.add("(select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null) > 0");
|
||||||
|
@ -499,14 +499,14 @@ public class DocumentResource extends BaseResource {
|
|||||||
break;
|
break;
|
||||||
case "shared":
|
case "shared":
|
||||||
// New shared state criteria
|
// New shared state criteria
|
||||||
if (params[1].equals("yes")) {
|
documentCriteria.setShared(params[1].equals("yes"));
|
||||||
documentCriteria.setShared(true);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case "lang":
|
case "lang":
|
||||||
// New language criteria
|
// New language criteria
|
||||||
if (Constants.SUPPORTED_LANGUAGES.contains(params[1])) {
|
if (Constants.SUPPORTED_LANGUAGES.contains(params[1])) {
|
||||||
documentCriteria.setLanguage(params[1]);
|
documentCriteria.setLanguage(params[1]);
|
||||||
|
} else {
|
||||||
|
documentCriteria.setLanguage(UUID.randomUUID().toString());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "by":
|
case "by":
|
||||||
@ -522,9 +522,7 @@ public class DocumentResource extends BaseResource {
|
|||||||
break;
|
break;
|
||||||
case "workflow":
|
case "workflow":
|
||||||
// New shared state criteria
|
// New shared state criteria
|
||||||
if (params[1].equals("me")) {
|
documentCriteria.setActiveRoute(params[1].equals("me"));
|
||||||
documentCriteria.setActiveRoute(true);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case "full":
|
case "full":
|
||||||
// New full content search criteria
|
// New full content search criteria
|
||||||
|
@ -14,7 +14,6 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
|||||||
$scope.limit = _.isUndefined(localStorage.documentsPageSize) ? '10' : localStorage.documentsPageSize;
|
$scope.limit = _.isUndefined(localStorage.documentsPageSize) ? '10' : localStorage.documentsPageSize;
|
||||||
$scope.search = $state.params.search ? $state.params.search : '';
|
$scope.search = $state.params.search ? $state.params.search : '';
|
||||||
$scope.searchOpened = false;
|
$scope.searchOpened = false;
|
||||||
$scope.setSearch = function (search) { $scope.search = search };
|
|
||||||
$scope.searchDropdownAnchor = angular.element(document.querySelector('.search-dropdown-anchor'));
|
$scope.searchDropdownAnchor = angular.element(document.querySelector('.search-dropdown-anchor'));
|
||||||
$scope.paginationShown = true;
|
$scope.paginationShown = true;
|
||||||
$scope.advsearch = {};
|
$scope.advsearch = {};
|
||||||
@ -81,6 +80,8 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.extractNavigatedTag();
|
||||||
|
|
||||||
// Call API later
|
// Call API later
|
||||||
timeoutPromise = $timeout(function () {
|
timeoutPromise = $timeout(function () {
|
||||||
$scope.loadDocuments();
|
$scope.loadDocuments();
|
||||||
@ -118,22 +119,6 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
|||||||
$state.go('document.view', { id: id });
|
$state.go('document.view', { id: id });
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load tags
|
|
||||||
$scope.tags = [];
|
|
||||||
Restangular.one('tag/list').get().then(function (data) {
|
|
||||||
$scope.tags = data.tags;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find children tags.
|
|
||||||
* @param parent
|
|
||||||
*/
|
|
||||||
$scope.getChildrenTags = function(parent) {
|
|
||||||
return _.filter($scope.tags, function(tag) {
|
|
||||||
return tag.parent === parent;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a promise for typeahead user.
|
* Returns a promise for typeahead user.
|
||||||
*/
|
*/
|
||||||
@ -210,12 +195,18 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
|||||||
$scope.searchOpened = false;
|
$scope.searchOpened = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the search.
|
||||||
|
*/
|
||||||
$scope.clearSearch = function () {
|
$scope.clearSearch = function () {
|
||||||
$scope.advsearch = {};
|
$scope.advsearch = {};
|
||||||
$scope.search = '';
|
$scope.search = '';
|
||||||
$scope.searchOpened = false;
|
$scope.searchOpened = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import an EML file.
|
||||||
|
*/
|
||||||
$scope.importEml = function (file) {
|
$scope.importEml = function (file) {
|
||||||
// Open the import modal
|
// Open the import modal
|
||||||
$uibModal.open({
|
$uibModal.open({
|
||||||
@ -235,4 +226,97 @@ angular.module('docs').controller('Document', function ($scope, $rootScope, $tim
|
|||||||
$scope.loadDocuments();
|
$scope.loadDocuments();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Tag navigation
|
||||||
|
$scope.tags = [];
|
||||||
|
$scope.navigatedFilter = { parent: '' };
|
||||||
|
$scope.navigatedTag = undefined;
|
||||||
|
$scope.navigationEnabled = _.isUndefined(localStorage.navigationEnabled) ?
|
||||||
|
true : localStorage.navigationEnabled === 'true';
|
||||||
|
|
||||||
|
Restangular.one('tag/list').get().then(function (data) {
|
||||||
|
$scope.tags = data.tags;
|
||||||
|
$scope.extractNavigatedTag();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparator for the navigation tag filter.
|
||||||
|
*/
|
||||||
|
$scope.navigatedComparator = function (actual, expected) {
|
||||||
|
if (expected === '') {
|
||||||
|
return _.isUndefined(actual);
|
||||||
|
}
|
||||||
|
return angular.equals(actual, expected);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to a specific tag.
|
||||||
|
*/
|
||||||
|
$scope.navigateToTag = function (tag) {
|
||||||
|
if (tag) {
|
||||||
|
$scope.search = 'tag:' + tag.name;
|
||||||
|
} else {
|
||||||
|
$scope.search = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate one tag up.
|
||||||
|
*/
|
||||||
|
$scope.navigateUp = function () {
|
||||||
|
if (!$scope.navigatedTag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$scope.navigateToTag(_.findWhere($scope.tags, { id: $scope.navigatedTag.parent }));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current navigation breadcrumb.
|
||||||
|
*/
|
||||||
|
$scope.getCurrentNavigation = function () {
|
||||||
|
if (!$scope.navigatedTag) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var nav = [];
|
||||||
|
nav.push($scope.navigatedTag);
|
||||||
|
var current = $scope.navigatedTag;
|
||||||
|
while (current.parent) {
|
||||||
|
current = _.findWhere($scope.tags, { id: current.parent });
|
||||||
|
if (!current) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nav.push(current);
|
||||||
|
}
|
||||||
|
return nav.reverse();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the current navigated tag from the search query.
|
||||||
|
* Called each time the search query changes.
|
||||||
|
*/
|
||||||
|
$scope.extractNavigatedTag = function () {
|
||||||
|
// Find the current tag in the search query
|
||||||
|
var tagFound = /tag:([^ ]*)/.exec($scope.search);
|
||||||
|
if (tagFound) {
|
||||||
|
tagFound = tagFound[1];
|
||||||
|
// We search only for exact match
|
||||||
|
$scope.navigatedTag = _.findWhere($scope.tags, { name: tagFound });
|
||||||
|
} else {
|
||||||
|
$scope.navigatedTag = undefined;
|
||||||
|
}
|
||||||
|
if ($scope.navigatedTag) {
|
||||||
|
$scope.navigatedFilter = {parent: $scope.navigatedTag.id};
|
||||||
|
} else {
|
||||||
|
$scope.navigatedFilter = {parent: ''};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the navigation context.
|
||||||
|
*/
|
||||||
|
$scope.navigationToggle = function () {
|
||||||
|
$scope.navigationEnabled = !$scope.navigationEnabled;
|
||||||
|
localStorage.navigationEnabled = $scope.navigationEnabled;
|
||||||
|
};
|
||||||
});
|
});
|
@ -39,6 +39,8 @@
|
|||||||
"global_quota_warning": "<strong>Warning!</strong> Global quota almost reached at {{ current | number: 0 }}MB ({{ percent | number: 1 }}%) used on {{ total | number: 0 }}MB"
|
"global_quota_warning": "<strong>Warning!</strong> Global quota almost reached at {{ current | number: 0 }}MB ({{ percent | number: 1 }}%) used on {{ total | number: 0 }}MB"
|
||||||
},
|
},
|
||||||
"document": {
|
"document": {
|
||||||
|
"navigation_up": "Go up one level",
|
||||||
|
"toggle_navigation": "Toggle folder navigation",
|
||||||
"search_simple": "Simple search",
|
"search_simple": "Simple search",
|
||||||
"search_fulltext": "Fulltext search",
|
"search_fulltext": "Fulltext search",
|
||||||
"search_creator": "Creator",
|
"search_creator": "Creator",
|
||||||
|
@ -1,16 +1,7 @@
|
|||||||
<script type="text/ng-template" id="tag-tree-item">
|
|
||||||
<span class="btn" ng-style="{ 'background-color': tag.color }"></span>
|
|
||||||
<span class="btn btn-link" ng-click="setSearch('tag:' + tag.name)">
|
|
||||||
{{ tag.name }}
|
|
||||||
</span>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li ng-repeat="tag in getChildrenTags(tag.id)" ng-include="'tag-tree-item'"></li>
|
|
||||||
</ul>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="well well-3d">
|
<div class="well well-3d">
|
||||||
|
<!-- Main new document button -->
|
||||||
<div class="text-center mb-19">
|
<div class="text-center mb-19">
|
||||||
<div class="btn-group" uib-dropdown>
|
<div class="btn-group" uib-dropdown>
|
||||||
<a href="#/document/add" class="btn btn-primary">
|
<a href="#/document/add" class="btn btn-primary">
|
||||||
@ -25,18 +16,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Search (simple and advanced) -->
|
||||||
<div class="row search-dropdown-anchor">
|
<div class="row search-dropdown-anchor">
|
||||||
<div class="col-xs-2 tag-tree-dropdown" uib-dropdown>
|
<div class="col-xs-12 input-group">
|
||||||
<button class="btn btn-block btn-default" uib-dropdown-toggle ng-disabled="disabled">
|
|
||||||
{{ 'document.tags' | translate }} <span class="caret"></span>
|
|
||||||
</button>
|
|
||||||
<ul uib-dropdown-menu class="tag-tree">
|
|
||||||
<li ng-if="getChildrenTags().length == 0">{{ 'document.no_tags' | translate }}</li>
|
|
||||||
<li ng-repeat="tag in getChildrenTags()" ng-include="'tag-tree-item'"></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-xs-10 input-group">
|
|
||||||
<input type="search" class="form-control" ng-attr-placeholder="{{ 'document.search' | translate }}" ng-model="search" />
|
<input type="search" class="form-control" ng-attr-placeholder="{{ 'document.search' | translate }}" ng-model="search" />
|
||||||
<span class="input-group-addon btn" ng-click="openSearch()">
|
<span class="input-group-addon btn" ng-click="openSearch()">
|
||||||
<div uib-dropdown
|
<div uib-dropdown
|
||||||
@ -145,6 +127,39 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Navigation breadcrumb -->
|
||||||
|
<ol class="breadcrumb breadcrumb-navigation pull-left mt-10">
|
||||||
|
<li><a href ng-click="navigateToTag()"><span class="fas fa-home"></span></a></li>
|
||||||
|
<li ng-repeat="tag in getCurrentNavigation()" ng-class="{ active: tag.id == navigatedTag.id }">
|
||||||
|
<a class="label" ng-style="{ 'background-color': tag.color }" ng-if="tag.id != navigatedTag.id" href ng-click="navigateToTag(tag)">{{ tag.name }}</a>
|
||||||
|
<span class="label" ng-style="{ 'background-color': tag.color }" ng-if="tag.id == navigatedTag.id">{{ tag.name }}</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<div class="btn-group mt-10 pull-right">
|
||||||
|
<!-- Go up in the navigation -->
|
||||||
|
<button class="btn btn-default" ng-click="navigateUp()"
|
||||||
|
uib-tooltip="{{ 'document.navigation_up' | translate }}"
|
||||||
|
tooltip-append-to-body="true"
|
||||||
|
ng-if="navigatedTag">
|
||||||
|
<span class="fas fa-chevron-up"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-default" ng-click="navigationToggle()"
|
||||||
|
ng-class="{ active: navigationEnabled }"
|
||||||
|
uib-tooltip="{{ 'document.toggle_navigation' | translate }}"
|
||||||
|
tooltip-append-to-body="true">
|
||||||
|
<span class="far" ng-class="{ 'fa-folder-open': navigationEnabled, 'fa-folder': !navigationEnabled }"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Current folder -->
|
||||||
|
<table class="row table table-hover table-navigation" ng-show="navigationEnabled">
|
||||||
|
<tr ng-repeat="tag in tags | filter: navigatedFilter: navigatedComparator" ng-click="navigateToTag(tag)">
|
||||||
|
<td colspan="2"><span class="fas fa-tags" ng-style="{ color: tag.color }"></span> {{ tag.name }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Document list -->
|
||||||
<table class="row table table-hover table-documents">
|
<table class="row table table-hover table-documents">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -14,8 +14,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-show="document">
|
<div ng-show="document">
|
||||||
<div class="pull-right">
|
<div class="pull-right btn-group">
|
||||||
<div class="dropdown" uib-dropdown ng-class="{ 'btn-group': document.writable }">
|
<button class="btn btn-danger" ng-show="document.writable" ng-click="deleteDocument(document)"><span class="fas fa-trash"></span> {{ 'delete' | translate }}</button>
|
||||||
|
<a href="#/document/edit/{{ document.id }}" ng-show="document.writable" class="btn btn-primary"><span class="fas fa-pencil-alt"></span> {{ 'edit' | translate }}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pull-right dropdown" uib-dropdown>
|
||||||
<button class="btn btn-default" uib-dropdown-toggle>
|
<button class="btn btn-default" uib-dropdown-toggle>
|
||||||
<span class="fas fa-cloud-download-alt"></span>
|
<span class="fas fa-cloud-download-alt"></span>
|
||||||
{{ 'export' | translate }}
|
{{ 'export' | translate }}
|
||||||
@ -35,9 +39,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<button class="btn btn-danger" ng-show="document.writable" ng-click="deleteDocument(document)"><span class="fas fa-trash"></span> {{ 'delete' | translate }}</button>
|
|
||||||
<a href="#/document/edit/{{ document.id }}" ng-show="document.writable" class="btn btn-primary"><span class="fas fa-pencil-alt"></span> {{ 'edit' | translate }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
|
@ -34,6 +34,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
.table-navigation {
|
||||||
|
tr {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
td {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-navigation {
|
||||||
|
background: none;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&> li + li:before {
|
||||||
|
content: '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Documents list
|
// Documents list
|
||||||
.table-documents {
|
.table-documents {
|
||||||
margin-top: 18px !important;
|
margin-top: 18px !important;
|
||||||
|
Loading…
Reference in New Issue
Block a user