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> <span class='vim-nav-hint'>Back to view mode</span>
</a> </a>
<% } else { %> <% } 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> <i class='fa fa-pencil'></i>
<span class='vim-nav-hint'>Edit post</span> <span class='vim-nav-hint'>Edit post</span>
</a> </a>

View File

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

View File

@ -3,7 +3,11 @@
<ul> <ul>
<% for (let post of ctx.results) { %> <% for (let post of ctx.results) { %>
<li> <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) %> <%= ctx.makeThumbnail(post.thumbnailUrl) %>
<span class='type' data-type='<%= post.type %>'> <span class='type' data-type='<%= post.type %>'>
<%= post.type %> <%= post.type %>

View File

@ -3,9 +3,22 @@
--><% for (let user of ctx.results) { %><!-- --><% for (let user of ctx.results) { %><!--
--><li> --><li>
<div class='wrapper'> <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'> <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/> Registered: <%= ctx.makeRelativeTime(user.creationTime) %><br/>
Last seen: <%= ctx.makeRelativeTime(user.lastLoginTime) %> Last seen: <%= ctx.makeRelativeTime(user.lastLoginTime) %>
</div> </div>

View File

@ -24,6 +24,17 @@ class PageController {
run(ctx) { run(ctx) {
this._pageView.unrender(); 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); this._pageView.render(ctx);
} }

View File

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

View File

@ -202,18 +202,19 @@ class TagsController {
topNavController.activate('tags'); topNavController.activate('tags');
pageController.run({ pageController.run({
state: ctx.state, searchQuery: ctx.searchQuery,
clientUrl: '/tags/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
requestPage: page => { requestPage: page => {
const text = ctx.searchQuery.text; const text = ctx.searchQuery.text;
return api.get( return api.get(
`/tags/?query=${text}&page=${page}&pageSize=50`); `/tags/?query=${text}&page=${page}&pageSize=50`);
}, },
clientUrl: '/tags/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
searchQuery: ctx.searchQuery,
headerRenderer: this._tagsHeaderView, headerRenderer: this._tagsHeaderView,
pageRenderer: this._tagsPageView, 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'); topNavController.activate('users');
pageController.run({ pageController.run({
state: ctx.state, searchQuery: ctx.searchQuery,
clientUrl: '/users/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
requestPage: page => { requestPage: page => {
const text = ctx.searchQuery.text; const text = ctx.searchQuery.text;
return api.get( return api.get(
`/users/?query=${text}&page=${page}&pageSize=30`); `/users/?query=${text}&page=${page}&pageSize=30`);
}, },
clientUrl: '/users/' + misc.formatSearchQuery({
text: ctx.searchQuery.text, page: '{page}'}),
searchQuery: ctx.searchQuery,
headerRenderer: this._usersHeaderView, headerRenderer: this._usersHeaderView,
pageRenderer: this._usersPageView, pageRenderer: this._usersPageView,
pageContext: {
canViewUsers: api.hasPrivilege('users:view'),
},
}); });
} }

View File

@ -19,6 +19,10 @@ class PostReadonlySidebarControl {
post: this._post, post: this._post,
getTagCategory: this._getTagCategory, getTagCategory: this._getTagCategory,
getTagUsages: this._getTagUsages, 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 upvoteButton = sourceNode.querySelector('.upvote');
const downvoteButton = sourceNode.querySelector('.downvote') const downvoteButton = sourceNode.querySelector('.downvote')
@ -29,12 +33,16 @@ class PostReadonlySidebarControl {
const fitWidthButton = sourceNode.querySelector('.fit-width') const fitWidthButton = sourceNode.querySelector('.fit-width')
const fitHeightButton = sourceNode.querySelector('.fit-height'); const fitHeightButton = sourceNode.querySelector('.fit-height');
upvoteButton.addEventListener( if (upvoteButton) {
'click', this._eventRequestProxy( upvoteButton.addEventListener(
() => this._setScore(this._post.ownScore === 1 ? 0 : 1))); 'click', this._eventRequestProxy(
downvoteButton.addEventListener( () => this._setScore(this._post.ownScore === 1 ? 0 : 1)));
'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) { if (addFavButton) {
addFavButton.addEventListener( addFavButton.addEventListener(

View File

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

View File

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

View File

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