Adds a refactor menu and checks the current document for property issues.
- Tags
- Identifier
de.smartics.userscripts.confluence.projectdoc-refactor-document
- Type
- Repository
- Since
- 1.0
The userscript runs checks on the properties specified in the properties table of the projectdoc document. If the check finds issues, a report is rendered on the top of the current page.
It add a menu with actions to clean this document and its children.
Code
The code of the script for reference.
projectdoc-refactor-document.js
/*
* Copyright 2019-2024 Kronseder & Reiner GmbH, smartics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
AJS.toInit(function () {
const logToConsole = true;
if (logToConsole) {
AJS.log("[projectdoc-refactor-document] Refactoring tools ...");
}
const $propertiesMarker = AJS.$(".projectdoc-document-element.properties");
if (!$propertiesMarker.length) {
if (logToConsole) AJS.log("[projectdoc-refactor-document] Not a projectdoc document. Quitting.");
return;
}
const findMessageContainer = function () {
let $messageContainer = AJS.$("#messageContainer");
if ($messageContainer.length) {
const $li = AJS.$("<li></li>");
$messageContainer.append($li);
return $li;
}
$messageContainer = AJS.$("#action-messages");
if ($messageContainer.length) {
return $messageContainer;
}
$messageContainer = AJS.$("#full-height-container");
return $messageContainer;
};
const setTooltip = function ($element, text) {
AJS.$($element).tooltip({
title: function () {
return text;
}
});
};
const showMessageIn = function ($messages, title, $content, type) {
if ($messages.length) {
const $message = AJS.$("<div></div>");
$message.addClass("aui-message aui-message-" + type);
const $title = AJS.$("<p></p>");
$title.addClass("title");
const $titleSpan = AJS.$("<strong></strong>");
$titleSpan.text(title);
$title.append($titleSpan);
$message.append($title);
$message.append($content);
$messages.append($message);
} else {
AJS.log("[projectdoc-refactor-document] Failed to locate element with " + dialogId + " to render messages");
}
};
const showMessage = function (dialogId, title, $content, type) {
const $messages = AJS.$("#" + dialogId);
showMessageIn($messages, title, $content, type);
};
const addTableRow = function ($table, typeName, originalValue, cleanedValue) {
if (originalValue) {
const $tr = AJS.$('<tr></tr>');
$tr.append(AJS.$('<th class="confluenceTh">' + typeName + '</th>'));
const $tdOriginal = AJS.$('<td class="confluenceTd"></td>');
$tdOriginal.text(originalValue);
$tr.append($tdOriginal);
const $tdCleaned = AJS.$('<td class="confluenceTd"></td>');
$tdCleaned.text(cleanedValue);
$tr.append($tdCleaned);
$table.append($tr);
}
};
const removeOldMessage = function ($messageContainer) {
if ($messageContainer) {
const $oldMessage = $messageContainer.find("#userscript-document-report");
if ($oldMessage) {
$oldMessage.remove();
}
}
};
const renderReport = function (baseUrl, title, report) {
if (!report) {
return;
}
const pageReports = report["page-reports"];
const issueCount = report["issue-count"];
if (issueCount <= 0) {
if (pageReports) {
if (logToConsole) AJS.log(AJS.format("[projectdoc-refactor-document] Checked {0} pages, found no properties with issues according to configured checks!", pageReports.length));
} else {
AJS.log("[projectdoc-refactor-document] Invalid report returned.");
}
return;
}
if (logToConsole) AJS.log(AJS.format("[projectdoc-refactor-document] Checked {0} pages, found {1} properties with issues according to configured checks!", pageReports.length, issueCount));
const $messageContainer = findMessageContainer();
if ($messageContainer.length) {
const isOnePageReport = pageReports.length == 1;
const $message = AJS.$('<div id="userscript-document-report" class="aui-message aui-message-error">\n' +
'<p class="title">\n' +
' <strong>' + title + '</strong>\n' +
'</p>\n' +
// '<p>The following issues have been encountered.</p>\n' +
// '<h3>Invalid Properties</h3>\n' +
(isOnePageReport ? '<p>This page contains ' + issueCount + ' properties with issues.</p>\n' : '<p>Checked ' + pageReports.length + ' pages, found ' + issueCount + ' properties with issues according to configured checks.</p>\n') +
'<div id="userscript-document-report-issues"></div>\n' +
'</div>');
const $divReport = AJS.$($message).find("#userscript-document-report-issues");
if ($divReport.length) {
AJS.$.each(pageReports, function (index, pageReport) {
if (logToConsole) AJS.log("[projectdoc-refactor-document] Processing page report: " + JSON.stringify(pageReport));
const $reportItem = AJS.$('<div></div>');
if (!isOnePageReport) {
$reportItem.append(AJS.$('<h3><a href="' + baseUrl + "/pages/viewpage.action?pageId=" + pageReport["page-id"] + '">' + pageReport["page-title"] + '</a></h3>'));
} else {
$reportItem.append("<div/>");
}
const $propertiesIssues = AJS.$('<div></div>');
$propertiesIssues.append("<div/>");
AJS.$.each(pageReport["issues"], function (index, propertyIssues) {
if (isOnePageReport) {
$propertiesIssues.append(AJS.$('<h3>' + propertyIssues["property-name"] + '</h3>'));
} else {
$propertiesIssues.append(AJS.$('<h4>' + propertyIssues["property-name"] + '</h4>'));
}
const $issuesTable = AJS.$('<table class="confluenceTable"></table>');
addTableRow($issuesTable, "Name", propertyIssues["original-name"], propertyIssues["cleaned-name"]);
addTableRow($issuesTable, "Value", propertyIssues["original-value"], propertyIssues["cleaned-value"]);
addTableRow($issuesTable, "Controls", propertyIssues["original-controls"], propertyIssues["cleaned-controls"]);
$propertiesIssues.append($issuesTable);
});
$reportItem.append($propertiesIssues);
$divReport.append($reportItem);
});
$divReport.append(AJS.$('<p>For property values not to be altered by the document cleaning process, apply the <a href="https://www.smartics.eu/confluence/x/DoDsAg">preserve</a> property control.</p>'));
const $mainButtons = AJS.$("<div class='buttons-container' style='margin-top: 1em;'></div>");
$divReport.append($mainButtons);
const $submit = AJS.$("<button class='aui-button aui-button-primary' id='userscript-document-report-clean-button'><span class=\"aui-icon aui-icon-small aui-iconfont-upload\">" + AJS.I18n.getText('de.smartics.userscripts.button.delete.icon') + "</span> Clean now</button>");
setTooltip($submit, "Clean document, removing the reported issues.");
AJS.$($submit).on('click', function (e) {
e.preventDefault();
removeOldMessage($messageContainer);
const $spinner = AJS.$('<div id="userscript-document-report"><h4>Cleaning document</h4></div>');
$spinner.append(AJS.$('<p>Removing issues from document properties ...</p>'));
$spinner.append(AJS.$('<aui-spinner size="large"></aui-spinner>'));
$spinner.append(AJS.$('<p style="margin-bottom: 2em;"><em>(The page will be reloaded once the cleaning process is finished.)</em></p>'));
$messageContainer.append($spinner);
cleanDocumentAction();
});
$mainButtons.append($submit);
removeOldMessage($messageContainer);
$messageContainer.append($message);
} else {
AJS.log(AJS.format("[projectdoc-refactor-document] Failed to locate own list by ID #userscript-document-report-issues! Skipping report ..."));
}
} else {
AJS.log(AJS.format("[projectdoc-refactor-document] Failed to locate message container on page with ID #messageContainer! Skipping report ..."));
}
};
const renderContentReport = function (baseUrl, title, report) {
if (!report) {
return;
}
const issueCount = report["adjustment-count"];
if (issueCount <= 0) {
if (logToConsole) AJS.log(AJS.format("[projectdoc-refactor-document] Checked 1 page, found no issues according to configured checks!"));
return;
}
const $messageContainer = findMessageContainer();
if ($messageContainer.length) {
const $message = AJS.$('<div id="userscript-document-report" class="aui-message aui-message-error">\n' +
'<p class="title">\n' +
' <strong>' + title + '</strong>\n' +
'</p>\n' +
// '<p>The following issues have been encountered.</p>\n' +
// '<h3>Invalid Properties</h3>\n' +
'<p>This page containes ' + issueCount + ' content issues.</p>\n' +
'</div>');
const transformerReports = report["details"]["space-reports"][0]["page-reports"][0]["transformer-reports"];
const $details = AJS.$("<ul></ul>");
$message.append($details);
for (let i = 0; i < transformerReports.length; i++) {
const transformerReport = transformerReports[i];
const $item = AJS.$("<li>" + transformerReport["id"] + ": " + transformerReport["adjustment-count"] + "</li>");
$details.append($item);
}
const $mainButtons = AJS.$("<div class='buttons-container' style='margin-top: 1em;'></div>");
$message.append($mainButtons);
const $submit = AJS.$("<button class='aui-button aui-button-primary' id='userscript-document-report-clean-button'><span class=\"aui-icon aui-icon-small aui-iconfont-upload\">" + AJS.I18n.getText('de.smartics.userscripts.button.delete.icon') + "</span> Clean now</button>");
setTooltip($submit, "Clean document, removing the reported issues.");
AJS.$($submit).on('click', function (e) {
e.preventDefault();
removeOldMessage($messageContainer);
const $spinner = AJS.$('<div id="userscript-document-report"><h4>Cleaning document</h4></div>');
$spinner.append(AJS.$('<p>Removing issues from document content ...</p>'));
$spinner.append(AJS.$('<aui-spinner size="large"></aui-spinner>'));
$spinner.append(AJS.$('<p style="margin-bottom: 2em;"><em>(The page will be reloaded once the cleaning process is finished.)</em></p>'));
$messageContainer.append($spinner);
cleanDocumentContentAction();
});
$mainButtons.append($submit);
removeOldMessage($messageContainer);
$messageContainer.append($message);
} else {
AJS.log(AJS.format("[projectdoc-refactor-document] Failed to locate message container on page with ID #messageContainer! Skipping report ..."));
}
};
const cleanDocument = function (reportOnly, includeChildren, reload) {
const pageId = AJS.Meta.get('page-id');
const baseUrl = AJS.Meta.get('base-url');
const serviceUrl = baseUrl + "/rest/projectdoc/1/service/cleanup?id-list=" + pageId + (reportOnly ? "&report-only=true" : "&comment=Document+clean+process") + (includeChildren ? "&include-children=true" : "");
AJS.$.ajax({
url: serviceUrl,
type: "POST",
dataType: 'json',
contentType: "application/json",
data: ""
}).success(function (data) {
if (logToConsole) AJS.log("[projectdoc-refactor-document] " + (reportOnly ? 'Checked' : 'Cleaned') + ' document ' + pageId + ' successfully!');
if (logToConsole) AJS.log('[projectdoc-refactor-document] Response: ' + JSON.stringify(data));
if (reportOnly) {
const report = data["report"];
renderReport(baseUrl, "Property Cleaning Report", report);
}
if (reload) {
location.reload();
}
}).error(function (jqXHR, textStatus) {
AJS.log("[projectdoc-refactor-document] Error " + (reportOnly ? "checking" : "cleaning") + " document: " + jqXHR.status + " (" + textStatus + ")");
if (!reportOnly) {
const $messageContainer = findMessageContainer();
removeOldMessage($messageContainer);
showMessageIn($messageContainer, "Error", AJS.$("<p>Failed to clean document (" + jqXHR.status + " / " + textStatus + ").</p>"), "error");
}
// alert("Failed to clean document: " + jqXHR.status + " (" + textStatus + ")");
});
};
const cleanDocumentContent = function (reportOnly, reload) {
const pageId = AJS.Meta.get('page-id');
const baseUrl = AJS.Meta.get('base-url');
// ,consecutive-whitespaces
const serviceUrl = baseUrl + "/rest/smartics-workbench/1/traverser/pages?page-ids=" + pageId + "&processor=" + encodeURIComponent("{processor: \"clean-entity\", transformers=\"empty-paragraph-remover,punctuation\"}") + " &dry-run=" + (reportOnly ? "true" : "false&comment=Document+content+clean+process");
AJS.$.ajax({
url: serviceUrl,
type: "POST",
dataType: 'json',
contentType: "application/json",
data: ""
}).success(function (data) {
if (logToConsole) AJS.log("[projectdoc-refactor-document] " + (reportOnly ? 'Checked' : 'Cleaned') + ' document ' + pageId + '\'s content successfully!');
if (logToConsole) AJS.log('[projectdoc-refactor-document] Response: ' + JSON.stringify(data));
if (reportOnly) {
const report = data; //["report"];
renderContentReport(baseUrl, "Document Content Cleaning Report", report);
}
if (reload) {
location.reload();
}
}).error(function (jqXHR, textStatus) {
AJS.log("[projectdoc-refactor-document] Error " + (reportOnly ? "checking" : "cleaning") + " document content: " + jqXHR.status + " (" + textStatus + ")");
if (!reportOnly) {
const $messageContainer = findMessageContainer();
removeOldMessage($messageContainer);
showMessageIn($messageContainer, "Error", AJS.$("<p>Failed to clean document content (" + jqXHR.status + " / " + textStatus + ").</p>"), "error");
}
// alert("Failed to clean document: " + jqXHR.status + " (" + textStatus + ")");
});
};
const reindexCurrentSpace = function () {
const spaceKey = AJS.Meta.get('space-key');
const baseUrl = AJS.Meta.get('base-url');
const serviceUrl = baseUrl + "/rest/projectdoc-internal/1/indexer/spaces?body-only=true&spaceKeys=" + spaceKey;
AJS.$.ajax({
url: serviceUrl,
type: "POST",
dataType: 'json',
contentType: "application/json",
data: "",
statusCode: {
202: function (xhr) {
AJS.log("[projectdoc-refactor-document] Reindex space " + spaceKey + " successfully started: " + JSON.stringify(xhr));
const message = "Successfully started reindexing current space (" + spaceKey + "). <p>Job: " + xhr.responseText + "</p>";
AJS.flag({
type: 'info',
close: 'auto',
body: message
});
}
}
}).success(function (data) {
if (logToConsole) AJS.log("[projectdoc-refactor-document] Reindex space " + spaceKey + " successfully started: " + JSON.stringify(data));
}).error(function (jqXHR, textStatus) {
if (jqXHR.status != 202) {
AJS.log("[projectdoc-refactor-document] Error reindexing space " + spaceKey + " (" + jqXHR.status + " / " + textStatus + ")!");
}
});
};
const cleanDocumentAction = function () {
cleanDocument(false, false, true);
}
const reportAction = function () {
cleanDocument(true, false, false);
}
const cleanDocumentsAction = function () {
cleanDocument(false, true, true);
}
const reportDocumentContentAction = function () {
cleanDocumentContent(true, false);
}
const cleanDocumentContentAction = function () {
cleanDocumentContent(false, true);
}
const createMenu = function () {
const menuId = "refactor";
const sectionId = "projectdoc-refactor-menu-clean";
const $mainMenu = USERSCRIPT4C_MENU.createMenu(menuId, "Refactor");
USERSCRIPT4C_MENU.registerMenu("view.menu", $mainMenu, "inspect");
// In case you need to append the menu to an element identified by a selector, use this:
// USERSCRIPT4C_MENU.registerBySelector($mainMenu, "#my-id");
USERSCRIPT4C_MENU.addSection(menuId, {
id: sectionId,
label: "Clean",
weight: 10
});
USERSCRIPT4C_MENU.addMenuItem(sectionId, {
id: "projectdoc-menu-refactor-item-document-clean-document",
label: "Clean document",
weight: "100"
}, cleanDocumentAction);
USERSCRIPT4C_MENU.addMenuItem(sectionId, {
id: "projectdoc-menu-refactor-item-space-clean-documents",
label: "Clean with child documents",
weight: "200"
}, cleanDocumentsAction);
USERSCRIPT4C_MENU.addMenuItem(sectionId, {
id: "projectdoc-menu-refactor-item-document-clean-content",
label: "Clean document content",
weight: "300"
}, cleanDocumentContentAction);
const reindexSectionId = "projectdoc-refactor-menu-reindex";
USERSCRIPT4C_MENU.addSection(menuId, {
id: reindexSectionId,
label: "Reindex",
weight: 20
});
USERSCRIPT4C_MENU.addMenuItem(reindexSectionId, {
id: "projectdoc-menu-refactor-item-space-reindex",
label: "Reindex current space",
weight: "100"
}, reindexCurrentSpace);
return createMenu;
}
if (logToConsole) AJS.log("[projectdoc-refactor-document] Adding refactoring menu ...");
createMenu();
reportAction();
reportDocumentContentAction();
});
Details
More information on using this userscript.
Report
The report renders each document property with all issues concerning the name, value , and controls.
The issues found on the current page can be resolve immediately by clicking the "Clean now" button.
Related Scripts
Name | Short Description |
---|---|
Removes projectdoc tools (blueprints and macros) from the current page. |
|
Renders a menu with tools to inspect information from a projectdoc document, shown in the browser. |
|
Provides an interface to specify and launch queries for projectdoc documents. |
Resources
More information on this topic is available by the following resources.
- Document Cleanup
- Runs a projectdoc cleanup on the referenced document.
- preserve
- Prevents cleanup services from applying their changes to name, value, and controls of a property.