mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-16 10:46:14 +01:00
Merge remote-tracking branch 'refs/remotes/origin/master' into develop
This commit is contained in:
@@ -60,12 +60,12 @@
|
||||
"mousetrap": "^1.6.1",
|
||||
"mubsub": "^1.4.0",
|
||||
"nconf": "^0.9.1",
|
||||
"nodebb-plugin-composer-default": "6.0.11",
|
||||
"nodebb-plugin-composer-default": "6.0.12",
|
||||
"nodebb-plugin-dbsearch": "2.0.9",
|
||||
"nodebb-plugin-emoji": "2.1.0",
|
||||
"nodebb-plugin-emoji-android": "2.0.0",
|
||||
"nodebb-plugin-markdown": "8.3.0",
|
||||
"nodebb-plugin-mentions": "2.2.2",
|
||||
"nodebb-plugin-mentions": "2.2.3",
|
||||
"nodebb-plugin-soundpack-default": "1.0.0",
|
||||
"nodebb-plugin-spam-be-gone": "0.5.1",
|
||||
"nodebb-rewards-essentials": "0.0.11",
|
||||
@@ -73,7 +73,7 @@
|
||||
"nodebb-theme-persona": "7.2.20",
|
||||
"nodebb-theme-slick": "1.1.4",
|
||||
"nodebb-theme-vanilla": "8.1.9",
|
||||
"nodebb-widget-essentials": "4.0.1",
|
||||
"nodebb-widget-essentials": "4.0.2",
|
||||
"nodemailer": "4.4.1",
|
||||
"passport": "^0.4.0",
|
||||
"passport-local": "1.0.0",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"available": "Available Widgets",
|
||||
"explanation": "Select a widget from the dropdown menu and then drag and drop it into a template's widget area on the left.",
|
||||
"none-installed": "No widgets found! Activate the essential widgets plugin in the <a href=\"%1\">plugins</a> control panel.",
|
||||
"clone-from": "Clone widgets from",
|
||||
"containers.available": "Available Containers",
|
||||
"containers.explanation": "Drag and drop on top of any active widget",
|
||||
"containers.none": "None",
|
||||
@@ -14,6 +15,8 @@
|
||||
|
||||
"alert.confirm-delete": "Are you sure you wish to delete this widget?",
|
||||
"alert.updated": "Widgets Updated",
|
||||
"alert.update-success": "Successfully updated widgets"
|
||||
"alert.update-success": "Successfully updated widgets",
|
||||
"alert.clone-success": "Successfully cloned widgets",
|
||||
|
||||
"error.select-clone": "Please select a page to clone from"
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
"chat.seven_days": "7天",
|
||||
"chat.thirty_days": "30天",
|
||||
"chat.three_months": "3个月",
|
||||
"chat.delete_message_confirm": "确认删除此消息吗?",
|
||||
"chat.delete_message_confirm": "你确定删除此消息吗?",
|
||||
"chat.add-users-to-room": "向此聊天室中添加成员",
|
||||
"chat.confirm-chat-with-dnd-user": "该用户将其状态设置为DnD(请勿打扰)。 你还想和他们聊天吗?",
|
||||
"composer.compose": "编写帮助",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
@import "./appearance/themes";
|
||||
@import "./extend/plugins";
|
||||
@import "./extend/rewards";
|
||||
@import "./extend/widgets";
|
||||
@import "./advanced/database";
|
||||
@import "./advanced/logs";
|
||||
@import "./advanced/errors";
|
||||
|
||||
@@ -38,10 +38,16 @@
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.mdl-card__supporting-text h2 {
|
||||
.mdl-card__supporting-text {
|
||||
font-size: 1.5rem;
|
||||
margin: 0 auto;
|
||||
|
||||
.mdl-card__title-text {
|
||||
display: inline-block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme].selected .mdl-button {
|
||||
color: black;
|
||||
|
||||
19
public/less/admin/extend/widgets.less
Normal file
19
public/less/admin/extend/widgets.less
Normal file
@@ -0,0 +1,19 @@
|
||||
.page-extend-widgets {
|
||||
[component="clone"] {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
align-content: stretch;
|
||||
|
||||
[component="clone/button"] {
|
||||
flex-grow: 1;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
max-height: 300px;
|
||||
overflow-y: scroll;
|
||||
min-width: 250px;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ define('admin/extend/widgets', ['jqueryui'], function () {
|
||||
$('#widget-selector').trigger('change');
|
||||
|
||||
loadWidgetData();
|
||||
setupCloneButton();
|
||||
};
|
||||
|
||||
function prepareWidgets() {
|
||||
@@ -224,5 +225,51 @@ define('admin/extend/widgets', ['jqueryui'], function () {
|
||||
});
|
||||
}
|
||||
|
||||
function setupCloneButton() {
|
||||
var clone = $('[component="clone"]');
|
||||
var cloneBtn = $('[component="clone/button"]');
|
||||
|
||||
clone.find('.dropdown-menu li').on('click', function () {
|
||||
var template = $(this).find('a').text();
|
||||
cloneBtn.translateHtml('[[admin/extend/widgets:clone-from]] <strong>' + template + '</strong>');
|
||||
cloneBtn.attr('data-template', template);
|
||||
});
|
||||
|
||||
cloneBtn.on('click', function () {
|
||||
var template = cloneBtn.attr('data-template');
|
||||
if (!template) {
|
||||
return app.alertError('[[admin/extend/widgets:error.select-clone]]');
|
||||
}
|
||||
|
||||
var currentTemplate = $('#active-widgets .active.tab-pane[data-template] .area');
|
||||
var templateToClone = $('#active-widgets .tab-pane[data-template="' + template + '"] .area');
|
||||
|
||||
var currentAreas = currentTemplate.map(function () {
|
||||
return $(this).attr('data-location');
|
||||
}).get();
|
||||
|
||||
var areasToClone = templateToClone.map(function () {
|
||||
var location = $(this).attr('data-location');
|
||||
return currentAreas.indexOf(location) !== -1 ? location : undefined;
|
||||
}).get().filter(function (i) { return i; });
|
||||
|
||||
function clone(location) {
|
||||
$('#active-widgets .tab-pane[data-template="' + template + '"] [data-location="' + location + '"]').each(function () {
|
||||
$(this).find('[data-widget]').each(function () {
|
||||
var widget = $(this).clone(true);
|
||||
$('#active-widgets .active.tab-pane[data-template]:not([data-template="global"]) [data-location="' + location + '"] .widget-area').append(widget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
for (var i = 0, ii = areasToClone.length; i < ii; i++) {
|
||||
var location = areasToClone[i];
|
||||
clone(location);
|
||||
}
|
||||
|
||||
app.alertSuccess('[[admin/extend/widgets:alert.clone-success]]');
|
||||
});
|
||||
}
|
||||
|
||||
return Widgets;
|
||||
});
|
||||
|
||||
@@ -67,7 +67,8 @@ define('admin/general/navigation', ['translator', 'iconSelect', 'benchpress', 'j
|
||||
|
||||
data.enabled = false;
|
||||
data.index = (parseInt($('#enabled').children().last().attr('data-index'), 10) || 0) + 1;
|
||||
|
||||
data.title = translator.escape(data.title);
|
||||
data.text = translator.escape(data.text);
|
||||
Benchpress.parse('admin/general/navigation', 'navigation', { navigation: [data] }, function (li) {
|
||||
translator.translate(li, function (li) {
|
||||
li = $(translator.unescape(li));
|
||||
|
||||
@@ -279,7 +279,7 @@ module.exports = function (db, module) {
|
||||
callback = callback || helpers.noop;
|
||||
value = parseInt(value, 10);
|
||||
if (!key || isNaN(value)) {
|
||||
return callback();
|
||||
return callback(null, null);
|
||||
}
|
||||
|
||||
var data = {};
|
||||
|
||||
@@ -117,14 +117,18 @@ module.exports = function (redisClient, module) {
|
||||
};
|
||||
|
||||
module.incrObjectField = function (key, field, callback) {
|
||||
redisClient.hincrby(key, field, 1, callback);
|
||||
module.incrObjectFieldBy(key, field, 1, callback);
|
||||
};
|
||||
|
||||
module.decrObjectField = function (key, field, callback) {
|
||||
redisClient.hincrby(key, field, -1, callback);
|
||||
module.incrObjectFieldBy(key, field, -1, callback);
|
||||
};
|
||||
|
||||
module.incrObjectFieldBy = function (key, field, value, callback) {
|
||||
value = parseInt(value, 10);
|
||||
if (!key || isNaN(value)) {
|
||||
return callback(null, null);
|
||||
}
|
||||
redisClient.hincrby(key, field, value, callback);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -24,8 +24,8 @@ module.exports = function (Topics) {
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
var tids = results.tagTids.concat(results.searchTids).concat(results.categoryTids);
|
||||
tids = _.uniq(tids).filter(function (_tid) {
|
||||
var tids = _.shuffle(_.uniq(results.tagTids.concat(results.searchTids).concat(results.categoryTids)));
|
||||
tids = tids.filter(function (_tid) {
|
||||
return parseInt(_tid, 10) !== parseInt(tid, 10);
|
||||
});
|
||||
|
||||
@@ -59,10 +59,10 @@ module.exports = function (Topics) {
|
||||
function getSearchTids(tid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Topics.getTopicField(tid, 'title', next);
|
||||
Topics.getTopicFields(tid, ['title', 'cid'], next);
|
||||
},
|
||||
function (title, next) {
|
||||
search.searchQuery('topic', title, [], [], next);
|
||||
function (topicData, next) {
|
||||
search.searchQuery('topic', topicData.title, [topicData.cid], [], next);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div id="skins" class="skins">
|
||||
<div class="directory row" id="bootstrap_themes">
|
||||
<div class="directory row text-center" id="bootstrap_themes">
|
||||
<i class="fa fa-refresh fa-spin"></i> [[admin/appearance/skins:loading]]
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div id="themes" class="themes">
|
||||
<div class="directory row" id="installed_themes">
|
||||
<div class="directory row text-center" id="installed_themes">
|
||||
<i class="fa fa-refresh fa-spin"></i> [[admin/appearance/themes:checking-for-installed]]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,6 +59,20 @@
|
||||
</div>
|
||||
<!-- END availableWidgets -->
|
||||
</div>
|
||||
|
||||
<div class="btn-group" component="clone">
|
||||
<button type="button" class="btn btn-primary" component="clone/button">[[admin/extend/widgets:clone-from]] ...</button>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<!-- BEGIN templates -->
|
||||
<!-- IF !@first -->
|
||||
<li><a href="#">{templates.template}</a></li>
|
||||
<!-- END -->
|
||||
<!-- END templates -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<div class="row">
|
||||
|
||||
<form role="form" class="category" data-cid="{category.cid}">
|
||||
<div class="row">
|
||||
<div class="col-md-3 pull-right">
|
||||
@@ -13,9 +11,8 @@
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade active in row" id="category-settings">
|
||||
<div class="col-md-9">
|
||||
<div class="row">
|
||||
<div class="col-md-9" id="category-settings">
|
||||
<div class="category-settings-form">
|
||||
<fieldset>
|
||||
<label for="cid-{category.cid}-name">
|
||||
@@ -166,9 +163,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button id="save" class="floating-button mdl-button mdl-js-button mdl-button--fab mdl-js-ripple-effect mdl-button--colored">
|
||||
<i class="material-icons">save</i>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="post-queue panel panel-primary">
|
||||
<div class="post-queue panel panel-primary preventSlideout">
|
||||
<div class="panel-heading">
|
||||
[[admin/manage/post-queue:post-queue]]
|
||||
</div>
|
||||
|
||||
@@ -438,5 +438,17 @@ describe('Hash methods', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if value is NaN', function (done) {
|
||||
db.incrObjectFieldBy('testObject15', 'lastonline', 'notanumber', function (err, newValue) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(newValue, null);
|
||||
db.isObjectField('testObject15', 'lastonline', function (err, isField) {
|
||||
assert.ifError(err);
|
||||
assert(!isField);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user