0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-05-08 10:13:32 +02:00

Apply new table style to wizard logs view

Dieser Commit ist enthalten in:
angusmcleod 2021-09-09 14:07:12 +08:00
Ursprung a8e81150f1
Commit 7b57e7fcab
26 geänderte Dateien mit 599 neuen und 378 gelöschten Zeilen

Datei anzeigen

@ -1,10 +1,11 @@
import Component from "@ember/component";
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 I18n from "I18n";
export default Component.extend({
classNameBindings: ["value.type"],
isText: equal("value.type", "text"),
isComposer: equal("value.type", "composer"),
isDate: equal("value.type", "date"),
@ -18,13 +19,29 @@ export default Component.extend({
isTag: equal("value.type", "tag"),
isCategory: equal("value.type", "category"),
isGroup: equal("value.type", "group"),
isUser: equal("fieldName", "username"),
isUserSelector: equal("value.type", "user_selector"),
isSubmittedAt: equal("fieldName", "submitted_at"),
isTextArea: equal("value.type", "textarea"),
isSubmittedAt: equal("field", "submitted_at"),
isComposerPreview: equal("value.type", "composer_preview"),
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")
checkboxValue(value) {
@ -44,10 +61,10 @@ export default Component.extend({
if (state === "text-collapsed") {
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") {
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;
},
@discourseComputed("value")
userProfileUrl(value) {
const isUser = this.get("isUser");
@discourseComputed("isUser", "field", "value")
username(isUser, field, value) {
if (isUser) {return value.username;}
if (field === "username") {return value.value;}
return null;
},
if (isUser) {
return `/u/${value.username}`;
}
showUsername: notEmpty("username"),
@discourseComputed("username")
userProfileUrl(username) {
if (username) {return `/u/${username}`;}
return "/";
},
@discourseComputed("value")
categoryUrl(value) {
const isCategory = this.get("isCategory");
if (isCategory) {
return `/c/${value.value}`;
}
@ -104,7 +126,6 @@ export default Component.extend({
@discourseComputed("value")
groupUrl(value) {
const isGroup = this.get("isGroup");
if (isGroup) {
return `/g/${value.value}`;
}

Datei anzeigen

@ -6,10 +6,9 @@ export default Controller.extend(ModalFunctionality, {
save() {
this.send("closeModal");
},
resetToDefault() {
this.get("model.fields").forEach((field) => {
field.set("enabled", true);
});
this.get("model.reset")();
},
},
});

Datei anzeigen

@ -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();
},
},
});

Datei anzeigen

@ -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 { default as discourseComputed } from "discourse-common/utils/decorators";
export default Controller.extend({
refreshing: false,
hasLogs: notEmpty("logs"),
page: 0,
canLoadMore: true,
logs: [],
documentationUrl: "https://thepavilion.io/t/2818",
messageKey: "viewing",
loadLogs() {
if (!this.canLoadMore) {
return;
@discourseComputed("wizardId")
wizardName(wizardId) {
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);
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();
},
return key;
},
});

Datei anzeigen

@ -54,10 +54,14 @@ export default Controller.extend({
},
showEditColumnsModal() {
return showModal("admin-wizards-submissions-columns", {
return showModal("admin-wizards-columns", {
model: {
fields: this.get("fields"),
submissions: this.get("submissions"),
columns: this.get("fields"),
reset: () => {
this.get("fields").forEach((field) => {
field.set("enabled", true);
});
},
},
});
},

Datei anzeigen

@ -43,10 +43,16 @@ export default {
}
);
this.route("adminWizardsLogs", {
path: "/logs",
resetNamespace: true,
});
this.route(
"adminWizardsLogs",
{ path: "/logs", resetNamespace: true },
function () {
this.route("adminWizardsLogsShow", {
path: "/:wizardId/",
resetNamespace: true,
});
}
);
this.route("adminWizardsManager", {
path: "/manager",

Datei anzeigen

@ -3,14 +3,48 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
import EmberObject from "@ember/object";
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({
list(page = 0) {
return ajax("/admin/wizards/logs", {
data: {
page,
},
}).catch(popupAjaxError);
list(wizardId, page = 0) {
let data = {
page,
};
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;
});
},
});

Datei anzeigen

@ -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,
});
},
});

Datei anzeigen

@ -1,12 +1,24 @@
import CustomWizardLogs from "../models/custom-wizard-logs";
import DiscourseRoute from "discourse/routes/discourse";
import { ajax } from "discourse/lib/ajax";
export default DiscourseRoute.extend({
model() {
return CustomWizardLogs.list();
return ajax(`/admin/wizards/wizard`);
},
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);
},
},
});

Datei anzeigen

@ -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}}

Datei anzeigen

@ -1,11 +1,11 @@
<div class="admin-wizard-controls">
<h3>{{i18n "admin.wizard.log.nav_label"}}</h3>
{{d-button
label="refresh"
icon="sync"
action="refresh"
class="refresh"}}
<div class="admin-wizard-select admin-wizard-controls">
{{combo-box
value=wizardId
content=wizardList
onChange=(route-action "changeWizard")
options=(hash
none="admin.wizard.select"
)}}
</div>
{{wizard-message
@ -14,27 +14,6 @@
url=documentationUrl
component="logs"}}
{{#load-more selector=".log-list tr" action=(action "loadMore") class="wizard-logs"}}
{{#if noResults}}
<p>{{i18n "search.no_results"}}</p>
{{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}}
<div class="admin-wizard-container">
{{outlet}}
</div>

Datei anzeigen

@ -7,7 +7,7 @@
<div class="controls">
{{d-button
icon="sliders-h"
label="admin.wizard.submissions.edit_columns"
label="admin.wizard.edit_columns"
action=(action "showEditColumnsModal")
class="btn-default open-edit-columns-btn download-link"
}}
@ -26,12 +26,10 @@
</a>
</div>
<div class="wizard-submissions">
{{#load-more selector=".wizard-submissions tr" action=(action "loadMore")}}
<div class="wizard-table">
{{#load-more selector=".wizard-table tr" action=(action "loadMore")}}
{{#if noResults}}
<p>
{{i18n "search.no_results"}}
</p>
<p>{{i18n "search.no_results"}}</p>
{{else}}
<table>
<thead>
@ -49,9 +47,7 @@
{{#each displaySubmissions as |submission|}}
<tr>
{{#each-in submission as |field value|}}
<td>
{{submission-field fieldName=field value=value}}
</td>
<td>{{wizard-table-field field=field value=value}}</td>
{{/each-in}}
</tr>
{{/each}}

Datei anzeigen

@ -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}}

Datei anzeigen

@ -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}}
&mdash;
{{/if}}

Datei anzeigen

@ -1,14 +1,14 @@
{{#d-modal-body title="directory.edit_columns.title"}}
{{#d-modal-body title="admin.wizard.edit_columns"}}
{{#if loading}}
{{loading-spinner size="large"}}
{{else}}
<div class="edit-directory-columns-container">
{{#each model.fields as |field|}}
{{#each model.columns as |column|}}
<div class="edit-directory-column">
<div class="left-content">
<label class="column-name">
{{input type="checkbox" checked=field.enabled}}
{{directory-table-header-title field=field.label translated=true}}
{{input type="checkbox" checked=column.enabled}}
{{directory-table-header-title field=column.label translated=true}}
</label>
</div>
</div>

Datei anzeigen

@ -66,10 +66,10 @@
}
}
.wizard-submissions {
.wizard-table {
overflow: scroll;
table td {
table td:not(.small) {
min-width: 150px;
}
@ -77,25 +77,26 @@
text-transform: capitalize;
}
.submission-icon-item {
.wizard-table-icon-item {
display: flex;
align-items: center;
svg {
margin-right: 5px;
}
}
.submission-checkbox-true {
.wizard-table-checkbox-true {
text-transform: capitalize;
color: var(--success);
}
.submission-checkbox-false {
.wizard-table-checkbox-false {
text-transform: capitalize;
color: var(--danger);
}
.submission-long-text {
.wizard-table-long-text {
&-content {
white-space: nowrap;
word-wrap: break-word;
@ -114,25 +115,11 @@
}
}
.submission-composer-text {
.wizard-table-composer-text {
font-family: monospace;
}
}
.admin-wizards-logs {
.admin-wizard-controls {
h3 {
margin: 0 7px;
}
}
.wizard-logs {
.date {
width: 100px;
}
}
}
.wizard-settings-parent {
padding: 20px;
border: 1px solid var(--primary-low);
@ -215,6 +202,10 @@
margin-bottom: 0;
}
button {
font-size: 1rem;
}
.download-link {
font-size: 1rem;
line-height: 20px;
@ -230,10 +221,6 @@
font-size: 1rem;
background-color: var(--primary-low);
}
button {
font-size: 1rem;
}
}
}

Datei anzeigen

@ -58,6 +58,9 @@ en:
select_type: "Select a type"
condition: "Condition"
index: "Index"
edit_columns: "Edit Columns"
expand_text: "Read More"
collapse_text: "Show Less"
pro_support_button:
title: "Request Pro Support"
@ -108,6 +111,7 @@ en:
viewing: "You're viewing the submissions of the %{wizardName}"
documentation: "Check out the submissions documentation"
logs:
select: "Select a wizard to see its logs"
viewing: "View recent logs for wizards on the forum"
documentation: "Check out the logs documentation"
@ -378,9 +382,6 @@ en:
nav_label: "Submissions"
title: "{{name}} Submissions"
download: "Download"
edit_columns: "Edit Columns"
expand_text: "Read More"
collapse_text: "Show Less"
group_id: "Group ID"
category_id: "Category ID"
composer_preview: "Composer Preview"
@ -437,9 +438,14 @@ en:
log:
label: "Logs"
log:
nav_label: "Logs"
title: "{{name}} Logs"
date: Date
action: Action
user: User
message: Message
manager:
nav_label: Manager

Datei anzeigen

@ -38,6 +38,7 @@ Discourse::Application.routes.append do
get 'admin/wizards/api/:name/authorize' => 'admin_api#authorize'
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/export' => 'admin_manager#export'

Datei anzeigen

@ -1,9 +1,44 @@
# frozen_string_literal: true
class CustomWizard::AdminLogsController < CustomWizard::AdminController
before_action :find_wizard, except: [:index]
def index
render_serialized(
CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i),
CustomWizard::LogSerializer
render json: ActiveModel::ArraySerializer.new(
CustomWizard::Wizard.list(current_user),
each_serializer: CustomWizard::BasicWizardSerializer
)
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

Datei anzeigen

@ -1,5 +1,5 @@
{
"result": {
"line": 91.96
"line": 92.14
}
}

Datei anzeigen

@ -1,51 +1,56 @@
# frozen_string_literal: true
class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1]
KEY_MAP = {
wizard: "wizard_id",
action: "action",
user: "username",
date: "date",
message: "message"
}
def change
reversible do |dir|
dir.up do
# separate wizard/action/user into their own keys
wizard_logs = PluginStoreRow.where("
plugin_name = 'custom_wizard_log'
")
wizard_logs = PluginStoreRow.where("plugin_name = 'custom_wizard_log'")
if wizard_logs.exists?
wizard_logs.each do |row|
begin
log_json = JSON.parse(row.value)
rescue TypeError, JSON::ParserError
next
end
if wizard_logs.exists?
wizard_logs.each do |row|
begin
log_json = JSON.parse(row.value)
rescue TypeError, JSON::ParserError
next
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
attr_strs << log_json['message'].slice!(/(wizard: \S*; )/, 1)
attr_strs << log_json['message'].slice!(/(action: \S*; )/, 1)
attr_strs << log_json['message'].slice!(/(user: \S*; )/, 1)
# assumes no whitespace in the values
attr_strs << log_json['message'].slice!(/(wizard: \S*; )/, 1)
attr_strs << log_json['message'].slice!(/(action: \S*; )/, 1)
attr_strs << log_json['message'].slice!(/(user: \S*; )/, 1)
attr_strs.each do |attr_str|
if attr_str.is_a? String
attr_str.gsub!(/[;]/ , "")
key, value = attr_str.split(': ')
value.strip! if value
log_json[key] = value ? value : ''
end
attr_strs.each do |attr_str|
if attr_str.is_a? String
attr_str.gsub!(/[;]/ , "")
key, value = attr_str.split(': ')
value.strip! if value
key = KEY_MAP[key.to_sym] ? KEY_MAP[key.to_sym] : key
log_json[key] = value ? value : ''
end
row.value = log_json.to_json
row.save
end
row.value = log_json.to_json
row.save
end
end
end
end
dir.down do
wizard_logs = PluginStoreRow.where("
plugin_name = 'custom_wizard_log'
")
wizard_logs = PluginStoreRow.where("plugin_name = 'custom_wizard_log'")
if wizard_logs.exists?
wizard_logs.each do |row|
@ -56,19 +61,26 @@ class SplitCustomWizardLogFields < ActiveRecord::Migration[6.1]
end
# 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')
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
log_json['message'] = message_prefix
end
row.value = log_json.to_json
row.save
end
end
end

Datei anzeigen

@ -2,46 +2,51 @@
class CustomWizard::Log
include ActiveModel::Serialization
attr_accessor :date, :wizard, :action, :user, :message
attr_reader :date, :wizard_id, :action, :username, :message
attr_accessor :user
PAGE_LIMIT = 100
def initialize(attrs)
@date = attrs['date']
@wizard = attrs['wizard']
@action = attrs['action']
@user = attrs['user']
@message = attrs['message']
@wizard_id = attrs['wizard_id']
@username = attrs['username']
end
def self.create(wizard, action, user, message)
def self.create(wizard_id, action, username, message)
log_id = SecureRandom.hex(12)
PluginStore.set('custom_wizard_log',
log_id.to_s,
{
date: Time.now,
wizard: wizard,
wizard_id: wizard_id,
action: action,
user: user,
username: username,
message: message
}
)
end
def self.list_query
PluginStoreRow.where("
plugin_name = 'custom_wizard_log' AND
(value::json->'date') IS NOT NULL
").order("value::json->>'date' DESC")
def self.list_query(wizard_id = nil)
query = PluginStoreRow.where("plugin_name = 'custom_wizard_log' AND (value::json->'date') IS NOT NULL")
query = query.where("(value::json->>'wizard_id') = ?", wizard_id) if wizard_id
query.order("value::json->>'date' DESC")
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
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)
.map { |r| self.new(JSON.parse(r.value)) }
result
end
end

Datei anzeigen

@ -1,4 +1,10 @@
# frozen_string_literal: true
class CustomWizard::LogSerializer < ApplicationSerializer
attributes :date, :wizard, :action, :user, :message
attributes :date,
:action,
:username,
:message
has_one :user, serializer: ::BasicUserSerializer, embed: :objects
end

Datei anzeigen

@ -10,19 +10,25 @@ describe CustomWizard::Log do
it "creates logs" do
expect(
CustomWizard::Log.list.length
CustomWizard::Log.list.logs.length
).to eq(3)
end
it "lists logs by time created" do
expect(
CustomWizard::Log.list.first.message
CustomWizard::Log.list.logs.first.message
).to eq("Third log message")
end
it "paginates logs" do
expect(
CustomWizard::Log.list(0, 2).length
CustomWizard::Log.list(0, 2).logs.length
).to eq(2)
end
it "lists logs by wizard" do
expect(
CustomWizard::Log.list(0, 2, 'third-test-wizard').logs.length
).to eq(1)
end
end

Datei anzeigen

@ -3,21 +3,40 @@ require_relative '../../../plugin_helper'
describe CustomWizard::AdminLogsController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
let(:template) { get_wizard_fixture("wizard") }
before do
CustomWizard::Log.create('first-test-wizard', 'perform_first_action', 'first_test_user', 'First log message')
CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message')
CustomWizard::Log.create('third-test-wizard', 'perform_third_action', 'third_test_user', 'Third log message')
["first", "second", "third"].each_with_index do |key, index|
temp = template.dup
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)
end
it "returns a list of logs" do
it "returns a list of wizards" do
get "/admin/wizards/logs.json"
expect(response.parsed_body.length).to eq(3)
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
get "/admin/wizards/logs.json", params: { page: 1, limit: 2 }
expect(response.parsed_body.length).to eq(1)
get "/admin/wizards/logs/first_test_wizard.json", params: { page: 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

Datei anzeigen

@ -10,13 +10,12 @@ describe CustomWizard::LogSerializer do
CustomWizard::Log.create('second-test-wizard', 'perform_second_action', 'second_test_user', 'Second log message')
json_array = ActiveModel::ArraySerializer.new(
CustomWizard::Log.list(0),
CustomWizard::Log.list(0).logs,
each_serializer: CustomWizard::LogSerializer
).as_json
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][: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")
end
end