client/general: respect privileges

This commit is contained in:
rr- 2016-06-11 09:59:29 +02:00
parent 0842d90ca2
commit 7566d2e0f3
12 changed files with 141 additions and 66 deletions

View file

@ -28,7 +28,11 @@
<span class='vim-nav-hint'>Back to view mode</span>
</a>
<% } else { %>
<a href='/post/<%= ctx.post.id %>/edit'>
<% if (ctx.canEditPosts) { %>
<a href='/post/<%= ctx.post.id %>/edit'>
<% } else { %>
<a class='inactive'>
<% } %>
<i class='fa fa-pencil'></i>
<span class='vim-nav-hint'>Edit post</span>
</a>

View file

@ -39,30 +39,42 @@
<section class='social'>
<div class='score'>
<a class='upvote' href='#'>
<% if (ctx.post.ownScore == 1) { %>
<i class='fa fa-thumbs-up'></i>
<% } else { %>
<% if (ctx.canScorePosts) { %>
<a class='upvote' href='#'>
<% if (ctx.post.ownScore == 1) { %>
<i class='fa fa-thumbs-up'></i>
<% } else { %>
<i class='fa fa-thumbs-o-up'></i>
<% } %>
<span class='hint'></span>
</a>
<% } else { %>
<a class='upvote inactive'>
<i class='fa fa-thumbs-o-up'></i>
<% } %>
<span class='hint'></span>
</a>
</a>
<% } %>
<span class='value'><%= ctx.post.score %></span>
<a class='downvote' href='#'>
<% if (ctx.post.ownScore == -1) { %>
<i class='fa fa-thumbs-down'></i>
<% } else { %>
<i class='fa fa-thumbs-o-down'></i>
<% } %>
<span class='hint'></span>
</a>
<% if (ctx.canScorePosts) { %>
<a class='downvote' href='#'>
<% if (ctx.post.ownScore == -1) { %>
<i class='fa fa-thumbs-down'></i>
<% } else { %>
<i class='fa fa-thumbs-o-down'></i>
<% } %>
<span class='hint'></span>
</a>
<% } %>
</div>
<div class='fav'>
<% if (ctx.post.ownFavorite) { %>
<a class='remove-favorite' href='#'><i class='fa fa-heart'></i></a>
<% if (ctx.canFavoritePosts) { %>
<% if (ctx.post.ownFavorite) { %>
<a class='remove-favorite' href='#'><i class='fa fa-heart'></i></a>
<% } else { %>
<a class='add-favorite' href='#'><i class='fa fa-heart-o'></i></a>
<% } %>
<% } else { %>
<a class='add-favorite' href='#'><i class='fa fa-heart-o'></i></a>
<a class='add-favorite inactive'><i class='fa fa-heart-o'></i></a>
<% } %>
<span class='value'><%= ctx.post.favoriteCount %></span>
</div>
@ -74,12 +86,20 @@
<ul><!--
--><% for (let tag of ctx.post.tags) { %><!--
--><li><!--
--><% if (ctx.canViewTags) { %><!--
--><a href='/tag/<%= tag %>' class='tag-<%= ctx.getTagCategory(tag) %>'><!--
--><i class='fa fa-tag'></i><!--
--></a><!--
--><a href='/posts/text=<%= tag %>' class='tag-<%= ctx.getTagCategory(tag) %>'><!--
--><% } %><!--
--><% if (ctx.canListPosts) { %><!--
--></a><!--
--><% } %><!--
--><% if (ctx.canListPosts) { %><!--
--><a href='/posts/text=<%= tag %>' class='tag-<%= ctx.getTagCategory(tag) %>'><!--
--><% } %><!--
--><%= tag %><!--
--></a><!--
--><% if (ctx.canListPosts) { %><!--
--></a><!--
--><% } %><!--
--><span class='count'><%= ctx.getTagUsages(tag) %></span><!--
--></li><!--
--><% } %><!--

View file

@ -3,7 +3,11 @@
<ul>
<% for (let post of ctx.results) { %>
<li>
<a href='/post/<%= post.id %>' title='@<%= post.id %> (<%= post.type %>)&#10;&#10;Tags: <%= post.tags.map(tag => '#' + tag).join(' ') %>'>
<% if (ctx.canViewPosts) { %>
<a href='/post/<%= post.id %>' title='@<%= post.id %> (<%= post.type %>)&#10;&#10;Tags: <%= post.tags.map(tag => '#' + tag).join(' ') %>'>
<% } else { %>
<a>
<% } %>
<%= ctx.makeThumbnail(post.thumbnailUrl) %>
<span class='type' data-type='<%= post.type %>'>
<%= post.type %>

View file

@ -3,9 +3,22 @@
--><% for (let user of ctx.results) { %><!--
--><li>
<div class='wrapper'>
<a class='image' href='/user/<%= user.name %>'><%= ctx.makeThumbnail(user.avatarUrl) %></a>
<% if (ctx.canViewUsers) { %>
<a class='image' href='/user/<%= user.name %>'>
<% } %>
<%= ctx.makeThumbnail(user.avatarUrl) %>
<% if (ctx.canViewUsers) { %>
</a>
<% } %>
<div class='details'>
<a href='/user/<%= user.name %>'><%= user.name %></a><br/>
<% if (ctx.canViewUsers) { %>
<a href='/user/<%= user.name %>'>
<% } %>
<%= user.name %>
<% if (ctx.canViewUsers) { %>
</a>
<% } %>
<br/>
Registered: <%= ctx.makeRelativeTime(user.creationTime) %><br/>
Last seen: <%= ctx.makeRelativeTime(user.lastLoginTime) %>
</div>

View file

@ -24,6 +24,17 @@ class PageController {
run(ctx) {
this._pageView.unrender();
ctx.headerContext = ctx.headerContext || {};
Object.assign(ctx.headerContext, {
searchQuery: ctx.searchQuery,
});
ctx.pageContext = ctx.pageContext || {};
Object.assign(ctx.pageContext, {
searchQuery: ctx.searchQuery,
});
this._pageView.render(ctx);
}

View file

@ -42,7 +42,9 @@ class PostsController {
topNavController.activate('posts');
pageController.run({
state: ctx.state,
searchQuery: ctx.searchQuery,
clientUrl: '/posts/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
requestPage: page => {
const text = this._decorateSearchQuery(ctx.searchQuery.text);
return api.get(
@ -50,11 +52,11 @@ class PostsController {
`id,type,tags,score,favoriteCount,` +
`commentCount,thumbnailUrl`);
},
clientUrl: '/posts/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
searchQuery: ctx.searchQuery,
headerRenderer: this._postsHeaderView,
pageRenderer: this._postsPageView,
pageContext: {
canViewPosts: api.hasPrivilege('posts:view'),
}
});
}
@ -71,6 +73,7 @@ class PostsController {
editMode: editMode,
nextPostId: aroundResponse.next ? aroundResponse.next.id : null,
prevPostId: aroundResponse.prev ? aroundResponse.prev.id : null,
canEditPosts: api.hasPrivilege('posts:edit'),
});
}, response => {
this._emptyView.render();

View file

@ -202,18 +202,19 @@ class TagsController {
topNavController.activate('tags');
pageController.run({
state: ctx.state,
searchQuery: ctx.searchQuery,
clientUrl: '/tags/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
requestPage: page => {
const text = ctx.searchQuery.text;
return api.get(
`/tags/?query=${text}&page=${page}&pageSize=50`);
},
clientUrl: '/tags/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
searchQuery: ctx.searchQuery,
headerRenderer: this._tagsHeaderView,
pageRenderer: this._tagsPageView,
canEditTagCategories: api.hasPrivilege('tagCategories:edit'),
headerContext: {
canEditTagCategories: api.hasPrivilege('tagCategories:edit'),
},
});
}
}

View file

@ -65,17 +65,19 @@ class UsersController {
topNavController.activate('users');
pageController.run({
state: ctx.state,
searchQuery: ctx.searchQuery,
clientUrl: '/users/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
requestPage: page => {
const text = ctx.searchQuery.text;
return api.get(
`/users/?query=${text}&page=${page}&pageSize=30`);
},
clientUrl: '/users/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
searchQuery: ctx.searchQuery,
headerRenderer: this._usersHeaderView,
pageRenderer: this._usersPageView,
pageContext: {
canViewUsers: api.hasPrivilege('users:view'),
},
});
}

View file

@ -19,6 +19,10 @@ class PostReadonlySidebarControl {
post: this._post,
getTagCategory: this._getTagCategory,
getTagUsages: this._getTagUsages,
canListPosts: api.hasPrivilege('posts:list'),
canScorePosts: api.hasPrivilege('posts:score'),
canFavoritePosts: api.hasPrivilege('posts:favorite'),
canViewTags: api.hasPrivilege('tags:view'),
});
const upvoteButton = sourceNode.querySelector('.upvote');
const downvoteButton = sourceNode.querySelector('.downvote')
@ -29,12 +33,16 @@ class PostReadonlySidebarControl {
const fitWidthButton = sourceNode.querySelector('.fit-width')
const fitHeightButton = sourceNode.querySelector('.fit-height');
upvoteButton.addEventListener(
'click', this._eventRequestProxy(
() => this._setScore(this._post.ownScore === 1 ? 0 : 1)));
downvoteButton.addEventListener(
'click', this._eventRequestProxy(
() => this._setScore(this._post.ownScore === -1 ? 0 : -1)));
if (upvoteButton) {
upvoteButton.addEventListener(
'click', this._eventRequestProxy(
() => this._setScore(this._post.ownScore === 1 ? 0 : 1)));
}
if (downvoteButton) {
downvoteButton.addEventListener(
'click', this._eventRequestProxy(
() => this._setScore(this._post.ownScore === -1 ? 0 : -1)));
}
if (addFavButton) {
addFavButton.addEventListener(

View file

@ -1,6 +1,7 @@
'use strict';
require('../util/polyfill.js');
const api = require('../api.js');
const templates = require('../templates.js');
const tags = require('../tags.js');
const events = require('../events.js');
@ -137,25 +138,35 @@ function makeColorInput(options) {
}
function makePostLink(id) {
return makeNonVoidElement('a', {
'href': '/post/' + id,
}, '@' + id);
const text = '@' + id;
return api.hasPrivilege('posts:view') ?
makeNonVoidElement('a', {'href': '/post/' + id}, text) :
text;
}
function makeTagLink(name) {
const tag = tags.getTagByName(name);
let category = tag ? tag.category : 'unknown';
return makeNonVoidElement('a', {
'href': '/tag/' + name,
'class': 'tag-' + category,
}, name);
const category = tag ? tag.category : 'unknown';
return api.hasPrivilege('tags:view') ?
makeNonVoidElement(
'a', {
'href': '/tag/' + name,
'class': 'tag-' + category,
}, name) :
makeNonVoidElement(
'span', {
'class': 'tag-' + category,
},
name);
}
function makeUserLink(user) {
let link = api.hasPrivilege('users:view') ?
makeNonVoidElement('a', {'href': '/user/' + user.name}, user.name) :
user.name;
return makeNonVoidElement('span', {class: 'user'},
makeThumbnail(user.avatarUrl) +
makeNonVoidElement(
'a', {'href': '/user/' + user.name}, user.name));
link);
}
function makeFlexboxAlign(options) {

View file

@ -24,9 +24,8 @@ class EndlessPageView {
this._active = true;
this._working = 0;
let headerRendererCtx = ctx;
headerRendererCtx.target = pageHeaderHolder;
ctx.headerRenderer.render(headerRendererCtx);
ctx.headerContext.target = pageHeaderHolder;
ctx.headerRenderer.render(ctx.headerContext);
const threshold = window.innerHeight / 3;
@ -115,10 +114,10 @@ class EndlessPageView {
});
pageNode.setAttribute('data-page', pageNumber);
let pageRendererCtx = response;
pageRendererCtx.target = pageNode.querySelector(
Object.assign(ctx.pageContext, response);
ctx.pageContext.target = pageNode.querySelector(
'.page-content-holder');
ctx.pageRenderer.render(pageRendererCtx);
ctx.pageRenderer.render(ctx.pageContext);
if (pageNumber < this.minPageShown ||
this.minPageShown === null) {

View file

@ -69,14 +69,13 @@ class ManualPageView {
const pageNav = source.querySelector('.page-nav');
const currentPage = ctx.searchQuery.page;
let headerRendererCtx = ctx;
headerRendererCtx.target = pageHeaderHolder;
ctx.headerRenderer.render(headerRendererCtx);
ctx.headerContext.target = pageHeaderHolder;
ctx.headerRenderer.render(ctx.headerContext);
ctx.requestPage(currentPage).then(response => {
let pageRendererCtx = response;
pageRendererCtx.target = pageContentHolder;
ctx.pageRenderer.render(pageRendererCtx);
Object.assign(ctx.pageContext, response);
ctx.pageContext.target = pageContentHolder;
ctx.pageRenderer.render(ctx.pageContext);
const totalPages = Math.ceil(response.total / response.pageSize);
const pageNumbers = _getVisiblePageNumbers(currentPage, totalPages);