FEATURE: manage Permalinks

This commit is contained in:
Arpit Jalan 2015-07-15 18:24:28 +05:30
parent 7f43653cad
commit dc90c396f2
16 changed files with 296 additions and 1 deletions

View File

@ -0,0 +1,56 @@
export default Ember.Component.extend({
classNames: ['permalink-form'],
formSubmitted: false,
permalinkType: 'topic_id',
permalinkTypes: function() {
return [
{id: 'topic_id', name: I18n.t('admin.permalink.topic_id')},
{id: 'post_id', name: I18n.t('admin.permalink.post_id')},
{id: 'category_id', name: I18n.t('admin.permalink.category_id')},
{id: 'external_url', name: I18n.t('admin.permalink.external_url')}
];
}.property(),
permalinkTypePlaceholder: function() {
return 'admin.permalink.' + this.get('permalinkType');
}.property('permalinkType'),
actions: {
submit: function() {
if (!this.get('formSubmitted')) {
const self = this;
self.set('formSubmitted', true);
const permalink = Discourse.Permalink.create({url: self.get('url'), permalink_type: self.get('permalinkType'), permalink_type_value: self.get('permalink_type_value')});
permalink.save().then(function(result) {
self.set('url', '');
self.set('permalink_type_value', '');
self.set('formSubmitted', false);
self.sendAction('action', Discourse.Permalink.create(result.permalink));
Em.run.schedule('afterRender', function() { self.$('.permalink-url').focus(); });
}, function(e) {
self.set('formSubmitted', false);
let error;
if (e.responseJSON && e.responseJSON.errors) {
error = I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')});
} else {
error = I18n.t("generic_error");
}
bootbox.alert(error, function() { self.$('.permalink-url').focus(); });
});
}
}
},
didInsertElement: function() {
var self = this;
self._super();
Em.run.schedule('afterRender', function() {
self.$('.external-url').keydown(function(e) {
if (e.keyCode === 13) { // enter key
self.send('submit');
}
});
});
}
});

View File

@ -0,0 +1,36 @@
export default Ember.ArrayController.extend({
loading: false,
filter: null,
show: Discourse.debounce(function() {
var self = this;
self.set('loading', true);
Discourse.Permalink.findAll(self.get("filter")).then(function(result) {
self.set('model', result);
self.set('loading', false);
});
}, 250).observes("filter"),
actions: {
recordAdded(arg) {
this.get("model").unshiftObject(arg);
},
destroy: function(record) {
const self = this;
return bootbox.confirm(I18n.t("admin.permalink.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
if (result) {
record.destroy().then(function(deleted) {
if (deleted) {
self.removeObject(record);
} else {
bootbox.alert(I18n.t("generic_error"));
}
}, function(){
bootbox.alert(I18n.t("generic_error"));
});
}
});
}
}
});

View File

@ -0,0 +1,22 @@
const Permalink = Discourse.Model.extend({
save: function() {
return Discourse.ajax("/admin/permalinks.json", {
type: 'POST',
data: {url: this.get('url'), permalink_type: this.get('permalink_type'), permalink_type_value: this.get('permalink_type_value')}
});
},
destroy: function() {
return Discourse.ajax("/admin/permalinks/" + this.get('id') + ".json", {type: 'DELETE'});
}
});
Permalink.reopenClass({
findAll: function(filter) {
return Discourse.ajax("/admin/permalinks.json", { data: { filter: filter } }).then(function(permalinks) {
return permalinks.map(p => Discourse.Permalink.create(p));
});
}
});
export default Permalink;

View File

@ -0,0 +1,9 @@
export default Discourse.Route.extend({
model() {
return Discourse.Permalink.findAll();
},
setupController(controller, model) {
controller.set('model', model);
}
});

View File

@ -22,6 +22,7 @@ export default {
});
this.resource('adminUserFields', { path: '/user_fields' });
this.resource('adminEmojis', { path: '/emojis' });
this.resource('adminPermalinks', { path: '/permalinks' });
});
this.route('api');

View File

@ -0,0 +1,5 @@
<b>{{i18n 'admin.permalink.form.label'}}</b>
{{text-field value=url disabled=formSubmitted class="permalink-url" placeholderKey="admin.permalink.url" autocorrect="off" autocapitalize="off"}}
{{combo-box content=permalinkTypes value=permalinkType}}
{{text-field value=permalink_type_value disabled=formSubmitted class="external-url" placeholderKey=permalinkTypePlaceholder autocorrect="off" autocapitalize="off"}}
<button class="btn" {{action "submit" target="view"}} {{bind-attr disabled="formSubmitted"}}>{{i18n 'admin.permalink.form.add'}}</button>

View File

@ -4,6 +4,7 @@
{{nav-item route='adminSiteText' label='admin.site_text.title'}}
{{nav-item route='adminUserFields' label='admin.user_fields.title'}}
{{nav-item route='adminEmojis' label='admin.emoji.title'}}
{{nav-item route='adminPermalinks' label='admin.permalink.title'}}
{{/admin-nav}}
<div class="admin-container">

View File

@ -0,0 +1,25 @@
<div class="permalink-title"><h2>{{i18n 'admin.permalink.title'}}</h2></div>
<div class="pull-right">
{{text-field value=filter class="url-input" placeholderKey="admin.permalink.form.filter" autocorrect="off" autocapitalize="off"}}
</div>
{{permalink-form action="recordAdded"}}
<br/>
{{#conditional-loading-spinner condition=loading}}
{{#if model.length}}
<div class='table admin-logs-table permalinks'>
<div class="heading-container">
<div class="col heading first url">{{i18n 'admin.permalink.url'}}</div>
<div class="col heading topic_id">{{i18n 'admin.permalink.topic_id'}}</div>
<div class="col heading post_id">{{i18n 'admin.permalink.post_id'}}</div>
<div class="col heading category_id">{{i18n 'admin.permalink.category_id'}}</div>
<div class="col heading external_url">{{i18n 'admin.permalink.external_url'}}</div>
<div class="col heading actions"></div>
<div class="clearfix"></div>
</div>
{{view 'permalinks-list' content=controller}}
</div>
{{else}}
{{i18n 'search.no_results'}}
{{/if}}
{{/conditional-loading-spinner}}

View File

@ -0,0 +1,7 @@
<div class="col first url">{{url}}</div>
<div class="col topic_id">{{topic_id}}</div>
<div class="col post_id">{{post_id}}</div>
<div class="col category_id">{{category_id}}</div>
<div class="col external_url">{{external_url}}</div>
<div class="col action"><button class="btn btn-danger" {{action "destroy" this}}><i class="fa fa-trash-o"></i></button></div>
<div class="clearfix"></div>

View File

@ -0,0 +1,8 @@
import ListView from 'ember-addons/list-view';
import ListItemView from 'ember-addons/list-item-view';
export default ListView.extend({
height: 700,
rowHeight: 32,
itemViewClass: ListItemView.extend({templateName: "admin/templates/permalinks_list_item"})
});

View File

@ -1199,7 +1199,7 @@ table.api-keys {
position: absolute;
}
.staff-actions, .screened-emails, .screened-urls, .screened-ip-addresses {
.staff-actions, .screened-emails, .screened-urls, .screened-ip-addresses, .permalinks {
border-bottom: dotted 1px scale-color($primary, $lightness: 75%);
@ -1469,3 +1469,19 @@ table#user-badges {
width: 90%;
}
}
// Permalinks
.permalinks {
.url, .external_url {
width: 300px;
}
.action, .topic_id, .post_id, .category_id {
text-align: center;
width: 9.9099%;
}
}
.permalink-title {
margin-bottom: 10px;
}

View File

@ -0,0 +1,39 @@
class Admin::PermalinksController < Admin::AdminController
before_filter :fetch_permalink, only: [:destroy]
def index
filter = params[:filter]
permalinks = Permalink
permalinks = permalinks.where('url ILIKE :filter OR external_url ILIKE :filter', filter: "%#{params[:filter]}%") if filter.present?
permalinks = permalinks.limit(100).order('created_at desc').to_a
render_serialized(permalinks, PermalinkSerializer)
end
def create
params.require(:url)
params.require(:permalink_type)
params.require(:permalink_type_value)
permalink = Permalink.new(:url => params[:url], params[:permalink_type] => params[:permalink_type_value])
if permalink.save
render_serialized(permalink, PermalinkSerializer)
else
render_json_error(permalink)
end
end
def destroy
@permalink.destroy
render json: success_json
end
private
def fetch_permalink
@permalink = Permalink.find(params[:id])
end
end

View File

@ -0,0 +1,3 @@
class PermalinkSerializer < ApplicationSerializer
attributes :id, :url, :topic_id, :post_id, :category_id, :external_url
end

View File

@ -2443,6 +2443,19 @@ en:
image: "Image"
delete_confirm: "Are you sure you want to delete the :%{name}: emoji?"
permalink:
title: "Permalinks"
url: "URL"
topic_id: "Topic ID"
post_id: "Post ID"
category_id: "Category ID"
external_url: "External URL"
delete_confirm: Are you sure you want to delete this permalink?
form:
label: "New:"
add: "Add"
filter: "Search (URL or External URL)"
lightbox:
download: "download"

View File

@ -133,6 +133,7 @@ Discourse::Application.routes.draw do
get "customize" => "color_schemes#index", constraints: AdminConstraint.new
get "customize/css_html" => "site_customizations#index", constraints: AdminConstraint.new
get "customize/colors" => "color_schemes#index", constraints: AdminConstraint.new
get "customize/permalinks" => "permalinks#index", constraints: AdminConstraint.new
get "flags" => "flags#index"
get "flags/:filter" => "flags#index"
post "flags/agree/:id" => "flags#agree"
@ -148,6 +149,8 @@ Discourse::Application.routes.draw do
resources :color_schemes, constraints: AdminConstraint.new
resources :permalinks, constraints: AdminConstraint.new
get "version_check" => "versions#show"
resources :dashboard, only: [:index] do

View File

@ -0,0 +1,51 @@
require 'spec_helper'
describe Admin::PermalinksController do
it "is a subclass of AdminController" do
expect(Admin::PermalinksController < Admin::AdminController).to eq(true)
end
let!(:user) { log_in(:admin) }
describe 'index' do
it 'filters url' do
Fabricate(:permalink, url: "/forum/23")
Fabricate(:permalink, url: "/forum/98")
Fabricate(:permalink, url: "/discuss/topic/45")
Fabricate(:permalink, url: "/discuss/topic/76")
xhr :get, :index, filter: "topic"
expect(response).to be_success
result = JSON.parse(response.body)
expect(result.length).to eq(2)
end
it 'filters external url' do
Fabricate(:permalink, external_url: "http://google.com")
Fabricate(:permalink, external_url: "http://wikipedia.org")
Fabricate(:permalink, external_url: "http://www.discourse.org")
Fabricate(:permalink, external_url: "http://try.discourse.org")
xhr :get, :index, filter: "discourse"
expect(response).to be_success
result = JSON.parse(response.body)
expect(result.length).to eq(2)
end
it 'filters url and external url both' do
Fabricate(:permalink, url: "/forum/23", external_url: "http://google.com")
Fabricate(:permalink, url: "/discourse/98", external_url: "http://wikipedia.org")
Fabricate(:permalink, url: "/discuss/topic/45", external_url: "http://discourse.org")
Fabricate(:permalink, url: "/discuss/topic/76", external_url: "http://try.discourse.org")
xhr :get, :index, filter: "discourse"
expect(response).to be_success
result = JSON.parse(response.body)
expect(result.length).to eq(3)
end
end
end