Merge branch 'develop' into feature/wysiwyg-editor

* develop:
  Renamed Stories.render_action() to render_json_action()
  Added account image with link to https://gravatar.com
  Added HTML sanitizer for comments and stories. Fixes issue 64.
  Added button for easily adding a username to the troll filter
  Implemented “gaslighting” of troll comments, i.e. hiding a comment of a known troll for everyone else but themselves
  Removed misleading trailing “(Server)” from console output
  Fixes issue 143
  Separated rendering of core and custom CSS with Less and added more detailed error output
  Improved display of console messages
  Added bottom margin after article meta in case there is no title
  Introduced another value for the color of borders, vertical and horizontal lines

Conflicts:
	code/Global/0.node.js
	code/Global/Global.js
	code/Site/$Site.skin
	code/Story/$Story.skin
	code/Story/Story.js
	code/Story/Story.skin
This commit is contained in:
Tobi Schäfer 2015-03-25 10:56:53 +01:00
commit 95d3a7f6cb
13 changed files with 14822 additions and 123 deletions

View file

@ -221,9 +221,10 @@
</target>
<target name="server" depends="init">
<concat destfile="code/Global/0.node.js">
<header>module = {};</header>
<fileset file="node_modules/JSON/json2.js"/>
<exec executable="node_modules/.bin/browserify" output="code/Global/0.node.js">
<arg line="build/server.js -d"/>
</exec>
<concat destfile="code/Global/0.node.js" append="true">
<fileset file="node_modules/less/dist/less-rhino-1.7.5.js"/>
<fileset file="node_modules/marked/lib/marked.js"/>
</concat>

5
build/server.js Normal file
View file

@ -0,0 +1,5 @@
require('JSON/json2.js');
global.node = {
sanitizeHtml: require('sanitize-html')
};

View file

@ -119,6 +119,7 @@ Comment.prototype.getPermission = function(action) {
this.story.getPermission(action) &&
this.status !== Comment.PENDING;
case 'delete':
case 'filter':
return this.story.getPermission.call(this, 'delete');
case 'edit':
return this.status !== Comment.DELETED &&
@ -167,7 +168,17 @@ Comment.prototype.edit_action = function() {
res.data.body = this.renderSkinAsString('Comment#edit');
this.site.renderSkin('Site#page');
return;
};
Comment.prototype.filter_action = function () {
var username = this.creator.name;
var trollFilter = this.site.trollFilter;
if (trollFilter.indexOf(username) < 0) {
trollFilter.push(username);
this.site.setMetadata('trollFilter', trollFilter);
}
res.redirect(req.data.http_referer);
};
/**
*
@ -179,9 +190,14 @@ Comment.prototype.update = function(data) {
}
// Get difference to current content before applying changes
var delta = this.getDelta(data);
this.title = data.title;
this.text = data.text;
this.setMetadata(data);
this.title = this.title ? stripTags(data.title) : String.EMPTY;
if (User.require(User.TRUSTED) || Membership.require(Membership.CONTRIBUTOR)) {
this.text = data.text;
} else {
this.text = this.text ? node.sanitizeHtml(data.text) : String.EMPTY;
}
if (this.story.commentMode === Story.MODERATED) {
this.status = Comment.PENDING;
@ -217,6 +233,14 @@ Comment.prototype.getConfirmExtra = function () {
}
};
Comment.prototype.isGaslighted = function () {
var creatorIsTroll = this.site.trollFilter.indexOf(this.creator.name) > -1;
if (session.user && creatorIsTroll) {
return session.user.name !== this.creator.name;
}
return creatorIsTroll;
};
/**
*
* @param {String} name
@ -245,6 +269,10 @@ Comment.prototype.text_macro = function() {
gettext('This comment was removed by the author.') :
gettext('This comment was removed.'));
res.writeln('</em>');
} else if (this.isGaslighted()) {
res.write('<em>');
res.write('This comment is gaslighted.');
res.write('</em>');
} else {
res.write(this.text);
}
@ -276,3 +304,9 @@ Comment.prototype.modifier_macro = function() {
Comment.prototype.link_macro = function(param, action, text) {
return HopObject.prototype.link_macro.call(this, param, action, text);
}
Comment.prototype.meta_macro = function (param) {
if (this.status === Comment.PUBLIC && !this.isGaslighted()) {
this.renderSkin('Comment#meta');
}
};

View file

@ -10,24 +10,28 @@
<% #content %>
<a id='<% comment.id %>'></a>
<article class='uk-comment uk-margin-top'>
<div class='uk-comment-header'>
<% if <% comment.creator url %> is null then '' else <% comment.creator url prefix="<a href='" suffix="'>" %> %>
<img alt='' class='uk-comment-avatar' src='<% comment.creator gravatar suffix='?s=50&amp;d=mm' %>'>
<% if <% comment.creator url %> is null then '' else </a> %>
<h4 class='uk-comment-title'><% comment.creator %></h4>
<header class='uk-comment-header'>
<% comment.meta %>
<ul class='uk-comment-meta uk-subnav uk-subnav-line'>
<li><% comment.created short %></li>
<% comment.link main "<i class='uk-icon-link'></i>" prefix=<li> suffix=</li> %>
<% comment.link delete "<i class='uk-icon-trash-o'></i>" prefix=<li> suffix=</li> %>
<% comment.link filter "<i class='uk-icon-filter'></i>" prefix=<li> suffix=</li> %>
<% comment.link edit "<i class='uk-icon-pencil'></i>" prefix=<li> suffix=</li> %>
<% if <% param.commentLink %> is false then '' else <% comment.link comment#form "<i class='uk-icon-comment-o'></i> " prefix=<li> suffix=</li> %> %>
</ul>
</div>
</header>
<div class='uk-comment-body'>
<% comment.text | comment.format %>
</div>
</article>
<% #meta %>
<% if <% comment.creator url %> is null then '' else <% comment.creator url prefix="<a href='" suffix="'>" %> %>
<img alt='' class='uk-comment-avatar' src='<% comment.creator gravatar suffix='?s=50&amp;d=mm' %>'>
<% if <% comment.creator url %> is null then '' else </a> %>
<h4 class='uk-comment-title'><% comment.creator %></h4>
<% #comment %>
<% comment.skin Comment#content %>

File diff suppressed because one or more lines are too long

View file

@ -244,7 +244,7 @@ var console = function (type) {
writeln(shellColors[type] + '[' + now + '] [' + type.toUpperCase() + '] [console] ' + argString + '\u001B[0m');
if (typeof res !== 'undefined') {
res.debug('<script>console.' + type + '("%c%s (Server)", "font-style: italic;", ' +
res.debug('<script>console.' + type + '("%c%s", "font-style: italic;", ' +
JSON.stringify(argString) + ');</script>');
}
}

View file

@ -99,6 +99,33 @@
<% site.skin $Site#admin restricted=true %>
<fieldset class='uk-margin-top'>
<legend><% gettext Advanced %></legend>
<div class='uk-form-row'>
<label class='uk-form-label'>
<% gettext 'Troll Filter' %>
</label>
<div class='uk-form-controls'>
<% site.textarea trollFilter rows=5 class='uk-width-1-1' %>
</div>
</div>
<div class='uk-form-row'>
<label class='uk-form-label'>
<% gettext 'Referrer Filter' %>
</label>
<div class='uk-form-controls'>
<% site.textarea spamfilter rows=5 class='uk-width-1-1' %>
<p class="uk-form-help-block">
<% gettext "Enter one filter {0}pattern{1} per line to be applied on every URL in the referrer and backlink lists." '<a href="http://en.wikipedia.org/wiki/Regular_expression">' </a> %>
</p>
</div>
</div>
<div class='uk-form-row'>
<label class='uk-form-label'>
<% gettext Bookmarklet %>
</label>
<div class='uk-form-controls'>
<a class='uk-button' data-uk-tooltip='{pos: "right"}' href="javascript: var siteUrl = '<% site.href %>'; var selection = (window.getSelection) ? window.getSelection() : document.selection.createRange(); selection = selection.text || selection; selection = selection + ''; var url='<% root.static %>../../formica.html?s=' + encodeURIComponent(siteUrl) + '&amp;l=' + encodeURIComponent(location.href) + '&amp;r=' + encodeURIComponent(document.referrer) + '&amp;w=400&amp;h=400&amp;c=' + encodeURIComponent(selection || document.title); window.open(url, 'formica', 'width=630, height=350'); void 0;" title="<% gettext 'Drag to Bookmarks Bar' %>"><% gettext "Post to {0}" <% site.title %> %></a>
</div>
</div>
<div class='uk-form-row'>
<label class='uk-form-label' for='callbackUrl'>
<% gettext 'Callback URL' %>
@ -119,25 +146,6 @@
<% site.diskspace %>
</div>
</div>
<div class='uk-form-row'>
<label class='uk-form-label'>
<% gettext Bookmarklet %>
</label>
<div class='uk-form-controls'>
<a class='uk-button' data-uk-tooltip='{pos: "right"}' href="javascript: var siteUrl = '<% site.href %>'; var selection = (window.getSelection) ? window.getSelection() : document.selection.createRange(); selection = selection.text || selection; selection = selection + ''; var url='<% root.static %>../../formica.html?s=' + encodeURIComponent(siteUrl) + '&amp;l=' + encodeURIComponent(location.href) + '&amp;r=' + encodeURIComponent(document.referrer) + '&amp;w=400&amp;h=400&amp;c=' + encodeURIComponent(selection || document.title); window.open(url, 'formica', 'width=630, height=350'); void 0;" title="<% gettext 'Drag to Bookmarks Bar' %>"><% gettext "Post to {0}" <% site.title %> %></a>
</div>
</div>
</fieldset>
<fieldset class='uk-margin-top'>
<legend><% gettext "Referrer Filter" %></legend>
<div class='uk-form-row'>
<div class='uk-form-controls'>
<% site.textarea spamfilter rows=5 class='uk-width-1-1' %>
<p class="uk-form-help-block">
<% gettext "Enter one filter {0}pattern{1} per line to be applied on every URL in the referrer and backlink lists." '<a href="http://en.wikipedia.org/wiki/Regular_expression">' </a> %>
</p>
</div>
</div>
</fieldset>
<div class='uk-margin-top'>
<button class='uk-button uk-button-primary' type="submit" id="submit" name="save" value="1">
@ -609,6 +617,11 @@ h1 a, .uk-table a {
border-color: @border-color;
}
// Remove the left padding of the first meta subnav item for vertical alignment with the username
h4 + .uk-comment-meta li:first-child {
padding-left: 0;
}
.av-page {
width: 900px; // FIXME: Could we use the `vw` unit already?
}

View file

@ -42,9 +42,10 @@ this.handleMetadata('spamfilter');
this.handleMetadata('tagline');
this.handleMetadata('timeZone');
this.handleMetadata('title');
this.handleMetadata('trollFilter');
/**
* @function
* Ffunction
* @returns {String[]}
* @see defineConstants
*/
@ -367,7 +368,16 @@ Site.prototype.getFormOptions = function(name) {
default:
return HopObject.prototype.getFormOptions.apply(this, arguments);
}
};
Site.prototype.getFormValue = function (name) {
switch (name) {
case 'trollFilter':
var trolls = this.getMetadata('trollFilter');
return trolls ? trolls.join('\n') : String.EMPTY;
}
return HopObject.prototype.getFormValue.apply(this, arguments);
};
/**
* @returns {String}
@ -407,7 +417,10 @@ Site.prototype.update = function(data) {
notificationMode: data.notificationMode || Site.NOBODY,
timeZone: data.timeZone || root.getTimeZone().getID(),
locale: data.locale || root.getLocale().toString(),
spamfilter: data.spamfilter || ''
spamfilter: data.spamfilter || '',
trollFilter: data.trollFilter.split(/\r\n|\r|\n/).filter(function (item) {
return item.length > 0;
})
});
if (User.require(User.PRIVILEGED)) {
@ -934,7 +947,7 @@ Site.prototype.spamfilter_macro = function() {
}
}
return;
}
};
/**
*

View file

@ -53,7 +53,7 @@ Stories.prototype.getPermission = function(action) {
case 'top':
case 'closed':
case 'create':
case 'render':
case 'render.json':
case 'user':
return Site.require(Site.OPEN) && session.user ||
Membership.require(Membership.CONTRIBUTOR) ||
@ -124,7 +124,7 @@ Stories.prototype.top_action = function() {
return;
}
Stories.prototype.render_action = function () {
Stories.prototype.render_json_action = function () {
var content = String(req.postParams.http_post_remainder);
var story = new Story;
story.site = res.handlers.site;

View file

@ -226,7 +226,7 @@ else
if ($(event.currentTarget).hasClass('uk-htmleditor-button-preview')) {
// FIXME: Should we really render the macros via AJAX call?
var raw = encodeURIComponent(editor.editor.getValue());
$.post('<% stories.href render %>', raw)
$.post('<% stories.href render.json %>', raw)
.done(function (data) {
$('.uk-htmleditor-preview div').html(data);
});

View file

@ -241,8 +241,14 @@ Story.prototype.update = function(data) {
// Get difference to current content before applying changes
var delta = this.getDelta(data);
this.title = data.title ? stripTags(data.title.trim()) : String.EMPTY;
this.text = data.text ? data.text.trim() : String.EMPTY;
this.title = data.title ? stripTags(data.title) : String.EMPTY;
if (User.require(User.TRUSTED) || Membership.require(Membership.CONTRIBUTOR)) {
this.text = data.text;
} else {
this.text = this.text ? node.sanitizeHtml(data.text) : String.EMPTY;
}
this.status = data.status || Story.PUBLIC;
this.mode = data.mode || Story.FEATURED;
this.commentMode = data.commentMode || Story.OPEN;

View file

@ -53,12 +53,21 @@
<form id='edit' class='uk-form uk-form-stacked uk-margin-top' method="post">
<input type="hidden" name="digest" id="digest">
<input type="hidden" name="hash" id="hash">
<figure class='uk-float-right' style='height: 0'>
<a href='https://gravatar.com'>
<img alt='' src='<% membership.creator gravatar suffix='?s=160&amp;d=mm' %>' style='border: 1px solid #ddd; border-radius: 4px'>
<figcaption class='uk-text-center'>
<% gettext 'Account Image' %></a>
</figcaption>
</a>
</figure>
<div class='uk-width-2-3'>
<div class='uk-form-row'>
<label class='uk-form-label' for='email'>
<% gettext E-mail %>
</label>
<div class='uk-form-controls'>
<% user.input email type=email class='uk-form uk-width-1-2' %>
<% user.input email type=email class='uk-form uk-width-2-3' %>
<a href='mailto:<% user.email %>'><i class='uk-icon-envelope'></i></a>
</div>
</div>
@ -68,7 +77,7 @@
<i class='uk-icon-info-circle uk-text-muted' title='<% gettext "If you enter a URL here your user name will appear as link next to your posted items." %>' data-uk-tooltip="{pos: 'right'}"></i>
</label>
<div class='uk-form-controls'>
<% user.input url type=url class='uk-form uk-width-1-2' %>
<% user.input url type=url class='uk-form uk-width-2-3' %>
<a href='<% user.url %>'><i class='uk-icon-link'></i></a>
</div>
</div>
@ -76,7 +85,7 @@
<label class='uk-form-label' for='password'>
<% gettext "Password" %>
</label>
<div class='uk-form-controls uk-form-password uk-width-1-2'>
<div class='uk-form-controls uk-form-password uk-width-2-3'>
<input type="password" name="password" id="password" class='uk-width-1-1'>
<a href='javascript:' class='uk-form-password-toggle' data-uk-form-password="{
lblHide: '<% gettext Hide %>',
@ -84,6 +93,7 @@
}"><% gettext Show %></a>
</div>
</div>
</div>
<% user.skin $User#admin restricted=true %>
<div class='uk-form-row uk-margin-top'>
<button class='uk-button uk-button-primary' type="submit" id="submit" name="save" value="1">

View file

@ -33,6 +33,7 @@
"less": "git://github.com/less/less.js#v1.7.5",
"marked": "^0.3.3",
"minifyify": "^6.1.0",
"napa": "^1.1.0"
"napa": "^1.1.0",
"sanitize-html": "^1.6.1"
}
}