-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7766 from JaydenTeoh/feat/add-tags-type
Tag Edit UI & Plugin System
- Loading branch information
Showing
21 changed files
with
524 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
""" | ||
For processing Tags | ||
""" | ||
|
||
import json | ||
|
||
from infogami.utils.view import public | ||
|
||
|
||
@public | ||
def process_plugins_data(data): | ||
plugin_type = list(data.keys())[0] | ||
# Split the string into key-value pairs | ||
parameters = data[plugin_type].split(',') | ||
|
||
# Create a dictionary to store the formatted parameters | ||
plugin_data = {} | ||
|
||
# Iterate through the pairs and extract the key-value information | ||
for pair in parameters: | ||
key, value = pair.split('=') | ||
key = key.strip() | ||
plugin_data[key] = eval(value) | ||
|
||
return plugin_type, plugin_data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
$def with (plugins) | ||
|
||
$for plugin in plugins: | ||
$ plugin_type, plugin_data = process_plugins_data(plugin) | ||
$:macros[plugin_type](**plugin_data) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
$def with(subjects) | ||
|
||
$# Takes following parameters | ||
$# * subjects (str) -- A string containing a comma-separated list of subjects | ||
|
||
$ subject_filters = subjects.split('+') | ||
|
||
<body id="related-subjects"> | ||
<div id="related-subject-filters"> | ||
<label><input type="checkbox" class="select-all"> Select All</label> | ||
$for filter in subject_filters: | ||
<label><input type="checkbox" class="subject-filter"> $filter</label> | ||
</div> | ||
<div id="related-subjects-carousel"> | ||
$ query_string = get_related_subjects_query() | ||
$:macros.QueryCarousel(query=query_string, title=_("You might also like"), key="related-subjects-carousel") | ||
</div> | ||
</body> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/** | ||
* Functionality for Tags form | ||
*/ | ||
import 'jquery-ui/ui/widgets/sortable'; | ||
|
||
const pluginsTypesList = ['RelatedSubjects', 'QueryCarousel', 'ListCarousel'] | ||
|
||
function checkRequiredFields() { | ||
const nameInput = document.getElementById('tag_name'); | ||
const descriptionInput = document.getElementById('tag_description'); | ||
const tagType = document.getElementById('tag_type'); | ||
if (!nameInput.value) { | ||
nameInput.focus({focusVisible: true}); | ||
throw new Error('Name is required'); | ||
} | ||
if (!descriptionInput.value) { | ||
descriptionInput.focus({focusVisible: true}); | ||
throw new Error('Description is required'); | ||
} | ||
if (!tagType.value) { | ||
tagType.focus({focusVisible: true}); | ||
throw new Error('Tag type is required'); | ||
} | ||
} | ||
|
||
export function initPluginsForm() { | ||
document.querySelector('.addPluginBtn').addEventListener('click', function() { | ||
const newRow = ` | ||
<tr class="plugins-input-row"> | ||
<td> | ||
<select id="plugins_type" class="select-tag-plugins-container"> | ||
<option value="">Select Plugin</option> | ||
${pluginsTypesList.map(pluginType => `<option value="${pluginType}">${pluginType}</option>`).join('')} | ||
</select> | ||
</td> | ||
<td><textarea id="plugin_data_input"></textarea></td> | ||
<td><span class="delete-plugin-btn">[X]</span></td> | ||
<td><span class="drag-handle">☰</span></td> | ||
</tr>` | ||
document | ||
.getElementById('pluginsFormRows') | ||
.insertAdjacentHTML('beforeEnd', newRow); | ||
initDeletePluginbtns(); // Reinitialize the delete-row-buttons' onclick listener | ||
}); | ||
|
||
// Make the table rows draggable | ||
$('#pluginsFormRows').sortable({ | ||
handle: '.drag-handle' | ||
}); | ||
|
||
initDeletePluginbtns(); | ||
} | ||
|
||
// Handle plugin deletion | ||
function initDeletePluginbtns() { | ||
document.querySelectorAll('.delete-plugin-btn').forEach(function(row) { | ||
row.addEventListener('click', function() { | ||
row.closest('.plugins-input-row').remove(); | ||
}); | ||
}); | ||
} | ||
|
||
export function initAddTagForm() { | ||
document | ||
.getElementById('addtag') | ||
.addEventListener('submit', function(e) { | ||
e.preventDefault(); | ||
clearPluginsAndInputErrors(); | ||
try { | ||
checkRequiredFields(); | ||
} catch (e) { | ||
return; | ||
} | ||
let pluginsData = []; | ||
try { | ||
pluginsData = getPluginsData(); | ||
} catch (e) { | ||
return; | ||
} | ||
const pluginsInput = document.getElementById('tag_plugins'); | ||
pluginsInput.value = JSON.stringify(pluginsData); | ||
// Submit the form | ||
this.submit(); | ||
}); | ||
} | ||
|
||
export function initEditTagForm() { | ||
document | ||
.getElementById('edittag') | ||
.addEventListener('submit', function(e) { | ||
e.preventDefault(); | ||
clearPluginsAndInputErrors(); | ||
try { | ||
checkRequiredFields(); | ||
} catch (e) { | ||
return; | ||
} | ||
let pluginsData = []; | ||
try { | ||
pluginsData = getPluginsData(); | ||
} catch (e) { | ||
return; | ||
} | ||
const pluginsInput = document.getElementById('tag_plugins'); | ||
pluginsInput.value = JSON.stringify(pluginsData); | ||
// Submit the form | ||
this.submit(); | ||
}); | ||
} | ||
|
||
function getPluginsData() { | ||
const formData = []; | ||
document.querySelectorAll('.plugins-input-row').forEach(function(row) { | ||
const pluginsType = row.querySelector('#plugins_type').value; | ||
const dataInput = row.querySelector('#plugin_data_input').value; | ||
|
||
const newPlugin = {} | ||
newPlugin[pluginsType] = dataInput | ||
const error = parseAndValidatePluginsData(newPlugin); | ||
if (error) { | ||
const errorDiv = document.getElementById('plugin_errors'); | ||
errorDiv.classList.remove('hidden'); | ||
errorDiv.textContent = error; | ||
row.classList.add('invalid-tag-plugins-error'); | ||
throw new Error(error); | ||
} | ||
|
||
formData.push(newPlugin); | ||
}); | ||
|
||
return formData; | ||
} | ||
|
||
function clearPluginsAndInputErrors() { | ||
const nameInput = document.getElementById('tag_name'); | ||
const descriptionInput = document.getElementById('tag_description'); | ||
const tagType = document.getElementById('tag_type'); | ||
nameInput.focus({focusVisible: false}); | ||
descriptionInput.focus({focusVisible: false}); | ||
tagType.focus({focusVisible: false}); | ||
const errorDiv = document.getElementById('plugin_errors'); | ||
errorDiv.classList.add('hidden'); | ||
document.querySelectorAll('.plugins-input-row').forEach(function(row) { | ||
row.classList.remove('invalid-tag-plugins-error'); | ||
}); | ||
} | ||
|
||
function parseAndValidatePluginsData(plugin) { | ||
const validInputRegex = /^[\w\s]+=(?:'[^']*'|"[^"]*"|\w+)$/; | ||
const pluginType = Object.keys(plugin)[0]; | ||
const pluginData = plugin[pluginType]; | ||
if (!pluginType) { | ||
return 'Plugin type is required'; | ||
} | ||
if (!pluginsTypesList.includes(pluginType)) { | ||
return `Invalid plugin type: ${pluginType}`; | ||
} | ||
if (!pluginData) { | ||
return 'Plugin parameters are required'; | ||
} | ||
const keyValuePairs = pluginData.split(', '); | ||
for (const pair of keyValuePairs) { | ||
if (!pair.includes('=')) { | ||
return 'Missing equal sign: Each parameter should be in the form of \'key=value\''; | ||
} | ||
const splitResults = pair.split('='); | ||
if (splitResults.length !== 2) { | ||
return 'Too many equal signs: Each parameter should be in the form of \'key=value\''; | ||
} | ||
const value = splitResults[1]; | ||
|
||
if (!validInputRegex.test(pair)) { | ||
return `Invalid parameters: ${value}`; | ||
} | ||
} | ||
return null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* global render_subjects_carousel */ | ||
|
||
import { render } from 'less'; | ||
|
||
export function initRelatedSubjectsCarousel() { | ||
const subjectCheckboxes = document.querySelectorAll('.subject-filter'); | ||
subjectCheckboxes.forEach((checkbox) => { | ||
checkbox.addEventListener('change', renderSubjectsCarousel); | ||
}) | ||
} | ||
|
||
function generateQuery() { | ||
const selectedSubjects = []; | ||
const checkboxes = document.querySelectorAll('.subject-filter:checked'); | ||
checkboxes.forEach((checkbox) => { | ||
const subject = checkbox.parentNode.textContent.trim(); | ||
selectedSubjects.push(subject); | ||
}); | ||
const generatedString = selectedSubjects.join('&'); | ||
return generatedString; | ||
} | ||
|
||
function renderSubjectsCarousel() { | ||
const queryString = generateQuery(); | ||
const url = new URL(window.location.href); | ||
url.searchParams.set('subjects', queryString); | ||
window.history.replaceState(null, null, url); | ||
$('#related-subjects-carousel').load(`${window.location.href} #related-subjects-carousel`) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from infogami.utils.view import public | ||
import web | ||
import json | ||
|
||
|
||
@public | ||
def load_plugin_json(plugins_str): | ||
return json.loads(plugins_str) | ||
|
||
|
||
@public | ||
def display_plugins_data(data): | ||
plugin_type = list(data.keys())[0] | ||
# Split the string into key-value pairs | ||
parameters = data[plugin_type].split(',') | ||
|
||
# Create a dictionary to store the formatted parameters | ||
plugin_fields = [] | ||
|
||
# Iterate through the pairs and extract the key-value information | ||
for pair in parameters: | ||
try: | ||
key, value = pair.split('=', 1) | ||
key = key.strip() | ||
plugin_fields.append(f'{key}={value}') | ||
except ValueError: | ||
plugin_fields.append(pair) | ||
|
||
plugin_data = ', '.join(plugin_fields) | ||
|
||
return plugin_type, plugin_data | ||
|
||
|
||
@public | ||
def get_tag_types(): | ||
return ["subject", "work", "collection"] | ||
|
||
|
||
@public | ||
def get_plugin_types(): | ||
return ["RelatedSubjects", "QueryCarousel", "ListCarousel"] | ||
|
||
|
||
def setup(): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.