diff --git a/src/templates/settings/_includes/advanced.twig b/src/templates/settings/_includes/advanced.twig
new file mode 100644
index 00000000..09abe5dc
--- /dev/null
+++ b/src/templates/settings/_includes/advanced.twig
@@ -0,0 +1,99 @@
+{# @var craft \craft\web\twig\variables\CraftVariable #}
+{#
+/**
+ * Retour plugin for Craft CMS
+ *
+ * Retour Settings index.twig
+ *
+ * @author nystudio107
+ * @copyright Copyright (c) 2018 nystudio107
+ * @link https://nystudio107.com/
+ * @package Retour
+ * @since 3.0.0
+ */
+#}
+
+{% import "_includes/forms" as forms %}
+{% from "retour/_includes/macros.twig" import configWarning %}
+
+
+ {% namespace "settings" %}
+ {{ forms.lightswitchField({
+ label: "Enable API Access"|t("retour"),
+ instructions: "Determines whether the Retour API endpoint should be enabled for anonymous frontend access."|t("retour"),
+ id: "enableApiEndpoint",
+ name: "enableApiEndpoint",
+ on: settings.enableApiEndpoint,
+ warning: configWarning("enableApiEndpoint", "retour"),
+ errors: settings.getErrors("enableApiEndpoint"),
+ }) }}
+
+ {{ forms.editableTableField({
+ label: "Exclude Patterns"|t("retour"),
+ instructions: "[Regular expressions](https://regexr.com/) to match URIs that should be excluded from Retour."|t("retour"),
+ id: 'excludePatterns',
+ name: 'excludePatterns',
+ required: false,
+ allowAdd: true,
+ allowDelete: true,
+ allowReorder: true,
+ defaultValues: {
+ pattern: "",
+ },
+ cols: {
+ pattern: {
+ heading: "RegEx pattern to exclude"|t("retour"),
+ type: "singleline",
+ width: "100%",
+ code: true,
+ },
+ },
+ rows: settings.excludePatterns,
+ errors: settings.getErrors("excludePatterns"),
+ }) }}
+
+ {{ forms.editableTableField({
+ label: "Additional Headers"|t("retour"),
+ instructions: "Additional headers to add to the redirected request"|t("retour"),
+ id: 'additionalHeaders',
+ name: 'additionalHeaders',
+ allowAdd: true,
+ allowDelete: true,
+ allowReorder: true,
+ required: false,
+ defaultValues: {
+ name: "",
+ value: "",
+ },
+ cols: {
+ name: {
+ heading: "Header Name"|t("retour"),
+ type: "singleline",
+ width: "50%",
+ code: true,
+ },
+ value: {
+ heading: "Header Value"|t("retour"),
+ type: "singleline",
+ width: "50%",
+ code: true,
+ },
+ },
+ rows: settings.additionalHeaders,
+ errors: settings.getErrors("additionalHeaders"),
+ }) }}
+
+ {{ forms.textField({
+ label: "CSV Delimiter"|t("retour"),
+ instructions: "The delimiter between data column values for importing CSV files (normally `,`)."|t("retour"),
+ id: "csvColumnDelimiter",
+ name: "csvColumnDelimiter",
+ size: 1,
+ maxlength: 1,
+ value: settings.csvColumnDelimiter,
+ warning: configWarning("csvColumnDelimiter", "retour"),
+ errors: settings.getErrors("csvColumnDelimiter"),
+ }) }}
+
+ {% endnamespace %}
+
diff --git a/src/templates/settings/_includes/general.twig b/src/templates/settings/_includes/general.twig
new file mode 100644
index 00000000..13379cb4
--- /dev/null
+++ b/src/templates/settings/_includes/general.twig
@@ -0,0 +1,75 @@
+{# @var craft \craft\web\twig\variables\CraftVariable #}
+{#
+/**
+ * Retour plugin for Craft CMS
+ *
+ * Retour Settings index.twig
+ *
+ * @author nystudio107
+ * @copyright Copyright (c) 2018 nystudio107
+ * @link https://nystudio107.com/
+ * @package Retour
+ * @since 3.0.0
+ */
+#}
+
+{% import "_includes/forms" as forms %}
+{% from "retour/_includes/macros.twig" import configWarning %}
+
+
+ {% namespace "settings" %}
+ {{ forms.textField({
+ label: "Plugin name"|t("retour"),
+ instructions: "The public-facing name of the plugin"|t("retour"),
+ id: "pluginName",
+ name: "pluginName",
+ value: settings.pluginName,
+ warning: configWarning("pluginName", "retour"),
+ errors: settings.getErrors("pluginName"),
+ }) }}
+
+ {{ forms.lightswitchField({
+ label: "Create Entry Redirects"|t("retour"),
+ instructions: "Controls whether Retour automatically creates static redirects when an entry's URI changes."|t("retour"),
+ id: "createUriChangeRedirects",
+ name: "createUriChangeRedirects",
+ on: settings.createUriChangeRedirects,
+ warning: configWarning("createUriChangeRedirects", "retour"),
+ errors: settings.getErrors("createUriChangeRedirects"),
+ }) }}
+
+ {{ forms.selectField({
+ label: "Entry Redirects URL Match Type"|t("retour"),
+ instructions: "Should the automatically created Entry Redirects be matched by path (e.g. `/new-recipes/`) or by full URL (e.g.: `http://example.com/de/new-recipes/`)?"|t("retour"),
+ id: "uriChangeRedirectSrcMatch",
+ name: "uriChangeRedirectSrcMatch",
+ value: settings.uriChangeRedirectSrcMatch ?? "pathonly",
+ options: {
+ "pathonly": "Path Only"|t("retour"),
+ "fullurl": "Full URL"|t("retour"),
+ },
+ errors: settings.getErrors("uriChangeRedirectSrcMatch"),
+ }) }}
+
+ {{ forms.lightswitchField({
+ label: "Strip Query String from 404s"|t("retour"),
+ instructions: "Should the query string be stripped from all 404 URLs before their evaluation?"|t("retour"),
+ id: "alwaysStripQueryString",
+ name: "alwaysStripQueryString",
+ on: settings.alwaysStripQueryString,
+ warning: configWarning("alwaysStripQueryString", "retour"),
+ errors: settings.getErrors("alwaysStripQueryString"),
+ }) }}
+
+ {{ forms.lightswitchField({
+ label: "Preserve Query String"|t("retour"),
+ instructions: "Should the query string be preserved and passed along to the redirected URL?"|t("retour"),
+ id: "preserveQueryString",
+ name: "preserveQueryString",
+ on: settings.preserveQueryString,
+ warning: configWarning("preserveQueryString", "retour"),
+ errors: settings.getErrors("preserveQueryString"),
+ }) }}
+
+ {% endnamespace %}
+
diff --git a/src/templates/settings/_includes/statistics.twig b/src/templates/settings/_includes/statistics.twig
new file mode 100644
index 00000000..d043f916
--- /dev/null
+++ b/src/templates/settings/_includes/statistics.twig
@@ -0,0 +1,98 @@
+{# @var craft \craft\web\twig\variables\CraftVariable #}
+{#
+/**
+ * Retour plugin for Craft CMS
+ *
+ * Retour Settings index.twig
+ *
+ * @author nystudio107
+ * @copyright Copyright (c) 2018 nystudio107
+ * @link https://nystudio107.com/
+ * @package Retour
+ * @since 3.0.0
+ */
+#}
+
+{% import "_includes/forms" as forms %}
+{% from "retour/_includes/macros.twig" import configWarning %}
+
+
+ {% namespace "settings" %}
+ {{ forms.lightswitchField({
+ label: "Strip Query String from Statistics"|t("retour"),
+ instructions: "Should the query string be stripped from the saved statistics source URLs?"|t("retour"),
+ id: "stripQueryStringFromStats",
+ name: "stripQueryStringFromStats",
+ on: settings.stripQueryStringFromStats,
+ warning: configWarning("stripQueryStringFromStats", "retour"),
+ errors: settings.getErrors("stripQueryStringFromStats"),
+ }) }}
+
+ {{ forms.lightswitchField({
+ label: "Record Remote IP"|t("retour"),
+ instructions: "Should the anonymous ip address of the client causing a 404 be recorded?"|t("retour"),
+ id: "recordRemoteIp",
+ name: "recordRemoteIp",
+ on: settings.recordRemoteIp,
+ warning: configWarning("recordRemoteIp", "retour"),
+ errors: settings.getErrors("recordRemoteIp"),
+ }) }}
+
+ {{ forms.textField({
+ label: "Statistics to Store"|t("retour"),
+ instructions: "How many unique 404 statistics should be stored before they are trimmed."|t("retour"),
+ id: "statsStoredLimit",
+ name: "statsStoredLimit",
+ size: 7,
+ maxlength: 7,
+ value: settings.statsStoredLimit,
+ warning: configWarning("statsStoredLimit", "retour"),
+ errors: settings.getErrors("statsStoredLimit"),
+ }) }}
+
+ {{ forms.selectField({
+ label: "Dashboard Refresh Interval"|t("retour"),
+ instructions: "Dashboard data live refresh interval."|t("retour"),
+ id: "refreshIntervalSecs",
+ name: "refreshIntervalSecs",
+ options: {
+ 0: "Never"|t("retour"),
+ 5: "5 seconds"|t("retour"),
+ 10: "10 seconds"|t("retour"),
+ 30: "30 seconds"|t("retour"),
+ 60: "60 seconds"|t("retour"),
+ },
+ size: 7,
+ maxlength: 7,
+ value: settings.refreshIntervalSecs,
+ warning: configWarning("refreshIntervalSecs", "retour"),
+ errors: settings.getErrors("refreshIntervalSecs"),
+ }) }}
+
+ {{ forms.lightswitchField({
+ label: "Automatically Trim Statistics"|t("retour"),
+ instructions: "Whether the Statistics should be trimmed after each new statistic is recorded."|t("retour"),
+ id: "automaticallyTrimStatistics",
+ name: "automaticallyTrimStatistics",
+ on: settings.automaticallyTrimStatistics,
+ warning: configWarning("automaticallyTrimStatistics", "retour"),
+ errors: settings.getErrors("automaticallyTrimStatistics"),
+ }) }}
+
+ {{ forms.selectField({
+ label: "Statistics Trimming Rate Limit"|t("retour"),
+ instructions: "The amount of time required between trimming of statistics."|t("retour"),
+ id: "statisticsRateLimitMs",
+ name: "statisticsRateLimitMs",
+ options: {
+ 3600000: "Once per hour"|t("retour"),
+ 86400000: "Once per day"|t("retour"),
+ 604800000: "Once per week"|t("retour"),
+ },
+ value: settings.statisticsRateLimitMs,
+ warning: configWarning("statisticsRateLimitMs", "retour"),
+ errors: settings.getErrors("statisticsRateLimitMs"),
+ }) }}
+
+ {% endnamespace %}
+
diff --git a/src/templates/settings/index.twig b/src/templates/settings/index.twig
index b89c0e73..37b45b2b 100644
--- a/src/templates/settings/index.twig
+++ b/src/templates/settings/index.twig
@@ -18,222 +18,30 @@
{% extends "_layouts/cp" %}
{% import "_includes/forms" as forms %}
+
{% from "retour/_includes/macros.twig" import configWarning %}
+{% set tabs = {
+ "general": {label: "General"|t('retour'), url: "#general"},
+ "appearance": {label: "Statistics"|t('retour'), url: "#statistics"},
+ "advanced": {label: "Advanced"|t('retour'), url: "#advanced"},
+} %}
+
{% block content %}
{{ redirectInput("retour/settings") }}
- {% namespace "settings" %}
- {{ forms.textField({
- label: "Plugin name"|t("retour"),
- instructions: "The public-facing name of the plugin"|t("retour"),
- id: "pluginName",
- name: "pluginName",
- value: settings.pluginName,
- warning: configWarning("pluginName", "retour"),
- errors: settings.getErrors("pluginName"),
- }) }}
-
- {{ forms.lightswitchField({
- label: "Create Entry Redirects"|t("retour"),
- instructions: "Controls whether Retour automatically creates static redirects when an entry's URI changes."|t("retour"),
- id: "createUriChangeRedirects",
- name: "createUriChangeRedirects",
- on: settings.createUriChangeRedirects,
- warning: configWarning("createUriChangeRedirects", "retour"),
- errors: settings.getErrors("createUriChangeRedirects"),
- }) }}
-
- {{ forms.selectField({
- label: "Entry Redirects URL Match Type"|t("retour"),
- instructions: "Should the automatically created Entry Redirects be matched by path (e.g. `/new-recipes/`) or by full URL (e.g.: `http://example.com/de/new-recipes/`)?"|t("retour"),
- id: "uriChangeRedirectSrcMatch",
- name: "uriChangeRedirectSrcMatch",
- value: settings.uriChangeRedirectSrcMatch ?? "pathonly",
- options: {
- "pathonly": "Path Only"|t("retour"),
- "fullurl": "Full URL"|t("retour"),
- },
- errors: settings.getErrors("uriChangeRedirectSrcMatch"),
- }) }}
-
- {{ forms.lightswitchField({
- label: "Strip Query String from 404s"|t("retour"),
- instructions: "Should the query string be stripped from all 404 URLs before their evaluation?"|t("retour"),
- id: "alwaysStripQueryString",
- name: "alwaysStripQueryString",
- on: settings.alwaysStripQueryString,
- warning: configWarning("alwaysStripQueryString", "retour"),
- errors: settings.getErrors("alwaysStripQueryString"),
- }) }}
-
- {{ forms.lightswitchField({
- label: "Preserve Query String"|t("retour"),
- instructions: "Should the query string be preserved and passed along to the redirected URL?"|t("retour"),
- id: "preserveQueryString",
- name: "preserveQueryString",
- on: settings.preserveQueryString,
- warning: configWarning("preserveQueryString", "retour"),
- errors: settings.getErrors("preserveQueryString"),
- }) }}
-
- {{ forms.lightswitchField({
- label: "Strip Query String from Statistics"|t("retour"),
- instructions: "Should the query string be stripped from the saved statistics source URLs?"|t("retour"),
- id: "stripQueryStringFromStats",
- name: "stripQueryStringFromStats",
- on: settings.stripQueryStringFromStats,
- warning: configWarning("stripQueryStringFromStats", "retour"),
- errors: settings.getErrors("stripQueryStringFromStats"),
- }) }}
-
- {{ forms.lightswitchField({
- label: "Record Remote IP"|t("retour"),
- instructions: "Should the anonymous ip address of the client causing a 404 be recorded?"|t("retour"),
- id: "recordRemoteIp",
- name: "recordRemoteIp",
- on: settings.recordRemoteIp,
- warning: configWarning("recordRemoteIp", "retour"),
- errors: settings.getErrors("recordRemoteIp"),
- }) }}
-
- {{ forms.textField({
- label: "Statistics to Store"|t("retour"),
- instructions: "How many unique 404 statistics should be stored before they are trimmed."|t("retour"),
- id: "statsStoredLimit",
- name: "statsStoredLimit",
- size: 7,
- maxlength: 7,
- value: settings.statsStoredLimit,
- warning: configWarning("statsStoredLimit", "retour"),
- errors: settings.getErrors("statsStoredLimit"),
- }) }}
-
- {{ forms.selectField({
- label: "Dashboard Refresh Interval"|t("retour"),
- instructions: "Dashboard data live refresh interval."|t("retour"),
- id: "refreshIntervalSecs",
- name: "refreshIntervalSecs",
- options: {
- 0: "Never"|t("retour"),
- 5: "5 seconds"|t("retour"),
- 10: "10 seconds"|t("retour"),
- 30: "30 seconds"|t("retour"),
- 60: "60 seconds"|t("retour"),
- },
- size: 7,
- maxlength: 7,
- value: settings.refreshIntervalSecs,
- warning: configWarning("refreshIntervalSecs", "retour"),
- errors: settings.getErrors("refreshIntervalSecs"),
- }) }}
-
- {{ forms.lightswitchField({
- label: "Automatically Trim Statistics"|t("retour"),
- instructions: "Whether the Statistics should be trimmed after each new statistic is recorded."|t("retour"),
- id: "automaticallyTrimStatistics",
- name: "automaticallyTrimStatistics",
- on: settings.automaticallyTrimStatistics,
- warning: configWarning("automaticallyTrimStatistics", "retour"),
- errors: settings.getErrors("automaticallyTrimStatistics"),
- }) }}
-
- {{ forms.selectField({
- label: "Statistics Trimming Rate Limit"|t("retour"),
- instructions: "The amount of time required between trimming of statistics."|t("retour"),
- id: "statisticsRateLimitMs",
- name: "statisticsRateLimitMs",
- options: {
- 3600000: "Once per hour"|t("retour"),
- 86400000: "Once per day"|t("retour"),
- 604800000: "Once per week"|t("retour"),
- },
- value: settings.statisticsRateLimitMs,
- warning: configWarning("statisticsRateLimitMs", "retour"),
- errors: settings.getErrors("statisticsRateLimitMs"),
- }) }}
-
- {{ forms.lightswitchField({
- label: "Enable API Access"|t("retour"),
- instructions: "Determines whether the Retour API endpoint should be enabled for anonymous frontend access."|t("retour"),
- id: "enableApiEndpoint",
- name: "enableApiEndpoint",
- on: settings.enableApiEndpoint,
- warning: configWarning("enableApiEndpoint", "retour"),
- errors: settings.getErrors("enableApiEndpoint"),
- }) }}
-
- {{ forms.editableTableField({
- label: "Exclude Patterns"|t("retour"),
- instructions: "[Regular expressions](https://regexr.com/) to match URIs that should be excluded from Retour."|t("retour"),
- id: 'excludePatterns',
- name: 'excludePatterns',
- required: false,
- allowAdd: true,
- allowDelete: true,
- allowReorder: true,
- defaultValues: {
- pattern: "",
- },
- cols: {
- pattern: {
- heading: "RegEx pattern to exclude"|t("retour"),
- type: "singleline",
- width: "100%",
- code: true,
- },
- },
- rows: settings.excludePatterns,
- errors: settings.getErrors("excludePatterns"),
- }) }}
- {{ forms.editableTableField({
- label: "Additional Headers"|t("retour"),
- instructions: "Additional headers to add to the redirected request"|t("retour"),
- id: 'additionalHeaders',
- name: 'additionalHeaders',
- allowAdd: true,
- allowDelete: true,
- allowReorder: true,
- required: false,
- defaultValues: {
- name: "",
- value: "",
- },
- cols: {
- name: {
- heading: "Header Name"|t("retour"),
- type: "singleline",
- width: "50%",
- code: true,
- },
- value: {
- heading: "Header Value"|t("retour"),
- type: "singleline",
- width: "50%",
- code: true,
- },
- },
- rows: settings.additionalHeaders,
- errors: settings.getErrors("additionalHeaders"),
- }) }}
+ {# -- General settings -- #}
+ {% include "retour/settings/_includes/general.twig" %}
- {{ forms.textField({
- label: "CSV Delimiter"|t("retour"),
- instructions: "The delimiter between data column values for importing CSV files (normally `,`)."|t("retour"),
- id: "csvColumnDelimiter",
- name: "csvColumnDelimiter",
- size: 1,
- maxlength: 1,
- value: settings.csvColumnDelimiter,
- warning: configWarning("csvColumnDelimiter", "retour"),
- errors: settings.getErrors("csvColumnDelimiter"),
- }) }}
+ {# -- Statistics settings -- #}
+ {% include "retour/settings/_includes/statistics.twig" %}
- {% endnamespace %}
+ {# -- Advanced settings -- #}
+ {% include "retour/settings/_includes/advanced.twig" %}
{# include our JavaScript modules #}
{{ parent() }}