Document sharing (client), better state handling for file view

This commit is contained in:
jendib 2013-08-15 15:23:41 +02:00
parent ed85909d00
commit ea1e226b50
11 changed files with 134 additions and 52 deletions

View File

@ -1,2 +1 @@
- Share a document with a link (client)
- Advanced search: shared documents (client/server) - Advanced search: shared documents (client/server)

View File

@ -182,11 +182,15 @@ public class FileResource extends BaseResource {
authenticate(); authenticate();
// Check document visibility // Check document visibility
DocumentDao documentDao = new DocumentDao(); try {
Document document = documentDao.getDocument(documentId); DocumentDao documentDao = new DocumentDao();
ShareDao shareDao = new ShareDao(); Document document = documentDao.getDocument(documentId);
if (!shareDao.checkVisibility(document, principal.getId(), shareId)) { ShareDao shareDao = new ShareDao();
throw new ForbiddenClientException(); if (!shareDao.checkVisibility(document, principal.getId(), shareId)) {
throw new ForbiddenClientException();
}
} catch (NoResultException e) {
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", documentId));
} }
FileDao fileDao = new FileDao(); FileDao fileDao = new FileDao();

View File

@ -8,15 +8,13 @@ App.controller('FileView', function($dialog, $state, $stateParams) {
keyboard: true, keyboard: true,
templateUrl: 'partial/docs/file.view.html', templateUrl: 'partial/docs/file.view.html',
controller: function($scope, $state, $stateParams, Restangular, dialog) { controller: function($scope, $state, $stateParams, Restangular, dialog) {
$scope.id = $stateParams.fileId;
// Load files // Load files
Restangular.one('file').getList('list', { id: $stateParams.id }).then(function(data) { Restangular.one('file').getList('list', { id: $stateParams.id }).then(function(data) {
$scope.files = data.files; $scope.files = data.files;
// Search current file // Search current file
_.each($scope.files, function(value, key, list) { _.each($scope.files, function(value) {
if (value.id == $scope.id) { if (value.id == $stateParams.fileId) {
$scope.file = value; $scope.file = value;
} }
}); });
@ -26,8 +24,8 @@ App.controller('FileView', function($dialog, $state, $stateParams) {
* Navigate to the next file. * Navigate to the next file.
*/ */
$scope.nextFile = function() { $scope.nextFile = function() {
_.each($scope.files, function(value, key, list) { _.each($scope.files, function(value, key) {
if (value.id == $scope.id) { if (value.id == $stateParams.fileId) {
var next = $scope.files[key + 1]; var next = $scope.files[key + 1];
if (next) { if (next) {
dialog.close({}); dialog.close({});
@ -41,8 +39,8 @@ App.controller('FileView', function($dialog, $state, $stateParams) {
* Navigate to the previous file. * Navigate to the previous file.
*/ */
$scope.previousFile = function() { $scope.previousFile = function() {
_.each($scope.files, function(value, key, list) { _.each($scope.files, function(value, key) {
if (value.id == $scope.id) { if (value.id == $stateParams.fileId) {
var previous = $scope.files[key - 1]; var previous = $scope.files[key - 1];
if (previous) { if (previous) {
dialog.close({}); dialog.close({});
@ -56,7 +54,7 @@ App.controller('FileView', function($dialog, $state, $stateParams) {
* Open the file in a new window. * Open the file in a new window.
*/ */
$scope.openFile = function() { $scope.openFile = function() {
window.open('api/file/' + $scope.id + '/data'); window.open('api/file/' + $stateParams.fileId + '/data');
}; };
/** /**
@ -65,6 +63,14 @@ App.controller('FileView', function($dialog, $state, $stateParams) {
$scope.closeFile = function () { $scope.closeFile = function () {
dialog.close(); dialog.close();
}; };
// Close the dialog when the user exits this state
var off = $scope.$on('$stateChangeStart', function(event, toState){
if (dialog.isOpen()) {
dialog.close(toState.name == 'document.view.file' ? {} : null);
}
off();
});
} }
}); });

View File

@ -31,6 +31,22 @@ var App = angular.module('share',
controller: 'Share' controller: 'Share'
} }
} }
})
.state('share.file', {
url: '/file/:fileId',
views: {
'file': {
controller: 'FileView'
}
}
})
.state('403', {
url: '/403',
views: {
'page': {
templateUrl: 'partial/share/403.html'
}
}
}); });
// Configuring Restangular // Configuring Restangular

View File

@ -0,0 +1,7 @@
'use strict';
/**
* Main controller.
*/
App.controller('Main', function() {
});

View File

@ -10,7 +10,7 @@ App.controller('Share', function($scope, $state, $stateParams, Restangular) {
$scope.document = data; $scope.document = data;
}, function (response) { }, function (response) {
if (response.data.status == 403) { if (response.data.status == 403) {
// TODO Sharing no more valid $state.transitionTo('403');
} }
}); });
@ -19,4 +19,11 @@ App.controller('Share', function($scope, $state, $stateParams, Restangular) {
.then(function (data) { .then(function (data) {
$scope.files = data.files; $scope.files = data.files;
}); });
/**
* Navigate to the selected file.
*/
$scope.openFile = function (file) {
$state.transitionTo('share.file', { documentId: $stateParams.documentId, shareId: $stateParams.shareId, fileId: file.id })
};
}); });

View File

@ -14,8 +14,8 @@
</div> </div>
<img ng-show="file.mimetype == 'image/png' || file.mimetype == 'image/jpeg' || file.mimetype == 'image/gif'" ng-src="api/file/{{ id }}/data" /> <img ng-if="$stateParams.fileId && (file.mimetype == 'image/png' || file.mimetype == 'image/jpeg' || file.mimetype == 'image/gif')" ng-src="api/file/{{ $stateParams.fileId }}/data" />
<div class="text-center" ng-show="file.mimetype == 'application/pdf'"> <div class="text-center" ng-if="$stateParams.fileId && file.mimetype == 'application/pdf'">
<img ng-src="api/file/{{ id }}/data?thumbnail=true" /> <img ng-src="api/file/{{ $stateParams.fileId }}/data?thumbnail=true" />
</div> </div>

View File

@ -0,0 +1,4 @@
<div class="hero-unit">
<h1>Not authorized</h1>
<p>The document you are trying to view is not shared anymore</p>
</div>

View File

@ -0,0 +1,4 @@
<div class="hero-unit">
<h1>Sismics Docs</h1>
<p>Ask a shared document link to access it</p>
</div>

View File

@ -1 +1,24 @@
Shared document <div class="row-fluid">
<div class="well span12">
<div class="page-header">
<h1>{{ document.title }} <small>{{ document.create_date | date: 'yyyy-MM-dd' }}</small></h1>
<ul class="inline">
<li ng-repeat="tag in document.tags"><span class="label label-info" ng-style="{ 'background': tag.color }">{{ tag.name }}</span></li>
</ul>
</div>
<p ng-bind-html="document.description | newline"></p>
<ul class="thumbnails thumbnails-file" ui-sortable="fileSortableOptions" ng-model="files" ng-show="files.length > 0">
<li class="span2 text-center thumbnail-container" ng-repeat="file in files">
<div class="thumbnail">
<a ng-click="openFile(file)">
<img class="thumbnail-file" ng-src="api/file/{{ file.id }}/data?thumbnail=true&share={{ $stateParams.shareId }}" tooltip="{{ file.mimetype }}" tooltip-placement="top" />
</a>
</div>
</li>
</ul>
<div ui-view="file"></div>
</div>
</div>

View File

@ -1,35 +1,47 @@
<!DOCTYPE html> <!DOCTYPE html>
<html ng-app="share"> <html ng-app="share">
<head> <head>
<title>Sismics Docs</title> <title>Sismics Docs</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="favicon.png" /> <link rel="shortcut icon" href="favicon.png" />
<link rel="stylesheet" href="style/bootstrap.css" type="text/css" /> <link rel="stylesheet" href="style/bootstrap.css" type="text/css" />
<script> <link rel="stylesheet/less" href="style/main.less" type="text/css" />
less = { <script>
env: 'development', // or "production" less = {
async: false, // load imports async env: 'development', // or "production"
fileAsync: false, // load imports async when in a page under a file protocol async: false, // load imports async
poll: 1000, // when in watch mode, time in ms between polls fileAsync: false, // load imports async when in a page under a file protocol
dumpLineNumbers: 'all' // or "mediaQuery" or "comments" poll: 1000, // when in watch mode, time in ms between polls
}; dumpLineNumbers: 'all' // or "mediaQuery" or "comments"
</script> };
<script src="lib/jquery.js" type="text/javascript"></script> </script>
<script src="lib/less.js" type="text/javascript"></script> <script src="lib/jquery.js" type="text/javascript"></script>
<script src="lib/underscore.js" type="text/javascript"></script> <script src="lib/less.js" type="text/javascript"></script>
<script src="lib/angular/angular.js" type="text/javascript"></script> <script src="lib/underscore.js" type="text/javascript"></script>
<script src="lib/angular/angular-sanitize.js" type="text/javascript"></script> <script src="lib/angular/angular.js" type="text/javascript"></script>
<script src="lib/angular/angular-mobile.js" type="text/javascript"></script> <script src="lib/angular/angular-sanitize.js" type="text/javascript"></script>
<script src="lib/angular.ui-router.js" type="text/javascript"></script> <script src="lib/angular/angular-mobile.js" type="text/javascript"></script>
<script src="lib/angular.ui-bootstrap.js" type="text/javascript"></script> <script src="lib/angular.ui-router.js" type="text/javascript"></script>
<script src="lib/angular.ui-utils.js" type="text/javascript"></script> <script src="lib/angular.ui-bootstrap.js" type="text/javascript"></script>
<script src="lib/angular.restangular.js" type="text/javascript"></script> <script src="lib/angular.ui-utils.js" type="text/javascript"></script>
<script src="app/share/app.js" type="text/javascript"></script> <script src="lib/angular.restangular.js" type="text/javascript"></script>
<script src="app/share/controller/Share.js" type="text/javascript"></script> <script src="app/share/app.js" type="text/javascript"></script>
</head> <script src="app/share/controller/Main.js" type="text/javascript"></script>
<body> <script src="app/share/controller/Share.js" type="text/javascript"></script>
<main ui-view="page"> <script src="app/share/controller/FileView.js" type="text/javascript"></script>
</main> <script src="app/docs/filter/Newline.js" type="text/javascript"></script>
</body> </head>
<body>
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="#">Sismics Docs</a>
</div>
</div>
</div>
<main ui-view="page">
</main>
</body>
</html> </html>