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:
commit
95d3a7f6cb
13 changed files with 14822 additions and 123 deletions
|
@ -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
5
build/server.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
require('JSON/json2.js');
|
||||
|
||||
global.node = {
|
||||
sanitizeHtml: require('sanitize-html')
|
||||
};
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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&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&d=mm' %>'>
|
||||
<% if <% comment.creator url %> is null then '' else </a> %>
|
||||
<h4 class='uk-comment-title'><% comment.creator %></h4>
|
||||
|
||||
<% #comment %>
|
||||
<% comment.skin Comment#content %>
|
||||
|
||||
|
|
14616
code/Global/0.node.js
14616
code/Global/0.node.js
File diff suppressed because one or more lines are too long
|
@ -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>');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) + '&l=' + encodeURIComponent(location.href) + '&r=' + encodeURIComponent(document.referrer) + '&w=400&h=400&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) + '&l=' + encodeURIComponent(location.href) + '&r=' + encodeURIComponent(document.referrer) + '&w=400&h=400&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?
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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&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">
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue