Commit f42bf08e authored by Randall Leeds's avatar Randall Leeds

Merge pull request #1727 from hypothesis/sticky-visibility-setting-alt-2

Sticky privacy on the browser
parents 3931e1e3 57ae83d4
...@@ -172,11 +172,6 @@ class AppController ...@@ -172,11 +172,6 @@ class AppController
annotator.addPlugin 'Permissions', annotator.addPlugin 'Permissions',
user: token.userId user: token.userId
userAuthorize: authorizeAction userAuthorize: authorizeAction
permissions:
read: [token.userId]
update: [token.userId]
delete: [token.userId]
admin: [token.userId]
$scope.$apply -> $scope.$apply ->
$scope.persona = token.userId $scope.persona = token.userId
reset() reset()
......
privacy = ->
levels = ['Public', 'Only Me']
link: (scope, elem, attrs, controller) ->
return unless controller?
controller.$formatters.push (permissions) ->
return unless permissions?
if 'group:__world__' in (permissions.read or [])
'Public'
else
'Only Me'
controller.$parsers.push (privacy) ->
return unless privacy?
permissions = controller.$modelValue
if privacy is 'Public'
if permissions.read
unless 'group:__world__' in permissions.read
permissions.read.push 'group:__world__'
else
permissions.read = ['group:__world__']
else
read = permissions.read or []
read = (role for role in read when role isnt 'group:__world__')
permissions.read = read
permissions
controller.$render = ->
scope.level = controller.$viewValue
scope.levels = levels
scope.setLevel = (level) ->
controller.$setViewValue level
controller.$render()
require: '?ngModel'
restrict: 'E'
scope: {}
templateUrl: 'privacy.html'
repeatAnim = -> repeatAnim = ->
restrict: 'A' restrict: 'A'
scope: scope:
...@@ -95,7 +51,6 @@ match = -> ...@@ -95,7 +51,6 @@ match = ->
angular.module('h') angular.module('h')
.directive('privacy', privacy)
.directive('repeatAnim', repeatAnim) .directive('repeatAnim', repeatAnim)
.directive('whenscrolled', whenscrolled) .directive('whenscrolled', whenscrolled)
.directive('match', match) .directive('match', match)
...@@ -184,7 +184,9 @@ AnnotationController = [ ...@@ -184,7 +184,9 @@ AnnotationController = [
# If replying to a public annotation make the response public. # If replying to a public annotation make the response public.
if 'group:__world__' in (model.permissions.read or []) if 'group:__world__' in (model.permissions.read or [])
reply.permissions.read.push('group:__world__') reply.permissions.read = ['group:__world__']
else
reply.permissions.read = [model.user]
###* ###*
# @ngdoc method # @ngdoc method
...@@ -267,6 +269,10 @@ AnnotationController = [ ...@@ -267,6 +269,10 @@ AnnotationController = [
# Save highlights once logged in. # Save highlights once logged in.
if highlight and this.isHighlight() if highlight and this.isHighlight()
if model.user if model.user
model.permissions.read = [model.user]
model.permissions.update = [model.user]
model.permissions.delete = [model.user]
model.permissions.admin = [model.user]
annotator.publish 'annotationCreated', model annotator.publish 'annotationCreated', model
highlight = false # skip this on future updates highlight = false # skip this on future updates
else else
......
privacy = ['$window', ($window) ->
VISIBILITY_KEY ='hypothesis.visibility'
VISIBILITY_PUBLIC = 'public'
VISIBILITY_PRIVATE = 'private'
levels = [
{name: VISIBILITY_PUBLIC, text: 'Public'}
{name: VISIBILITY_PRIVATE, text: 'Only Me'}
]
getLevel = (name) ->
for level in levels
if level.name == name
return level
undefined
# Detection is needed because we run often as a third party widget and
# third party storage blocking often blocks cookies and local storage
# https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
storage = do ->
key = 'hypothesis.testKey'
try
$window.localStorage.setItem key, key
$window.localStorage.removeItem key
$window.localStorage
catch
memoryStorage = {}
getItem: (key) ->
if key of memoryStorage then memoryStorage[key] else null
setItem: (key, value) ->
memoryStorage[key] = value
link: (scope, elem, attrs, controller) ->
return unless controller?
controller.$formatters.push (permissions) ->
return unless permissions?
if 'group:__world__' in (permissions.read or [])
getLevel(VISIBILITY_PUBLIC)
else
getLevel(VISIBILITY_PRIVATE)
controller.$parsers.push (privacy) ->
return unless privacy?
permissions = controller.$modelValue
if privacy.name is VISIBILITY_PUBLIC
permissions.read = ['group:__world__']
else
permissions.read = [attrs.user]
permissions.update = [attrs.user]
permissions.delete = [attrs.user]
permissions.admin = [attrs.user]
permissions
controller.$render = ->
unless controller.$modelValue.read.length
name = storage.getItem VISIBILITY_KEY
level = getLevel(name)
controller.$setViewValue level
scope.level = controller.$viewValue
scope.levels = levels
scope.setLevel = (level) ->
storage.setItem VISIBILITY_KEY, level.name
controller.$setViewValue level
controller.$render()
require: '?ngModel'
restrict: 'E'
scope: {}
templateUrl: 'privacy.html'
]
angular.module('h')
.directive('privacy', privacy)
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
<privacy ng-click="$event.stopPropagation()" <privacy ng-click="$event.stopPropagation()"
ng-if="vm.annotation.permissions && vm.editing && action != 'delete'" ng-if="vm.annotation.permissions && vm.editing && action != 'delete'"
ng-model="vm.annotation.permissions" ng-model="vm.annotation.permissions"
user="{{vm.annotation.user}}"
class="dropdown privacy pull-right" class="dropdown privacy pull-right"
name="privacy" /> name="privacy" />
</aside> </aside>
......
...@@ -3,15 +3,16 @@ ...@@ -3,15 +3,16 @@
role="button" role="button"
class="dropdown-toggle" class="dropdown-toggle"
data-toggle="dropdown"> data-toggle="dropdown">
<i class="small" ng-class="{'h-icon-earth': level == 'Public', 'h-icon-locked': level == 'Only Me'}"></i> <i class="small" ng-class="{'h-icon-earth': level.name == 'public',
{{level}} 'h-icon-locked': level.name == 'private'}"></i>
{{level.text}}
<i class="h-icon-triangle"></i> <i class="h-icon-triangle"></i>
</span> </span>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li ng-repeat="p in levels" ng-click="setLevel(p)"> <li ng-repeat="level in levels" ng-click="setLevel(level)">
<i class="small" ng-class="{'h-icon-earth': p == 'Public', <i class="small" ng-class="{'h-icon-earth': level.name == 'public',
'h-icon-locked': p == 'Only Me'}"></i> 'h-icon-locked': level.name == 'private'}"></i>
{{p}} {{level.text}}
</li> </li>
</ul> </ul>
</div> </div>
assert = chai.assert
VISIBILITY_KEY ='hypothesis.visibility'
VISIBILITY_PUBLIC = 'public'
VISIBILITY_PRIVATE = 'private'
describe 'h.directives.privacy', ->
$window = null
$scope = null
$compile = null
$injector = null
$element = null
$isolateScope = null
beforeEach module('h')
beforeEach module('h.templates')
describe 'memory fallback', ->
fakeWindow = null
sandbox = null
beforeEach module ($provide) ->
sandbox = sinon.sandbox.create()
fakeWindow = {
localStorage: undefined
}
$provide.value '$window', fakeWindow
return
afterEach ->
sandbox.restore()
describe 'has memory fallback', ->
$scope2 = null
beforeEach inject (_$compile_, _$rootScope_) ->
$compile = _$compile_
$scope = _$rootScope_.$new()
$scope2 = _$rootScope_.$new()
it 'stores the default visibility level when it changes', ->
$scope.permissions = {read: ['acct:user@example.com']}
$element = $compile('<privacy ng-model="permissions">')($scope)
$scope.$digest()
$isolateScope = $element.isolateScope()
$isolateScope.setLevel(name: VISIBILITY_PUBLIC)
$scope2.permissions = {read: []}
$element = $compile('<privacy ng-model="permissions">')($scope2)
$scope2.$digest()
# Roundabout way: the storage works because the directive
# could read out the privacy level
readPermissions = $scope2.permissions.read[0]
assert.equal readPermissions, 'group:__world__'
describe 'has localStorage', ->
beforeEach inject (_$compile_, _$rootScope_, _$injector_, _$window_) ->
$compile = _$compile_
$scope = _$rootScope_.$new()
$injector = _$injector_
$window = _$window_
describe 'storage', ->
store = null
beforeEach ->
store = $window.localStorage
it 'stores the default visibility level when it changes', ->
$scope.permissions = {read: ['acct:user@example.com']}
$element = $compile('<privacy ng-model="permissions">')($scope)
$scope.$digest()
$isolateScope = $element.isolateScope()
$isolateScope.setLevel(name: VISIBILITY_PUBLIC)
expected = VISIBILITY_PUBLIC
stored = store.getItem VISIBILITY_KEY
assert.equal stored, expected
describe 'setting permissions', ->
store = null
modelCtrl = null
beforeEach ->
store = $window.localStorage
describe 'when permissions.read is empty', ->
beforeEach ->
store.setItem VISIBILITY_KEY, VISIBILITY_PUBLIC
$scope.permissions = {read: []}
$element = $compile('<privacy ng-model="permissions">')($scope)
$scope.$digest()
$isolateScope = $element.isolateScope()
it 'sets the initial permissions based on the stored privacy level', ->
assert.equal $isolateScope.level.name, VISIBILITY_PUBLIC
it 'does not alter the level on subsequent renderings', ->
modelCtrl = $element.controller('ngModel')
store.setItem VISIBILITY_KEY, VISIBILITY_PRIVATE
$scope.permissions.read = ['acct:user@example.com']
$scope.$digest()
assert.equal $isolateScope.level.name, VISIBILITY_PUBLIC
describe 'when permissions.read is filled', ->
it 'does not alter the level', ->
store.setItem VISIBILITY_KEY, VISIBILITY_PRIVATE
$scope.permissions = {read: ['group:__world__']}
$element = $compile('<privacy ng-model="permissions">')($scope)
$scope.$digest()
$isolateScope = $element.isolateScope()
assert.equal($isolateScope.level.name, VISIBILITY_PUBLIC)
describe 'user attribute', ->
beforeEach ->
$scope.permissions = {read: []}
it 'fills the permissions fields with the given user name', ->
store.setItem VISIBILITY_KEY, VISIBILITY_PRIVATE
$element = $compile('<privacy ng-model="permissions" user="acct:user@example.com">')($scope)
$scope.$digest()
user = "acct:user@example.com"
readPermissions = $scope.permissions.read[0]
updatePermissions = $scope.permissions.update[0]
deletePermissions = $scope.permissions.delete[0]
adminPermissions = $scope.permissions.admin[0]
assert.equal readPermissions, user
assert.equal updatePermissions, user
assert.equal deletePermissions, user
assert.equal adminPermissions, user
it 'puts group_world into the read permissions for public visibility', ->
store.setItem VISIBILITY_KEY, VISIBILITY_PUBLIC
$element = $compile('<privacy ng-model="permissions" user="acct:user@example.com">')($scope)
$scope.$digest()
user = "acct:user@example.com"
readPermissions = $scope.permissions.read[0]
updatePermissions = $scope.permissions.update[0]
deletePermissions = $scope.permissions.delete[0]
adminPermissions = $scope.permissions.admin[0]
assert.equal readPermissions, 'group:__world__'
assert.equal updatePermissions, user
assert.equal deletePermissions, user
assert.equal adminPermissions, user
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment