Spiegel von
https://github.com/paviliondev/discourse-custom-wizard.git
synchronisiert 2024-11-22 09:20:29 +01:00
Apply new table style to wizard logs view
Dieser Commit ist enthalten in:
Ursprung
a8e81150f1
Commit
7b57e7fcab
26 geänderte Dateien mit 599 neuen und 378 gelöschten Zeilen
|
@ -1,10 +1,11 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { equal } from "@ember/object/computed";
|
import { equal, notEmpty } from "@ember/object/computed";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
classNameBindings: ["value.type"],
|
||||||
isText: equal("value.type", "text"),
|
isText: equal("value.type", "text"),
|
||||||
isComposer: equal("value.type", "composer"),
|
isComposer: equal("value.type", "composer"),
|
||||||
isDate: equal("value.type", "date"),
|
isDate: equal("value.type", "date"),
|
||||||
|
@ -18,13 +19,29 @@ export default Component.extend({
|
||||||
isTag: equal("value.type", "tag"),
|
isTag: equal("value.type", "tag"),
|
||||||
isCategory: equal("value.type", "category"),
|
isCategory: equal("value.type", "category"),
|
||||||
isGroup: equal("value.type", "group"),
|
isGroup: equal("value.type", "group"),
|
||||||
isUser: equal("fieldName", "username"),
|
|
||||||
isUserSelector: equal("value.type", "user_selector"),
|
isUserSelector: equal("value.type", "user_selector"),
|
||||||
isSubmittedAt: equal("fieldName", "submitted_at"),
|
isSubmittedAt: equal("field", "submitted_at"),
|
||||||
isTextArea: equal("value.type", "textarea"),
|
|
||||||
isComposerPreview: equal("value.type", "composer_preview"),
|
isComposerPreview: equal("value.type", "composer_preview"),
|
||||||
textState: "text-collapsed",
|
textState: "text-collapsed",
|
||||||
toggleText: I18n.t("admin.wizard.submissions.expand_text"),
|
toggleText: I18n.t("admin.wizard.expand_text"),
|
||||||
|
|
||||||
|
@discourseComputed("value", "isUser")
|
||||||
|
hasValue(value, isUser) {
|
||||||
|
if (isUser) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return value && value.value;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("field", "value.type")
|
||||||
|
isUser(field, type) {
|
||||||
|
return field === "username" || field === "user" || type === "user";
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("value.type")
|
||||||
|
isLongtext(type) {
|
||||||
|
return type === "textarea" || type === "long_text";
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed("value")
|
@discourseComputed("value")
|
||||||
checkboxValue(value) {
|
checkboxValue(value) {
|
||||||
|
@ -44,10 +61,10 @@ export default Component.extend({
|
||||||
|
|
||||||
if (state === "text-collapsed") {
|
if (state === "text-collapsed") {
|
||||||
this.set("textState", "text-expanded");
|
this.set("textState", "text-expanded");
|
||||||
this.set("toggleText", I18n.t("admin.wizard.submissions.collapse_text"));
|
this.set("toggleText", I18n.t("admin.wizard.collapse_text"));
|
||||||
} else if (state === "text-expanded") {
|
} else if (state === "text-expanded") {
|
||||||
this.set("textState", "text-collapsed");
|
this.set("textState", "text-collapsed");
|
||||||
this.set("toggleText", I18n.t("admin.wizard.submissions.expand_text"));
|
this.set("toggleText", I18n.t("admin.wizard.expand_text"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -83,19 +100,24 @@ export default Component.extend({
|
||||||
return users;
|
return users;
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("value")
|
@discourseComputed("isUser", "field", "value")
|
||||||
userProfileUrl(value) {
|
username(isUser, field, value) {
|
||||||
const isUser = this.get("isUser");
|
if (isUser) {return value.username;}
|
||||||
|
if (field === "username") {return value.value;}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
if (isUser) {
|
showUsername: notEmpty("username"),
|
||||||
return `/u/${value.username}`;
|
|
||||||
}
|
@discourseComputed("username")
|
||||||
|
userProfileUrl(username) {
|
||||||
|
if (username) {return `/u/${username}`;}
|
||||||
|
return "/";
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("value")
|
@discourseComputed("value")
|
||||||
categoryUrl(value) {
|
categoryUrl(value) {
|
||||||
const isCategory = this.get("isCategory");
|
const isCategory = this.get("isCategory");
|
||||||
|
|
||||||
if (isCategory) {
|
if (isCategory) {
|
||||||
return `/c/${value.value}`;
|
return `/c/${value.value}`;
|
||||||
}
|
}
|
||||||
|
@ -104,7 +126,6 @@ export default Component.extend({
|
||||||
@discourseComputed("value")
|
@discourseComputed("value")
|
||||||
groupUrl(value) {
|
groupUrl(value) {
|
||||||
const isGroup = this.get("isGroup");
|
const isGroup = this.get("isGroup");
|
||||||
|
|
||||||
if (isGroup) {
|
if (isGroup) {
|
||||||
return `/g/${value.value}`;
|
return `/g/${value.value}`;
|
||||||
}
|
}
|
|
@ -6,10 +6,9 @@ export default Controller.extend(ModalFunctionality, {
|
||||||
save() {
|
save() {
|
||||||
this.send("closeModal");
|
this.send("closeModal");
|
||||||
},
|
},
|
||||||
|
|
||||||
resetToDefault() {
|
resetToDefault() {
|
||||||
this.get("model.fields").forEach((field) => {
|
this.get("model.reset")();
|
||||||
field.set("enabled", true);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
|
@ -0,0 +1,52 @@
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import { notEmpty } from "@ember/object/computed";
|
||||||
|
import CustomWizardLogs from "../models/custom-wizard-logs";
|
||||||
|
import Controller from "@ember/controller";
|
||||||
|
|
||||||
|
export default Controller.extend({
|
||||||
|
refreshing: false,
|
||||||
|
hasLogs: notEmpty("logs"),
|
||||||
|
page: 0,
|
||||||
|
canLoadMore: true,
|
||||||
|
logs: [],
|
||||||
|
messageKey: "viewing",
|
||||||
|
|
||||||
|
loadLogs() {
|
||||||
|
if (!this.canLoadMore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const page = this.get("page");
|
||||||
|
const wizardId = this.get("wizard.id");
|
||||||
|
|
||||||
|
this.set("refreshing", true);
|
||||||
|
|
||||||
|
CustomWizardLogs.list(wizardId, page)
|
||||||
|
.then((result) => {
|
||||||
|
this.set("logs", this.logs.concat(result.logs));
|
||||||
|
})
|
||||||
|
.finally(() => this.set("refreshing", false));
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("hasLogs", "refreshing")
|
||||||
|
noResults(hasLogs, refreshing) {
|
||||||
|
return !hasLogs && !refreshing;
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
loadMore() {
|
||||||
|
if (!this.loadingMore && this.logs.length < this.total) {
|
||||||
|
this.set("page", (this.page += 1));
|
||||||
|
this.loadLogs();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.setProperties({
|
||||||
|
canLoadMore: true,
|
||||||
|
page: 0,
|
||||||
|
logs: [],
|
||||||
|
});
|
||||||
|
this.loadLogs();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,52 +1,34 @@
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import { notEmpty } from "@ember/object/computed";
|
|
||||||
import CustomWizardLogs from "../models/custom-wizard-logs";
|
|
||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
|
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
refreshing: false,
|
|
||||||
hasLogs: notEmpty("logs"),
|
|
||||||
page: 0,
|
|
||||||
canLoadMore: true,
|
|
||||||
logs: [],
|
|
||||||
documentationUrl: "https://thepavilion.io/t/2818",
|
documentationUrl: "https://thepavilion.io/t/2818",
|
||||||
messageKey: "viewing",
|
|
||||||
|
|
||||||
loadLogs() {
|
@discourseComputed("wizardId")
|
||||||
if (!this.canLoadMore) {
|
wizardName(wizardId) {
|
||||||
return;
|
let currentWizard = this.wizardList.find(
|
||||||
|
(wizard) => wizard.id === wizardId
|
||||||
|
);
|
||||||
|
if (currentWizard) {
|
||||||
|
return currentWizard.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("wizardName")
|
||||||
|
messageOpts(wizardName) {
|
||||||
|
return {
|
||||||
|
wizardName,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("wizardId")
|
||||||
|
messageKey(wizardId) {
|
||||||
|
let key = "select";
|
||||||
|
|
||||||
|
if (wizardId) {
|
||||||
|
key = "viewing";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set("refreshing", true);
|
return key;
|
||||||
|
|
||||||
CustomWizardLogs.list()
|
|
||||||
.then((result) => {
|
|
||||||
if (!result || result.length === 0) {
|
|
||||||
this.set("canLoadMore", false);
|
|
||||||
}
|
|
||||||
this.set("logs", this.logs.concat(result));
|
|
||||||
})
|
|
||||||
.finally(() => this.set("refreshing", false));
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("hasLogs", "refreshing")
|
|
||||||
noResults(hasLogs, refreshing) {
|
|
||||||
return !hasLogs && !refreshing;
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
loadMore() {
|
|
||||||
this.set("page", (this.page += 1));
|
|
||||||
this.loadLogs();
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this.setProperties({
|
|
||||||
canLoadMore: true,
|
|
||||||
page: 0,
|
|
||||||
logs: [],
|
|
||||||
});
|
|
||||||
this.loadLogs();
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,10 +54,14 @@ export default Controller.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
showEditColumnsModal() {
|
showEditColumnsModal() {
|
||||||
return showModal("admin-wizards-submissions-columns", {
|
return showModal("admin-wizards-columns", {
|
||||||
model: {
|
model: {
|
||||||
fields: this.get("fields"),
|
columns: this.get("fields"),
|
||||||
submissions: this.get("submissions"),
|
reset: () => {
|
||||||
|
this.get("fields").forEach((field) => {
|
||||||
|
field.set("enabled", true);
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,10 +43,16 @@ export default {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.route("adminWizardsLogs", {
|
this.route(
|
||||||
path: "/logs",
|
"adminWizardsLogs",
|
||||||
resetNamespace: true,
|
{ path: "/logs", resetNamespace: true },
|
||||||
});
|
function () {
|
||||||
|
this.route("adminWizardsLogsShow", {
|
||||||
|
path: "/:wizardId/",
|
||||||
|
resetNamespace: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
this.route("adminWizardsManager", {
|
this.route("adminWizardsManager", {
|
||||||
path: "/manager",
|
path: "/manager",
|
||||||
|
|
|
@ -3,14 +3,48 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject from "@ember/object";
|
||||||
|
|
||||||
const CustomWizardLogs = EmberObject.extend();
|
const CustomWizardLogs = EmberObject.extend();
|
||||||
|
const logItemTypes = {
|
||||||
|
date: "date_time",
|
||||||
|
action: "text",
|
||||||
|
message: "long_text",
|
||||||
|
user: "user",
|
||||||
|
username: "text",
|
||||||
|
};
|
||||||
|
|
||||||
|
function logItem(item, attr) {
|
||||||
|
return {
|
||||||
|
value: item[attr],
|
||||||
|
type: logItemTypes[attr],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
CustomWizardLogs.reopenClass({
|
CustomWizardLogs.reopenClass({
|
||||||
list(page = 0) {
|
list(wizardId, page = 0) {
|
||||||
return ajax("/admin/wizards/logs", {
|
let data = {
|
||||||
data: {
|
page,
|
||||||
page,
|
};
|
||||||
},
|
|
||||||
}).catch(popupAjaxError);
|
return ajax(`/admin/wizards/logs/${wizardId}`, { data })
|
||||||
|
.catch(popupAjaxError)
|
||||||
|
.then((result) => {
|
||||||
|
if (result.logs) {
|
||||||
|
result.logs = result.logs.map((item) => {
|
||||||
|
let map = {};
|
||||||
|
|
||||||
|
if (item.date) {map.date = logItem(item, "date");}
|
||||||
|
if (item.action) {map.action = logItem(item, "action");}
|
||||||
|
if (item.user) {
|
||||||
|
map.user = item.user;
|
||||||
|
} else {
|
||||||
|
map.user = logItem(item, "username");
|
||||||
|
}
|
||||||
|
if (item.message) {map.message = logItem(item, "message");}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
17
assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6
Normale Datei
17
assets/javascripts/discourse/routes/admin-wizards-logs-show.js.es6
Normale Datei
|
@ -0,0 +1,17 @@
|
||||||
|
import CustomWizardLogs from "../models/custom-wizard-logs";
|
||||||
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
import { A } from "@ember/array";
|
||||||
|
|
||||||
|
export default DiscourseRoute.extend({
|
||||||
|
model(params) {
|
||||||
|
return CustomWizardLogs.list(params.wizardId);
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController(controller, model) {
|
||||||
|
controller.setProperties({
|
||||||
|
wizard: model.wizard,
|
||||||
|
logs: A(model.logs),
|
||||||
|
total: model.total,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,12 +1,24 @@
|
||||||
import CustomWizardLogs from "../models/custom-wizard-logs";
|
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
model() {
|
model() {
|
||||||
return CustomWizardLogs.list();
|
return ajax(`/admin/wizards/wizard`);
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
controller.set("logs", model);
|
const showParams = this.paramsFor("adminWizardsLogsShow");
|
||||||
|
|
||||||
|
controller.setProperties({
|
||||||
|
wizardId: showParams.wizardId,
|
||||||
|
wizardList: model.wizard_list,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
changeWizard(wizardId) {
|
||||||
|
this.controllerFor("adminWizardsLogs").set("wizardId", wizardId);
|
||||||
|
this.transitionTo("adminWizardsLogsShow", wizardId);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
45
assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs
Normale Datei
45
assets/javascripts/discourse/templates/admin-wizards-logs-show.hbs
Normale Datei
|
@ -0,0 +1,45 @@
|
||||||
|
{{#if logs}}
|
||||||
|
<div class="wizard-header large">
|
||||||
|
<label>
|
||||||
|
{{i18n "admin.wizard.log.title" name=wizard.name}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
{{d-button
|
||||||
|
label="refresh"
|
||||||
|
icon="sync"
|
||||||
|
action="refresh"
|
||||||
|
class="refresh"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wizard-table">
|
||||||
|
{{#load-more selector=".wizard-table tr" action=(action "loadMore")}}
|
||||||
|
{{#if noResults}}
|
||||||
|
<p>{{i18n "search.no_results"}}</p>
|
||||||
|
{{else}}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="date">{{i18n "admin.wizard.log.date"}}</th>
|
||||||
|
<th>{{i18n "admin.wizard.log.action"}}</th>
|
||||||
|
<th>{{i18n "admin.wizard.log.user"}}</th>
|
||||||
|
<th>{{i18n "admin.wizard.log.message"}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each logs as |log|}}
|
||||||
|
<tr>
|
||||||
|
{{#each-in log as |field value|}}
|
||||||
|
<td class="small">{{wizard-table-field field=field value=value}}</td>
|
||||||
|
{{/each-in}}
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{conditional-loading-spinner condition=refreshing}}
|
||||||
|
{{/load-more}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
|
@ -1,11 +1,11 @@
|
||||||
<div class="admin-wizard-controls">
|
<div class="admin-wizard-select admin-wizard-controls">
|
||||||
<h3>{{i18n "admin.wizard.log.nav_label"}}</h3>
|
{{combo-box
|
||||||
|
value=wizardId
|
||||||
{{d-button
|
content=wizardList
|
||||||
label="refresh"
|
onChange=(route-action "changeWizard")
|
||||||
icon="sync"
|
options=(hash
|
||||||
action="refresh"
|
none="admin.wizard.select"
|
||||||
class="refresh"}}
|
)}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{wizard-message
|
{{wizard-message
|
||||||
|
@ -14,27 +14,6 @@
|
||||||
url=documentationUrl
|
url=documentationUrl
|
||||||
component="logs"}}
|
component="logs"}}
|
||||||
|
|
||||||
{{#load-more selector=".log-list tr" action=(action "loadMore") class="wizard-logs"}}
|
<div class="admin-wizard-container">
|
||||||
{{#if noResults}}
|
{{outlet}}
|
||||||
<p>{{i18n "search.no_results"}}</p>
|
</div>
|
||||||
{{else}}
|
|
||||||
<table class="table grid">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Message</th>
|
|
||||||
<th class="date">Date</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#each logs as |log|}}
|
|
||||||
<tr>
|
|
||||||
<td>{{log.message}}</td>
|
|
||||||
<td class="date">{{bound-date log.date}}</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{conditional-loading-spinner condition=refreshing}}
|
|
||||||
{{/load-more}}
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
{{d-button
|
{{d-button
|
||||||
icon="sliders-h"
|
icon="sliders-h"
|
||||||
label="admin.wizard.submissions.edit_columns"
|
label="admin.wizard.edit_columns"
|
||||||
action=(action "showEditColumnsModal")
|
action=(action "showEditColumnsModal")
|
||||||
class="btn-default open-edit-columns-btn download-link"
|
class="btn-default open-edit-columns-btn download-link"
|
||||||
}}
|
}}
|
||||||
|
@ -26,12 +26,10 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="wizard-submissions">
|
<div class="wizard-table">
|
||||||
{{#load-more selector=".wizard-submissions tr" action=(action "loadMore")}}
|
{{#load-more selector=".wizard-table tr" action=(action "loadMore")}}
|
||||||
{{#if noResults}}
|
{{#if noResults}}
|
||||||
<p>
|
<p>{{i18n "search.no_results"}}</p>
|
||||||
{{i18n "search.no_results"}}
|
|
||||||
</p>
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -49,9 +47,7 @@
|
||||||
{{#each displaySubmissions as |submission|}}
|
{{#each displaySubmissions as |submission|}}
|
||||||
<tr>
|
<tr>
|
||||||
{{#each-in submission as |field value|}}
|
{{#each-in submission as |field value|}}
|
||||||
<td>
|
<td>{{wizard-table-field field=field value=value}}</td>
|
||||||
{{submission-field fieldName=field value=value}}
|
|
||||||
</td>
|
|
||||||
{{/each-in}}
|
{{/each-in}}
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
@ -1,163 +0,0 @@
|
||||||
{{#if isText}}
|
|
||||||
{{value.value}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isTextArea}}
|
|
||||||
<div class="submission-long-text">
|
|
||||||
<p class="submission-long-text-content {{textState}}">
|
|
||||||
{{value.value}}
|
|
||||||
</p>
|
|
||||||
<a href {{action "expandText"}}>
|
|
||||||
{{toggleText}}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isComposer}}
|
|
||||||
<div class="submission-long-text">
|
|
||||||
<p
|
|
||||||
class="submission-composer-text submission-long-text-content {{
|
|
||||||
textState
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
{{value.value}}
|
|
||||||
</p>
|
|
||||||
<a href {{action "expandText"}}>
|
|
||||||
{{toggleText}}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isComposerPreview}}
|
|
||||||
{{d-icon "comment-alt"}}
|
|
||||||
<span class="submission-composer-text">
|
|
||||||
{{i18n "admin.wizard.submissions.composer_preview"}}: {{value.value}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isTextOnly}}
|
|
||||||
{{value.value}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isDate}}
|
|
||||||
<span class="submission-icon-item">
|
|
||||||
{{d-icon "calendar"}}{{value.value}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isTime}}
|
|
||||||
<span class="submission-icon-item">
|
|
||||||
{{d-icon "clock"}}{{value.value}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isDateTime}}
|
|
||||||
<span class="submission-icon-item" title={{value.value}}>
|
|
||||||
{{d-icon "calendar"}}{{format-date value.value format="medium"}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isNumber}}
|
|
||||||
{{value.value}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isCheckbox}}
|
|
||||||
{{#if checkboxValue}}
|
|
||||||
<span class="submission-icon-item submission-checkbox-true">
|
|
||||||
{{d-icon "check"}}{{value.value}}
|
|
||||||
</span>
|
|
||||||
{{else}}
|
|
||||||
<span class="submission-icon-item submission-checkbox-false">
|
|
||||||
{{d-icon "times"}}{{value.value}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isUrl}}
|
|
||||||
<span class="submission-icon-item submission-url">
|
|
||||||
{{d-icon "link"}}
|
|
||||||
<a target="_blank" rel="noopener noreferrer" href={{value.value}}>
|
|
||||||
{{value.value}}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isUpload}}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="attachment"
|
|
||||||
href={{file.url}}
|
|
||||||
download
|
|
||||||
>
|
|
||||||
{{file.original_filename}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isDropdown}}
|
|
||||||
<span class="submission-icon-item">
|
|
||||||
{{d-icon "check-square"}}
|
|
||||||
{{value.value}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isTag}}
|
|
||||||
{{#each value.value as |tag|}}
|
|
||||||
{{discourse-tag tag}}
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isCategory}}
|
|
||||||
<strong>
|
|
||||||
{{i18n "admin.wizard.submissions.category_id"}}:
|
|
||||||
</strong>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href={{categoryUrl}}
|
|
||||||
title={{value.value}}
|
|
||||||
>
|
|
||||||
{{value.value}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isGroup}}
|
|
||||||
<strong>
|
|
||||||
{{i18n "admin.wizard.submissions.group_id"}}:
|
|
||||||
</strong>
|
|
||||||
{{value.value}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isUserSelector}}
|
|
||||||
{{#each submittedUsers as |user|}}
|
|
||||||
{{d-icon "user"}}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href={{user.url}}
|
|
||||||
title={{user.username}}
|
|
||||||
>
|
|
||||||
{{user.username}}
|
|
||||||
</a>
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isUser}}
|
|
||||||
{{#link-to "user" value}}
|
|
||||||
{{avatar value imageSize="tiny"}}
|
|
||||||
{{/link-to}}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href={{userProfileUrl}}
|
|
||||||
title={{value.name}}
|
|
||||||
>
|
|
||||||
{{value.username}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if isSubmittedAt}}
|
|
||||||
<span class="submission-date" title={{value.value}}>
|
|
||||||
{{d-icon "clock"}}{{format-date value format="tiny"}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
161
assets/javascripts/discourse/templates/components/wizard-table-field.hbs
Normale Datei
161
assets/javascripts/discourse/templates/components/wizard-table-field.hbs
Normale Datei
|
@ -0,0 +1,161 @@
|
||||||
|
{{#if hasValue}}
|
||||||
|
{{#if isText}}
|
||||||
|
{{value.value}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isLongtext}}
|
||||||
|
<div class="wizard-table-long-text">
|
||||||
|
<p class="wizard-table-long-text-content {{textState}}">
|
||||||
|
{{value.value}}
|
||||||
|
</p>
|
||||||
|
<a href {{action "expandText"}}>
|
||||||
|
{{toggleText}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isComposer}}
|
||||||
|
<div class="wizard-table-long-text">
|
||||||
|
<p class="wizard-table-composer-text wizard-table-long-text-content {{textState}}">
|
||||||
|
{{value.value}}
|
||||||
|
</p>
|
||||||
|
<a href {{action "expandText"}}>
|
||||||
|
{{toggleText}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isComposerPreview}}
|
||||||
|
{{d-icon "comment-alt"}}
|
||||||
|
<span class="wizard-table-composer-text">
|
||||||
|
{{i18n "admin.wizard.submissions.composer_preview"}}: {{value.value}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isTextOnly}}
|
||||||
|
{{value.value}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isDate}}
|
||||||
|
<span class="wizard-table-icon-item">
|
||||||
|
{{d-icon "calendar"}}{{value.value}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isTime}}
|
||||||
|
<span class="wizard-table-icon-item">
|
||||||
|
{{d-icon "clock"}}{{value.value}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isDateTime}}
|
||||||
|
<span class="wizard-table-icon-item" title={{value.value}}>
|
||||||
|
{{d-icon "calendar"}}{{format-date value.value format="medium"}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isNumber}}
|
||||||
|
{{value.value}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isCheckbox}}
|
||||||
|
{{#if checkboxValue}}
|
||||||
|
<span class="wizard-table-icon-item checkbox-true">
|
||||||
|
{{d-icon "check"}}{{value.value}}
|
||||||
|
</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="wizard-table-icon-item checkbox-false">
|
||||||
|
{{d-icon "times"}}{{value.value}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isUrl}}
|
||||||
|
<span class="wizard-table-icon-item url">
|
||||||
|
{{d-icon "link"}}
|
||||||
|
<a target="_blank" rel="noopener noreferrer" href={{value.value}}>
|
||||||
|
{{value.value}}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isUpload}}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="attachment"
|
||||||
|
href={{file.url}}
|
||||||
|
download
|
||||||
|
>
|
||||||
|
{{file.original_filename}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isDropdown}}
|
||||||
|
<span class="wizard-table-icon-item">
|
||||||
|
{{d-icon "check-square"}}
|
||||||
|
{{value.value}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isTag}}
|
||||||
|
{{#each value.value as |tag|}}
|
||||||
|
{{discourse-tag tag}}
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isCategory}}
|
||||||
|
<strong>
|
||||||
|
{{i18n "admin.wizard.submissions.category_id"}}:
|
||||||
|
</strong>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={{categoryUrl}}
|
||||||
|
title={{value.value}}
|
||||||
|
>
|
||||||
|
{{value.value}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isGroup}}
|
||||||
|
<strong>
|
||||||
|
{{i18n "admin.wizard.submissions.group_id"}}:
|
||||||
|
</strong>
|
||||||
|
{{value.value}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isUserSelector}}
|
||||||
|
{{#each submittedUsers as |user|}}
|
||||||
|
{{d-icon "user"}}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={{user.url}}
|
||||||
|
title={{user.username}}
|
||||||
|
>
|
||||||
|
{{user.username}}
|
||||||
|
</a>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isUser}}
|
||||||
|
{{#link-to "user" value}}
|
||||||
|
{{avatar value imageSize="tiny"}}
|
||||||
|
{{/link-to}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if showUsername}}
|
||||||
|
<a target="_blank" rel="noopener noreferrer" href={{userProfileUrl}} title={{username}}>
|
||||||
|
{{username}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isSubmittedAt}}
|
||||||
|
<span class="date" title={{value.value}}>
|
||||||
|
{{d-icon "clock"}}{{format-date value format="tiny"}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
—
|
||||||
|
{{/if}}
|
|
@ -1,14 +1,14 @@
|
||||||
{{#d-modal-body title="directory.edit_columns.title"}}
|
{{#d-modal-body title="admin.wizard.edit_columns"}}
|
||||||
{{#if loading}}
|
{{#if loading}}
|
||||||
{{loading-spinner size="large"}}
|
{{loading-spinner size="large"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="edit-directory-columns-container">
|
<div class="edit-directory-columns-container">
|
||||||
{{#each model.fields as |field|}}
|
{{#each model.columns as |column|}}
|
||||||
<div class="edit-directory-column">
|
<div class="edit-directory-column">
|
||||||
<div class="left-content">
|
<div class="left-content">
|
||||||
<label class="column-name">
|
<label class="column-name">
|
||||||
{{input type="checkbox" checked=field.enabled}}
|
{{input type="checkbox" checked=column.enabled}}
|
||||||
{{directory-table-header-title field=field.label translated=true}}
|
{{directory-table-header-title field=column.label translated=true}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -66,10 +66,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-submissions {
|
.wizard-table {
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
|
||||||
table td {
|
table td:not(.small) {
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,25 +77,26 @@
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.submission-icon-item {
|
.wizard-table-icon-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submission-checkbox-true {
|
.wizard-table-checkbox-true {
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
color: var(--success);
|
color: var(--success);
|
||||||
}
|
}
|
||||||
|
|
||||||
.submission-checkbox-false {
|
.wizard-table-checkbox-false {
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
.submission-long-text {
|
.wizard-table-long-text {
|
||||||
&-content {
|
&-content {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
@ -114,25 +115,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submission-composer-text {
|
.wizard-table-composer-text {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-wizards-logs {
|
|
||||||
.admin-wizard-controls {
|
|
||||||
h3 {
|
|
||||||
margin: 0 7px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.wizard-logs {
|
|
||||||
.date {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.wizard-settings-parent {
|
.wizard-settings-parent {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border: 1px solid var(--primary-low);
|
border: 1px solid var(--primary-low);
|
||||||
|
@ -215,6 +202,10 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.download-link {
|
.download-link {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
@ -230,10 +221,6 @@
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
background-color: var(--primary-low);
|
background-color: var(--primary-low);
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,9 @@ en:
|
||||||
select_type: "Select a type"
|
select_type: "Select a type"
|
||||||
condition: "Condition"
|
condition: "Condition"
|
||||||
index: "Index"
|
index: "Index"
|
||||||
|
edit_columns: "Edit Columns"
|
||||||
|
expand_text: "Read More"
|
||||||
|
collapse_text: "Show Less"
|
||||||
|
|
||||||
pro_support_button:
|
pro_support_button:
|
||||||
title: "Request Pro Support"
|
title: "Request Pro Support"
|
||||||
|
@ -108,6 +111,7 @@ en:
|
||||||
viewing: "You're viewing the submissions of the %{wizardName}"
|
viewing: "You're viewing the submissions of the %{wizardName}"
|
||||||
documentation: "Check out the submissions documentation"
|
documentation: "Check out the submissions documentation"
|
||||||
logs:
|
logs:
|
||||||
|
select: "Select a wizard to see its logs"
|
||||||
viewing: "View recent logs for wizards on the forum"
|
viewing: "View recent logs for wizards on the forum"
|
||||||
documentation: "Check out the logs documentation"
|
documentation: "Check out the logs documentation"
|
||||||
|
|
||||||
|
@ -378,9 +382,6 @@ en:
|
||||||
nav_label: "Submissions"
|
nav_label: "Submissions"
|
||||||
title: "{{name}} Submissions"
|
title: "{{name}} Submissions"
|
||||||
download: "Download"
|
download: "Download"
|
||||||
edit_columns: "Edit Columns"
|
|
||||||
expand_text: "Read More"
|
|
||||||
collapse_text: "Show Less"
|
|
||||||
group_id: "Group ID"
|
group_id: "Group ID"
|
||||||
category_id: "Category ID"
|
category_id: "Category ID"
|
||||||
composer_preview: "Composer Preview"
|
composer_preview: "Composer Preview"
|
||||||
|
@ -440,6 +441,11 @@ en:
|
||||||
|
|
||||||
log:
|
log:
|
||||||
nav_label: "Logs"
|
nav_label: "Logs"
|
||||||
|
title: "{{name}} Logs"
|
||||||
|
date: Date
|
||||||
|
action: Action
|
||||||
|
user: User
|
||||||
|
message: Message
|
||||||
|
|
||||||
manager:
|
manager:
|
||||||
nav_label: Manager
|
nav_label: Manager
|
||||||
|
|
|
@ -38,6 +38,7 @@ Discourse::Application.routes.append do
|
||||||
get 'admin/wizards/api/:name/authorize' => 'admin_api#authorize'
|
get 'admin/wizards/api/:name/authorize' => 'admin_api#authorize'
|
||||||
|
|
||||||
get 'admin/wizards/logs' => 'admin_logs#index'
|
get 'admin/wizards/logs' => 'admin_logs#index'
|
||||||
|
get 'admin/wizards/logs/:wizard_id' => 'admin_logs#show'
|
||||||
|
|
||||||
get 'admin/wizards/manager' => 'admin_manager#index'
|
get 'admin/wizards/manager' => 'admin_manager#index'
|
||||||
get 'admin/wizards/manager/export' => 'admin_manager#export'
|
get 'admin/wizards/manager/export' => 'admin_manager#export'
|
||||||
|
|
|
@ -1,9 +1,44 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class CustomWizard::AdminLogsController < CustomWizard::AdminController
|
class CustomWizard::AdminLogsController < CustomWizard::AdminController
|
||||||
|
before_action :find_wizard, except: [:index]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render_serialized(
|
render json: ActiveModel::ArraySerializer.new(
|
||||||
CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i),
|
CustomWizard::Wizard.list(current_user),
|
||||||
CustomWizard::LogSerializer
|
each_serializer: CustomWizard::BasicWizardSerializer
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
render_json_dump(
|
||||||
|
wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false),
|
||||||
|
logs: ActiveModel::ArraySerializer.new(
|
||||||
|
log_list.logs,
|
||||||
|
each_serializer: CustomWizard::LogSerializer
|
||||||
|
),
|
||||||
|
total: log_list.total
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def log_list
|
||||||
|
@log_list ||= begin
|
||||||
|
list = CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i, params[:wizard_id])
|
||||||
|
|
||||||
|
if list.logs.any? && (usernames = list.logs.map(&:username)).present?
|
||||||
|
user_map = User.where(username: usernames)
|
||||||
|
.reduce({}) do |result, user|
|
||||||
|
result[user.username] = user
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
list.logs.each do |log_item|
|
||||||
|
log_item.user = user_map[log_item.username]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
list
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"result": {
|
"result": {
|
||||||
"line": 91.96
|
"line": 92.14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +1,56 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1]
|
class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1]
|
||||||
|
KEY_MAP = {
|
||||||
|
wizard: "wizard_id",
|
||||||
|
action: "action",
|
||||||
|
user: "username",
|
||||||
|
date: "date",
|
||||||
|
message: "message"
|
||||||
|
}
|
||||||
|
|
||||||
def change
|
def change
|
||||||
reversible do |dir|
|
reversible do |dir|
|
||||||
dir.up do
|
dir.up do
|
||||||
# separate wizard/action/user into their own keys
|
# separate wizard/action/user into their own keys
|
||||||
|
|
||||||
wizard_logs = PluginStoreRow.where("
|
wizard_logs = PluginStoreRow.where("plugin_name = 'custom_wizard_log'")
|
||||||
plugin_name = 'custom_wizard_log'
|
|
||||||
")
|
|
||||||
|
|
||||||
if wizard_logs.exists?
|
if wizard_logs.exists?
|
||||||
wizard_logs.each do |row|
|
wizard_logs.each do |row|
|
||||||
begin
|
begin
|
||||||
log_json = JSON.parse(row.value)
|
log_json = JSON.parse(row.value)
|
||||||
rescue TypeError, JSON::ParserError
|
rescue TypeError, JSON::ParserError
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
if log_json.key?('message') && log_json['message'].is_a?(String)
|
if log_json.key?('message') && log_json['message'].is_a?(String)
|
||||||
|
|
||||||
attr_strs = []
|
attr_strs = []
|
||||||
|
|
||||||
# assumes no whitespace in the values
|
# assumes no whitespace in the values
|
||||||
attr_strs << log_json['message'].slice!(/(wizard: \S*; )/, 1)
|
attr_strs << log_json['message'].slice!(/(wizard: \S*; )/, 1)
|
||||||
attr_strs << log_json['message'].slice!(/(action: \S*; )/, 1)
|
attr_strs << log_json['message'].slice!(/(action: \S*; )/, 1)
|
||||||
attr_strs << log_json['message'].slice!(/(user: \S*; )/, 1)
|
attr_strs << log_json['message'].slice!(/(user: \S*; )/, 1)
|
||||||
|
|
||||||
attr_strs.each do |attr_str|
|
attr_strs.each do |attr_str|
|
||||||
if attr_str.is_a? String
|
if attr_str.is_a? String
|
||||||
attr_str.gsub!(/[;]/ , "")
|
attr_str.gsub!(/[;]/ , "")
|
||||||
key, value = attr_str.split(': ')
|
key, value = attr_str.split(': ')
|
||||||
value.strip! if value
|
value.strip! if value
|
||||||
log_json[key] = value ? value : ''
|
key = KEY_MAP[key.to_sym] ? KEY_MAP[key.to_sym] : key
|
||||||
end
|
log_json[key] = value ? value : ''
|
||||||
end
|
end
|
||||||
|
|
||||||
row.value = log_json.to_json
|
|
||||||
row.save
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
row.value = log_json.to_json
|
||||||
|
row.save
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
dir.down do
|
dir.down do
|
||||||
wizard_logs = PluginStoreRow.where("
|
wizard_logs = PluginStoreRow.where("plugin_name = 'custom_wizard_log'")
|
||||||
plugin_name = 'custom_wizard_log'
|
|
||||||
")
|
|
||||||
|
|
||||||
if wizard_logs.exists?
|
if wizard_logs.exists?
|
||||||
wizard_logs.each do |row|
|
wizard_logs.each do |row|
|
||||||
|
@ -56,19 +61,26 @@ class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1]
|
||||||
end
|
end
|
||||||
|
|
||||||
# concatenate wizard/action/user to start of message
|
# concatenate wizard/action/user to start of message
|
||||||
prefixes = log_json.extract!('wizard', 'action', 'user')
|
prefixes = log_json.extract!('wizard_id', 'action', 'username')
|
||||||
|
message_prefix = ""
|
||||||
|
|
||||||
message_prefix = prefixes.map { |k, v| "#{k}: #{v}" }.join('; ')
|
if prefixes.present?
|
||||||
|
message_prefix = prefixes.map do |k, v|
|
||||||
|
key = KEY_MAP.key(k) ? KEY_MAP.key(k) : k
|
||||||
|
"#{key.to_s}: #{v};"
|
||||||
|
end.join(' ')
|
||||||
|
end
|
||||||
|
|
||||||
if log_json.key?('message')
|
if log_json.key?('message')
|
||||||
log_json['message'] = "#{message_prefix}; #{log_json['message']}"
|
message = log_json['message']
|
||||||
|
message = "#{message_prefix} #{message}" if message_prefix.present?
|
||||||
|
log_json['message'] = message
|
||||||
else
|
else
|
||||||
log_json['message'] = message_prefix
|
log_json['message'] = message_prefix
|
||||||
end
|
end
|
||||||
|
|
||||||
row.value = log_json.to_json
|
row.value = log_json.to_json
|
||||||
row.save
|
row.save
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,46 +2,51 @@
|
||||||
class CustomWizard::Log
|
class CustomWizard::Log
|
||||||
include ActiveModel::Serialization
|
include ActiveModel::Serialization
|
||||||
|
|
||||||
attr_accessor :date, :wizard, :action, :user, :message
|
attr_reader :date, :wizard_id, :action, :username, :message
|
||||||
|
attr_accessor :user
|
||||||
|
|
||||||
PAGE_LIMIT = 100
|
PAGE_LIMIT = 100
|
||||||
|
|
||||||
def initialize(attrs)
|
def initialize(attrs)
|
||||||
@date = attrs['date']
|
@date = attrs['date']
|
||||||
@wizard = attrs['wizard']
|
|
||||||
@action = attrs['action']
|
@action = attrs['action']
|
||||||
@user = attrs['user']
|
|
||||||
@message = attrs['message']
|
@message = attrs['message']
|
||||||
|
@wizard_id = attrs['wizard_id']
|
||||||
|
@username = attrs['username']
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.create(wizard, action, user, message)
|
def self.create(wizard_id, action, username, message)
|
||||||
log_id = SecureRandom.hex(12)
|
log_id = SecureRandom.hex(12)
|
||||||
|
|
||||||
PluginStore.set('custom_wizard_log',
|
PluginStore.set('custom_wizard_log',
|
||||||
log_id.to_s,
|
log_id.to_s,
|
||||||
{
|
{
|
||||||
date: Time.now,
|
date: Time.now,
|
||||||
wizard: wizard,
|
wizard_id: wizard_id,
|
||||||
action: action,
|
action: action,
|
||||||
user: user,
|
username: username,
|
||||||
message: message
|
message: message
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.list_query
|
def self.list_query(wizard_id = nil)
|
||||||
PluginStoreRow.where("
|
query = PluginStoreRow.where("plugin_name = 'custom_wizard_log' AND (value::json->'date') IS NOT NULL")
|
||||||
plugin_name = 'custom_wizard_log' AND
|
query = query.where("(value::json->>'wizard_id') = ?", wizard_id) if wizard_id
|
||||||
(value::json->'date') IS NOT NULL
|
query.order("value::json->>'date' DESC")
|
||||||
").order("value::json->>'date' DESC")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.list(page = 0, limit = nil)
|
def self.list(page = 0, limit = nil, wizard_id = nil)
|
||||||
limit = limit.to_i > 0 ? limit.to_i : PAGE_LIMIT
|
limit = limit.to_i > 0 ? limit.to_i : PAGE_LIMIT
|
||||||
page = page.to_i
|
page = page.to_i
|
||||||
|
logs = self.list_query(wizard_id)
|
||||||
|
|
||||||
self.list_query.limit(limit)
|
result = OpenStruct.new(logs: [], total: nil)
|
||||||
|
result.total = logs.size
|
||||||
|
result.logs = logs.limit(limit)
|
||||||
.offset(page * limit)
|
.offset(page * limit)
|
||||||
.map { |r| self.new(JSON.parse(r.value)) }
|
.map { |r| self.new(JSON.parse(r.value)) }
|
||||||
|
|
||||||
|
result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CustomWizard::LogSerializer < ApplicationSerializer
|
class CustomWizard::LogSerializer < ApplicationSerializer
|
||||||
attributes :date, :wizard, :action, :user, :message
|
attributes :date,
|
||||||
|
:action,
|
||||||
|
:username,
|
||||||
|
:message
|
||||||
|
|
||||||
|
has_one :user, serializer: ::BasicUserSerializer, embed: :objects
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,19 +10,25 @@ describe CustomWizard::Log do
|
||||||
|
|
||||||
it "creates logs" do
|
it "creates logs" do
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::Log.list.length
|
CustomWizard::Log.list.logs.length
|
||||||
).to eq(3)
|
).to eq(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "lists logs by time created" do
|
it "lists logs by time created" do
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::Log.list.first.message
|
CustomWizard::Log.list.logs.first.message
|
||||||
).to eq("Third log message")
|
).to eq("Third log message")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "paginates logs" do
|
it "paginates logs" do
|
||||||
expect(
|
expect(
|
||||||
CustomWizard::Log.list(0, 2).length
|
CustomWizard::Log.list(0, 2).logs.length
|
||||||
).to eq(2)
|
).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "lists logs by wizard" do
|
||||||
|
expect(
|
||||||
|
CustomWizard::Log.list(0, 2, 'third-test-wizard').logs.length
|
||||||
|
).to eq(1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,21 +3,40 @@ require_relative '../../../plugin_helper'
|
||||||
|
|
||||||
describe CustomWizard::AdminLogsController do
|
describe CustomWizard::AdminLogsController do
|
||||||
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
||||||
|
let(:template) { get_wizard_fixture("wizard") }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
CustomWizard::Log.create('first-test-wizard', 'perform_first_action', 'first_test_user', 'First log message')
|
["first", "second", "third"].each_with_index do |key, index|
|
||||||
CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message')
|
temp = template.dup
|
||||||
CustomWizard::Log.create('third-test-wizard', 'perform_third_action', 'third_test_user', 'Third log message')
|
temp["id"] = "#{key}_test_wizard"
|
||||||
|
CustomWizard::Template.save(temp, skip_jobs: true)
|
||||||
|
CustomWizard::Log.create("#{key}_test_wizard", "perform_#{key}_action", "#{key}_test_user", "#{key} log message")
|
||||||
|
end
|
||||||
sign_in(admin_user)
|
sign_in(admin_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns a list of logs" do
|
it "returns a list of wizards" do
|
||||||
get "/admin/wizards/logs.json"
|
get "/admin/wizards/logs.json"
|
||||||
expect(response.parsed_body.length).to eq(3)
|
expect(response.parsed_body.length).to eq(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "returns a list of logs for a wizard" do
|
||||||
|
get "/admin/wizards/logs/first_test_wizard.json"
|
||||||
|
expect(response.parsed_body['logs'].length).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
it "paginates" do
|
it "paginates" do
|
||||||
get "/admin/wizards/logs.json", params: { page: 1, limit: 2 }
|
get "/admin/wizards/logs/first_test_wizard.json", params: { page: 1 }
|
||||||
expect(response.parsed_body.length).to eq(1)
|
expect(response.parsed_body['logs'].length).to eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns total logs for a wizard" do
|
||||||
|
get "/admin/wizards/logs/first_test_wizard.json"
|
||||||
|
expect(response.parsed_body['total']).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns basic wizard" do
|
||||||
|
get "/admin/wizards/logs/first_test_wizard.json"
|
||||||
|
expect(response.parsed_body['wizard']['id']).to eq("first_test_wizard")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,13 +10,12 @@ describe CustomWizard::LogSerializer do
|
||||||
CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message')
|
CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message')
|
||||||
|
|
||||||
json_array = ActiveModel::ArraySerializer.new(
|
json_array = ActiveModel::ArraySerializer.new(
|
||||||
CustomWizard::Log.list(0),
|
CustomWizard::Log.list(0).logs,
|
||||||
each_serializer: CustomWizard::LogSerializer
|
each_serializer: CustomWizard::LogSerializer
|
||||||
).as_json
|
).as_json
|
||||||
expect(json_array.length).to eq(2)
|
expect(json_array.length).to eq(2)
|
||||||
expect(json_array[0][:wizard]).to eq("second-test-wizard")
|
|
||||||
expect(json_array[0][:action]).to eq("perform_second_action")
|
expect(json_array[0][:action]).to eq("perform_second_action")
|
||||||
expect(json_array[0][:user]).to eq("second_test_user")
|
expect(json_array[0][:username]).to eq('second_test_user')
|
||||||
expect(json_array[0][:message]).to eq("Second log message")
|
expect(json_array[0][:message]).to eq("Second log message")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Laden …
In neuem Issue referenzieren