1
0
mirror of synced 2026-08-04 09:26:54 +00:00

Start work on JS side

This commit is contained in:
Kane York
2015-06-25 13:26:31 -07:00
parent 44bbc78160
commit 01b38207d9
30 changed files with 142 additions and 329 deletions
@@ -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}}
&rarr;
&nbsp;
{{#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;
+7 -19
View File
@@ -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."
+16 -14
View File
@@ -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