Start work on JS side
This commit is contained in:
parent
44bbc78160
commit
01b38207d9
|
@ -1,3 +0,0 @@
|
|||
{{#if canEditTags}}
|
||||
{{tag-chooser tags=model.tags tabIndex="4"}}
|
||||
{{/if}}
|
|
@ -1,3 +0,0 @@
|
|||
{{#if canEditTags}}
|
||||
{{tag-chooser tags=buffered.tags}}
|
||||
{{/if}}
|
|
@ -1,13 +0,0 @@
|
|||
{{#if tags_changes}}
|
||||
<div class='row'>
|
||||
{{i18n "tagging.changed"}}
|
||||
{{#each t in previousTagChanges}}
|
||||
{{discourse-tag tagId=t}}
|
||||
{{/each}}
|
||||
→
|
||||
|
||||
{{#each t in currentTagChanges}}
|
||||
{{discourse-tag tagId=t}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1,3 +0,0 @@
|
|||
<li>
|
||||
{{#link-to 'tags'}}{{i18n "tagging.tags"}}{{/link-to}}
|
||||
</li>
|
|
@ -1,3 +0,0 @@
|
|||
{{#each t in topic.tags}}
|
||||
{{discourse-tag tagId=t}}
|
||||
{{/each}}
|
|
@ -0,0 +1,3 @@
|
|||
import buildPluginAdapter from 'discourse/adapters/build-plugin';
|
||||
|
||||
export default buildPluginAdapter('explorer');
|
|
@ -1,7 +0,0 @@
|
|||
import RESTAdapter from 'discourse/adapters/rest';
|
||||
|
||||
export default RESTAdapter.extend({
|
||||
pathFor(type, id) {
|
||||
return "/tags/" + id + "/notifications";
|
||||
}
|
||||
});
|
|
@ -1,33 +0,0 @@
|
|||
export default Ember.Component.extend({
|
||||
tagName: 'a',
|
||||
classNameBindings: [':discourse-tag'],
|
||||
attributeBindings: ['href', 'style'],
|
||||
|
||||
href: function() {
|
||||
return "/tags/" + this.get('tagId');
|
||||
}.property('tagId'),
|
||||
|
||||
style: function() {
|
||||
const count = parseFloat(this.get('count')),
|
||||
minCount = parseFloat(this.get('minCount')),
|
||||
maxCount = parseFloat(this.get('maxCount'));
|
||||
|
||||
if (count && maxCount && minCount) {
|
||||
let ratio = (count - minCount) / maxCount;
|
||||
if (ratio) {
|
||||
ratio = ratio + 1.0;
|
||||
return "font-size: " + ratio + "em";
|
||||
}
|
||||
}
|
||||
}.property('count', 'scaleTo'),
|
||||
|
||||
render(buffer) {
|
||||
buffer.push(Handlebars.Utils.escapeExpression(this.get('tagId')));
|
||||
},
|
||||
|
||||
click(e) {
|
||||
e.preventDefault();
|
||||
Discourse.URL.routeTo(this.get('href'));
|
||||
return true;
|
||||
}
|
||||
});
|
|
@ -1,88 +0,0 @@
|
|||
function formatTag(t) {
|
||||
const ret = "<a href class='discourse-tag'>" + Handlebars.Utils.escapeExpression(t.id) + "</a>";
|
||||
return (t.count) ? ret + " <span class='discourse-tag-count'>x" + t.count + "</span>" : ret;
|
||||
}
|
||||
|
||||
export default Ember.TextField.extend({
|
||||
classNameBindings: [':tag-chooser'],
|
||||
attributeBindings: ['tabIndex'],
|
||||
|
||||
_setupTags: function() {
|
||||
const tags = this.get('tags') || [];
|
||||
this.set('value', tags.join(", "));
|
||||
}.on('init'),
|
||||
|
||||
_valueChanged: function() {
|
||||
const tags = this.get('value').split(',').map(v => v.trim()).reject(v => v.length === 0).uniq();
|
||||
this.set('tags', tags);
|
||||
}.observes('value'),
|
||||
|
||||
_initializeTags: function() {
|
||||
const site = this.site,
|
||||
filterRegexp = new RegExp(this.site.tags_filter_regexp, "g");
|
||||
|
||||
this.$().select2({
|
||||
tags: true,
|
||||
placeholder: I18n.t('tagging.choose_for_topic'),
|
||||
maximumInputLength: this.siteSettings.max_tag_length,
|
||||
maximumSelectionSize: this.siteSettings.max_tags_per_topic,
|
||||
initSelection(element, callback) {
|
||||
const data = [];
|
||||
|
||||
function splitVal(string, separator) {
|
||||
var val, i, l;
|
||||
if (string === null || string.length < 1) return [];
|
||||
val = string.split(separator);
|
||||
for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]);
|
||||
return val;
|
||||
}
|
||||
|
||||
$(splitVal(element.val(), ",")).each(function () {
|
||||
data.push({
|
||||
id: this,
|
||||
text: this
|
||||
});
|
||||
});
|
||||
|
||||
callback(data);
|
||||
},
|
||||
createSearchChoice: function(term, data) {
|
||||
term = term.replace(filterRegexp, '').trim();
|
||||
|
||||
// No empty terms, make sure the user has permission to create the tag
|
||||
if (!term.length || !site.get('can_create_tag')) { return; }
|
||||
|
||||
if ($(data).filter(function() {
|
||||
return this.text.localeCompare(term) === 0;
|
||||
}).length === 0) {
|
||||
return { id: term, text: term };
|
||||
}
|
||||
},
|
||||
createSearchChoicePosition: function(list, item) {
|
||||
// Search term goes on the bottom
|
||||
list.push(item);
|
||||
},
|
||||
formatSelectionCssClass: function () { return "discourse-tag"; },
|
||||
formatResult: formatTag,
|
||||
// formatSelection: formatTag,
|
||||
multiple: true,
|
||||
ajax: {
|
||||
quietMillis: 200,
|
||||
cache: true,
|
||||
url: "/tags/filter/search",
|
||||
dataType: 'json',
|
||||
data: function (term) {
|
||||
return { q: term };
|
||||
},
|
||||
results: function (data) {
|
||||
return data;
|
||||
}
|
||||
},
|
||||
});
|
||||
}.on('didInsertElement'),
|
||||
|
||||
_destroyTags: function() {
|
||||
this.$().select2('destroy');
|
||||
}.on('willDestroyElement')
|
||||
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
import NotificationsButton from 'discourse/components/notifications-button';
|
||||
|
||||
export default NotificationsButton.extend({
|
||||
classNames: ['notification-options', 'tag-notification-menu'],
|
||||
buttonIncludesText: false,
|
||||
i18nPrefix: 'tagging.notifications',
|
||||
|
||||
clicked(id) {
|
||||
this.sendAction('action', id);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
export default Ember.ArrayController.extend({
|
||||
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
notReady: Em.computed.not('ready'),
|
||||
|
||||
needs: ['admin-plugins-explorer'],
|
||||
|
||||
ready: function() {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(this.get('queryFile'));
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!parsed["query"];
|
||||
}.property('queryFile'),
|
||||
|
||||
actions: {
|
||||
doImport: function() {
|
||||
const self = this;
|
||||
const object = JSON.parse(this.get('queryFile')).query;
|
||||
|
||||
// Slight fixup before creating object
|
||||
delete object.id;
|
||||
|
||||
this.set('loading', true);
|
||||
this.store.createRecord('query', object).save().then(function(query) {
|
||||
self.send('closeModal');
|
||||
self.set('loading', false);
|
||||
|
||||
const parentController = self.get('controllers.admin-plugins-explorer');
|
||||
parentController.pushObject(query);
|
||||
parentController.set('selectedItem', query);
|
||||
}).catch(function(xhr) {
|
||||
self.set('loading', false);
|
||||
if (xhr.responseJSON) {
|
||||
bootbox.alert(xhr.responseJSON.errors.join("<br>"));
|
||||
} else {
|
||||
bootbox.alert(I18n.t('generic_error'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
export default Ember.Controller.extend({
|
||||
tag: null,
|
||||
list: null,
|
||||
|
||||
loadMoreTopics() {
|
||||
return this.get('list').loadMore();
|
||||
},
|
||||
|
||||
actions: {
|
||||
changeTagNotification(id) {
|
||||
const tagNotification = this.get('tagNotification');
|
||||
tagNotification.update({ notification_level: id });
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
export default {
|
||||
resource: 'admin.adminPlugins',
|
||||
path: '/plugins',
|
||||
map() {
|
||||
this.route('explorer');
|
||||
this.route('explorer-show', {path: 'explorer/:id'});
|
||||
}
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
export default {
|
||||
name: 'add-query-pluralization',
|
||||
initialize(container) {
|
||||
container.lookup('store:main').addPluralization('query', 'queries');
|
||||
}
|
||||
};
|
|
@ -1,44 +0,0 @@
|
|||
import ComposerController from 'discourse/controllers/composer';
|
||||
import HistoryController from 'discourse/controllers/history';
|
||||
import TopicController from 'discourse/controllers/topic';
|
||||
import { needsSecondRowIf } from 'discourse/components/header-extra-info';
|
||||
|
||||
// Work around a quirk of custom fields -- an array of one element
|
||||
// is returned as just that element. We should fix this properly
|
||||
// in custom fields and remove this.
|
||||
function customTagArray(fieldName) {
|
||||
return function() {
|
||||
var val = this.get(fieldName);
|
||||
if (!val) { return val; }
|
||||
if (!Array.isArray(val)) { val = [val]; }
|
||||
return val;
|
||||
}.property(fieldName);
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'extend-for-tagging',
|
||||
initialize() {
|
||||
Discourse.Composer.serializeOnCreate('tags');
|
||||
Discourse.Composer.serializeToTopic('tags', 'topic.tags');
|
||||
|
||||
TopicController.reopen({
|
||||
canEditTags: Ember.computed.not('isPrivateMessage')
|
||||
});
|
||||
|
||||
HistoryController.reopen({
|
||||
previousTagChanges: customTagArray('tags_changes.previous'),
|
||||
currentTagChanges: customTagArray('tags_changes.current')
|
||||
});
|
||||
|
||||
ComposerController.reopen({
|
||||
canEditTags: function() {
|
||||
return !this.site.mobileView &&
|
||||
this.get('model.canEditTitle') &&
|
||||
!this.get('model.creatingPrivateMessage');
|
||||
}.property('model.canEditTitle', 'model.creatingPrivateMessage')
|
||||
});
|
||||
|
||||
// Show a second row in the header if there are any tags on the topic
|
||||
needsSecondRowIf('topic.tags.length', tagsLength => parseInt(tagsLength) > 0);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
export default Discourse.Route.extend({
|
||||
model(params) {
|
||||
return this.store.find('query', params.get('id'));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
import showModal from 'discourse/lib/show-modal';
|
||||
|
||||
export default Discourse.Route.extend({
|
||||
controllerName: 'admin-plugins-explorer',
|
||||
|
||||
model() {
|
||||
return this.store.findAll('query');
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
|
||||
},
|
||||
|
||||
actions: {
|
||||
create() {
|
||||
|
||||
},
|
||||
importQuery() {
|
||||
showModal('import-query');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
export default Discourse.Route.extend({
|
||||
model() {
|
||||
return Discourse.ajax("/tags/filter/cloud.json");
|
||||
}
|
||||
});
|
|
@ -1,32 +0,0 @@
|
|||
export default Discourse.Route.extend({
|
||||
|
||||
model(tag) {
|
||||
tag.tag_id = Handlebars.Utils.escapeExpression(tag.tag_id);
|
||||
|
||||
if (this.get('currentUser')) {
|
||||
// If logged in, we should get the tag's user settings
|
||||
const self = this;
|
||||
return this.store.find('tagNotification', tag.tag_id).then(function(tn) {
|
||||
self.set('tagNotification', tn);
|
||||
return tag;
|
||||
});
|
||||
}
|
||||
|
||||
return tag;
|
||||
},
|
||||
|
||||
afterModel(tag) {
|
||||
const self = this;
|
||||
return Discourse.TopicList.list('tags/' + tag.tag_id).then(function(list) {
|
||||
self.set('list', list);
|
||||
});
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.setProperties({
|
||||
tag: model,
|
||||
list: this.get('list'),
|
||||
tagNotification: this.get('tagNotification')
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
export default function() {
|
||||
this.resource('tags', function() {
|
||||
this.route('show', {path: ':tag_id'});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{{d-button action="create" label="explorer.create" icon="plus"}}
|
||||
{{d-button action="importQuery" label="explorer.import.label" icon="upload"}}
|
||||
|
||||
{{#each query in model}}
|
||||
<div class="row">
|
||||
{{query.name}}
|
||||
</div>
|
||||
{{/each}}
|
|
@ -0,0 +1,8 @@
|
|||
<form {{action "dummy" on="submit"}}>
|
||||
<div class='modal-body'>
|
||||
{{json-file-uploader value=queryFile extension=".dcquery.json"}}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{{d-button class='btn-primary' action='doImport' type='submit' disabled=notReady icon="plus" label='explorer.import.label'}}
|
||||
</div>
|
||||
</form>
|
|
@ -1,9 +0,0 @@
|
|||
<div class="container list-container">
|
||||
<div class="row">
|
||||
<div class="full-width">
|
||||
<div id='list-area'>
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,10 +0,0 @@
|
|||
<h2>{{i18n "tagging.all_tags"}}</h2>
|
||||
|
||||
<div class='tag-cloud'>
|
||||
{{#each tag in cloud}}
|
||||
{{discourse-tag tagId=tag.id
|
||||
count=tag.count
|
||||
maxCount=model.max_count
|
||||
minCount=model.min_count}}
|
||||
{{/each}}
|
||||
</div>
|
|
@ -1,9 +0,0 @@
|
|||
{{#if tagNotification}}
|
||||
{{tag-notifications-button tag=tag.tag_id
|
||||
action="changeTagNotification"
|
||||
notificationLevel=tagNotification.notification_level}}
|
||||
{{/if}}
|
||||
|
||||
<h2>{{{i18n "tagging.topics_tagged" tag=tag.tag_id}}}</h2>
|
||||
|
||||
{{topic-list topics=list.topics}}
|
|
@ -0,0 +1,6 @@
|
|||
import ModalBodyView from "discourse/views/modal-body";
|
||||
|
||||
export default ModalBodyView.extend({
|
||||
templateName: 'modal/import-query',
|
||||
title: I18n.t('explorer.import.modal')
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
import DiscoveryTopicsView from "discourse/views/discovery-topics";
|
||||
|
||||
export default DiscoveryTopicsView;
|
|
@ -20,23 +20,11 @@ en:
|
|||
errors:
|
||||
explorer:
|
||||
no_semicolons: "Remove the semicolons from the query."
|
||||
tagging:
|
||||
all_tags: "All Tags"
|
||||
changed: "tags changed:"
|
||||
tags: "Tags"
|
||||
choose_for_topic: "choose optional tags for this topic"
|
||||
topics_tagged: "Topics tagged <span class='discourse-tag'>{{tag}}</span>"
|
||||
explorer:
|
||||
title: "Data Explorer"
|
||||
create: "Create New"
|
||||
import:
|
||||
label: "Import"
|
||||
modal: "Import A Query"
|
||||
export: "Export"
|
||||
|
||||
notifications:
|
||||
watching:
|
||||
title: "Watching"
|
||||
description: "You will automatically watch all new topics in this tag. You will be notified of all new posts and topics, plus the count of unread and new posts will also appear next to the topic."
|
||||
tracking:
|
||||
title: "Tracking"
|
||||
description: "You will automatically track all new topics in this tag. A count of unread and new posts will appear next to the topic."
|
||||
regular:
|
||||
title: "Regular"
|
||||
description: "You will be notified if someone mentions your @name or replies to your post."
|
||||
muted:
|
||||
title: "Muted"
|
||||
description: "You will not be notified of anything about new topics in this tag, and they will not appear on your unread tab."
|
||||
|
|
30
plugin.rb
30
plugin.rb
|
@ -204,14 +204,14 @@ SQL
|
|||
end
|
||||
|
||||
require_dependency 'application_controller'
|
||||
class DataExplorer::ExplorerController < ::ApplicationController
|
||||
class DataExplorer::QueryController < ::ApplicationController
|
||||
requires_plugin DataExplorer.plugin_name
|
||||
skip_before_filter :check_xhr, only: [:show]
|
||||
|
||||
def index
|
||||
# guardian.ensure_can_use_data_explorer!
|
||||
queries = DataExplorer::Query.all
|
||||
render_serialized queries, DataExplorer::QuerySerializer
|
||||
render_serialized queries, DataExplorer::QuerySerializer, root: 'queries'
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -225,7 +225,7 @@ SQL
|
|||
end
|
||||
|
||||
# guardian.ensure_can_see! query
|
||||
render_serialized query, DataExplorer::QuerySerializer
|
||||
render_serialized query, DataExplorer::QuerySerializer, root: 'queries'
|
||||
end
|
||||
|
||||
# Helper endpoint for logic
|
||||
|
@ -243,7 +243,7 @@ SQL
|
|||
end
|
||||
query.save
|
||||
|
||||
render_serialized query, DataExplorer::QuerySerializer
|
||||
render_serialized query, DataExplorer::QuerySerializer, root: 'queries'
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -253,12 +253,13 @@ SQL
|
|||
end
|
||||
query.save
|
||||
|
||||
render_serialized query, DataExplorer::QuerySerializer
|
||||
render_serialized query, DataExplorer::QuerySerializer, root: 'queries'
|
||||
end
|
||||
|
||||
def destroy
|
||||
query = DataExplorer::Query.find(params[:id].to_i)
|
||||
query.destroy
|
||||
render nothing: true
|
||||
end
|
||||
|
||||
def run
|
||||
|
@ -314,18 +315,19 @@ SQL
|
|||
end
|
||||
|
||||
DataExplorer::Engine.routes.draw do
|
||||
# GET /explorer -> explorer#index
|
||||
# POST /explorer -> explorer#create
|
||||
# GET /explorer/:id -> explorer#show
|
||||
# PUT /explorer/:id -> explorer#update
|
||||
# DELETE /explorer/:id -> explorer#destroy
|
||||
resources :explorer
|
||||
get 'explorer/parse_params' => "explorer#parse_params"
|
||||
post 'explorer/:id/run' => "explorer#run"
|
||||
root to: "query#index"
|
||||
get 'queries' => "query#index"
|
||||
# POST /query -> explorer#create
|
||||
# GET /query/:id -> explorer#show
|
||||
# PUT /query/:id -> explorer#update
|
||||
# DELETE /query/:id -> explorer#destroy
|
||||
resources :query
|
||||
get 'query/parse_params' => "query#parse_params"
|
||||
post 'query/:id/run' => "query#run"
|
||||
end
|
||||
|
||||
Discourse::Application.routes.append do
|
||||
mount ::DataExplorer::Engine, at: '/admin/plugins/', constraints: AdminConstraint.new
|
||||
mount ::DataExplorer::Engine, at: '/admin/plugins/explorer', constraints: AdminConstraint.new
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue