diff --git a/asset-bundles.json b/asset-bundles.json
index 209aa16..bf94419 100644
--- a/asset-bundles.json
+++ b/asset-bundles.json
@@ -2,7 +2,42 @@
"bundle": {
"js/pages/group": {
"scripts": [
- "uf-tweaks/js/pages/group.js"
+ "uf-tweaks/js/pages/group.js",
+ "uf-tweaks/js/widgets/groups.js",
+ "uf-tweaks/js/slug-override.js"
+ ],
+ "options": {
+ "sprinkle": {
+ "onCollision": "merge"
+ }
+ }
+ },
+ "js/pages/groups": {
+ "scripts": [
+ "uf-tweaks/js/widgets/groups.js",
+ "uf-tweaks/js/slug-override.js"
+ ],
+ "options": {
+ "sprinkle": {
+ "onCollision": "merge"
+ }
+ }
+ },
+ "js/pages/role": {
+ "scripts": [
+ "uf-tweaks/js/widgets/roles.js",
+ "uf-tweaks/js/slug-override.js"
+ ],
+ "options": {
+ "sprinkle": {
+ "onCollision": "merge"
+ }
+ }
+ },
+ "js/pages/roles": {
+ "scripts": [
+ "uf-tweaks/js/widgets/roles.js",
+ "uf-tweaks/js/slug-override.js"
],
"options": {
"sprinkle": {
diff --git a/assets/uf-tweaks/js/slug-override.js b/assets/uf-tweaks/js/slug-override.js
new file mode 100644
index 0000000..36eeb4a
--- /dev/null
+++ b/assets/uf-tweaks/js/slug-override.js
@@ -0,0 +1,20 @@
+
+function slugOverride(form, button_id) {
+ // Auto-generate slug
+ form.find('input[name=name]').on('input change', function() {
+ var manualSlug = form.find(button_id).prop('checked');
+ if (!manualSlug) {
+ var slug = getSlug($(this).val());
+ form.find('input[name=slug]').val(slug);
+ }
+ });
+
+ form.find(button_id).on('change', function() {
+ if ($(this).prop('checked')) {
+ form.find('input[name=slug]').prop('readonly', false);
+ } else {
+ form.find('input[name=slug]').prop('readonly', true);
+ form.find('input[name=name]').trigger('change');
+ }
+ });
+}
diff --git a/assets/uf-tweaks/js/widgets/groups.js b/assets/uf-tweaks/js/widgets/groups.js
new file mode 100644
index 0000000..eb9d6d7
--- /dev/null
+++ b/assets/uf-tweaks/js/widgets/groups.js
@@ -0,0 +1,64 @@
+/**
+ * Groups widget. Sets up dropdowns, modals, etc for a table of groups.
+ *
+ * This script depends on slug-override.js
+ *
+ */
+
+/**
+ * Override the admin "attachGroupForm" method for the sake of an element id override
+ */
+function attachGroupForm() {
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+
+ /**
+ * Set up modal widgets
+ */
+ // Set up any widgets inside the modal
+ form.find(".js-select2").select2({
+ width: '100%'
+ });
+
+ // Auto-generate slug
+ slugOverride(form, '#input-group-slug-override');
+
+ // Fontawesome-iconpicker
+ // Starcraft icons
+ var sc_icons = [{
+ title: "sc sc-terran",
+ searchTerms: ['starcraft', 'terran']
+ },
+ {
+ title: "sc sc-zerg",
+ searchTerms: ['starcraft', 'zerg']
+ }, {
+ title: "sc sc-protoss",
+ searchTerms: ['starcraft', 'protoss']
+ },
+ ]
+
+ $('.icp-auto').iconpicker({
+ // this is a hacky way to add in our custom icons to the default FA5 icons.
+ // See https://github.com/farbelous/fontawesome-iconpicker/issues/77
+ icons: typeof sc_icons != 'undefined' ? $.merge(sc_icons, $.iconpicker.defaultOptions.icons) : null,
+ });
+
+ // Set icon when changed
+ form.find('input[name=icon]').on('input change', function() {
+ $(this).prev(".icon-preview").find("i").removeClass().addClass($(this).val());
+ });
+
+ $('.icp-auto').iconpicker();
+
+ // Set up the form for submission
+ form.ufForm({
+ validator: page.validators
+ }).on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+}
diff --git a/assets/uf-tweaks/js/widgets/roles.js b/assets/uf-tweaks/js/widgets/roles.js
new file mode 100644
index 0000000..ca19865
--- /dev/null
+++ b/assets/uf-tweaks/js/widgets/roles.js
@@ -0,0 +1,29 @@
+/**
+ * Roles widget. Sets up dropdowns, modals, etc for a table of roles.
+ *
+ * This script depends on slug-override.js
+ *
+ */
+
+/**
+ * Override the admin "attachRoleForm" method for the sake of an element id override
+ */
+function attachRoleForm() {
+ $("body").on('renderSuccess.ufModal', function (data) {
+ var modal = $(this).ufModal('getModal');
+ var form = modal.find('.js-form');
+
+ /**
+ * Set up modal widgets
+ */
+ slugOverride(form, '#input-role-slug-override');
+
+ // Set up the form for submission
+ form.ufForm({
+ validator: page.validators
+ }).on("submitSuccess.ufForm", function() {
+ // Reload page on success
+ window.location.reload();
+ });
+ });
+}
diff --git a/locale/en_US/messages.php b/locale/en_US/messages.php
new file mode 100644
index 0000000..b758190
--- /dev/null
+++ b/locale/en_US/messages.php
@@ -0,0 +1,12 @@
+ 'Icon',
+];
\ No newline at end of file
diff --git a/templates/forms/group.html.twig b/templates/forms/group.html.twig
new file mode 100644
index 0000000..999757c
--- /dev/null
+++ b/templates/forms/group.html.twig
@@ -0,0 +1,11 @@
+{% extends "@admin/forms/group.html.twig" %}
+
+{% block group_form %}
+ {% include "forms/partials/input-group-name.html.twig" with { 'col_width': 'col-sm-12' } %}
+
+ {% include "forms/partials/input-group-slug.html.twig" with { 'col_width': 'col-sm-12' } %}
+
+ {% include "forms/partials/input-group-icon.html.twig" with { 'col_width': 'col-sm-12' } %}
+
+ {% include "forms/partials/input-group-description.html.twig" with { 'col_width': 'col-sm-12' } %}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/forms/partials/abstract/input-description.html.twig b/templates/forms/partials/abstract/input-description.html.twig
new file mode 100644
index 0000000..fcde7dc
--- /dev/null
+++ b/templates/forms/partials/abstract/input-description.html.twig
@@ -0,0 +1,8 @@
+{% if 'description' not in form.fields.hidden %}
+ {% if col_width %}
{% endif %}
+
+
+
+
+ {% if col_width %}
{% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/templates/forms/partials/abstract/input-icon.html.twig b/templates/forms/partials/abstract/input-icon.html.twig
new file mode 100644
index 0000000..4a0ca95
--- /dev/null
+++ b/templates/forms/partials/abstract/input-icon.html.twig
@@ -0,0 +1,11 @@
+{% if 'icon' not in form.fields.hidden %}
+ {% if col_width %}
{% endif %}
+
+
+
+
+
+
+
+ {% if col_width %}
{% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/templates/forms/partials/abstract/input-name.html.twig b/templates/forms/partials/abstract/input-name.html.twig
new file mode 100644
index 0000000..b62c000
--- /dev/null
+++ b/templates/forms/partials/abstract/input-name.html.twig
@@ -0,0 +1,11 @@
+{% if 'name' not in form.fields.hidden %}
+ {% if col_width %}
{% endif %}
+
+
+
+
+
+
+
+ {% if col_width %}
{% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/templates/forms/partials/abstract/input-slug.html.twig b/templates/forms/partials/abstract/input-slug.html.twig
new file mode 100644
index 0000000..b4afad0
--- /dev/null
+++ b/templates/forms/partials/abstract/input-slug.html.twig
@@ -0,0 +1,18 @@
+{% if 'description' not in form.fields.hidden %}
+ {% if col_width %}
{% endif %}
+
+
+
+
+
+ {% if 'slug' not in form.fields.disabled %}
+
+
+
+ {% endif %}
+
+
+ {% if col_width %}
{% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/templates/forms/partials/input-group-description.html.twig b/templates/forms/partials/input-group-description.html.twig
new file mode 100644
index 0000000..5be96cf
--- /dev/null
+++ b/templates/forms/partials/input-group-description.html.twig
@@ -0,0 +1,9 @@
+{% block input_group_description %}
+{% include "forms/partials/abstract/input-description.html.twig" with
+ {
+ "type" : "group",
+ "current_value" : group.description,
+ "col_width" : col_width
+ }
+%}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/forms/partials/input-group-icon.html.twig b/templates/forms/partials/input-group-icon.html.twig
new file mode 100644
index 0000000..cafbeed
--- /dev/null
+++ b/templates/forms/partials/input-group-icon.html.twig
@@ -0,0 +1,11 @@
+{% block input_group_icon %}
+{% include "forms/partials/abstract/input-icon.html.twig" with
+ {
+ "type" : "group",
+ "current_value" : group.icon,
+ "field_name" : translate('GROUP.ICON'),
+ "placeholder" : translate('GROUP.ICON_EXPLAIN'),
+ "col_width" : col_width
+ }
+%}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/forms/partials/input-group-name.html.twig b/templates/forms/partials/input-group-name.html.twig
new file mode 100644
index 0000000..2f810e6
--- /dev/null
+++ b/templates/forms/partials/input-group-name.html.twig
@@ -0,0 +1,11 @@
+{% block input_group_name %}
+{% include "forms/partials/abstract/input-name.html.twig" with
+ {
+ "type" : "group",
+ "current_value" : group.name,
+ "field_name" : translate('GROUP.NAME'),
+ "placeholder" : translate('GROUP.NAME_EXPLAIN'),
+ "col_width" : col_width
+ }
+%}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/forms/partials/input-group-slug.html.twig b/templates/forms/partials/input-group-slug.html.twig
new file mode 100644
index 0000000..c24f8fa
--- /dev/null
+++ b/templates/forms/partials/input-group-slug.html.twig
@@ -0,0 +1,9 @@
+{% block input_group_slug %}
+{% include "forms/partials/abstract/input-slug.html.twig" with
+ {
+ "type" : "group",
+ "current_value" : group.slug,
+ "col_width" : col_width
+ }
+%}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/forms/partials/input-role-description.html.twig b/templates/forms/partials/input-role-description.html.twig
new file mode 100644
index 0000000..f521e5a
--- /dev/null
+++ b/templates/forms/partials/input-role-description.html.twig
@@ -0,0 +1,9 @@
+{% block input_role_description %}
+{% include "forms/partials/abstract/input-description.html.twig" with
+ {
+ "type" : "role",
+ "current_value" : role.description,
+ "col_width" : col_width
+ }
+%}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/forms/partials/input-role-name.html.twig b/templates/forms/partials/input-role-name.html.twig
new file mode 100644
index 0000000..022da9d
--- /dev/null
+++ b/templates/forms/partials/input-role-name.html.twig
@@ -0,0 +1,10 @@
+{% block input_role_name %}
+{% include "forms/partials/abstract/input-name.html.twig" with
+ {
+ "type" : "role",
+ "current_value" : role.name,
+ "placeholder" : translate('ROLE.NAME_EXPLAIN'),
+ "col_width" : col_width
+ }
+%}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/forms/partials/input-role-slug.html.twig b/templates/forms/partials/input-role-slug.html.twig
new file mode 100644
index 0000000..c56a9f6
--- /dev/null
+++ b/templates/forms/partials/input-role-slug.html.twig
@@ -0,0 +1,9 @@
+{% block input_role_slug %}
+{% include "forms/partials/abstract/input-slug.html.twig" with
+ {
+ "type" : "role",
+ "current_value" : role.slug,
+ "col_width" : col_width
+ }
+%}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/forms/partials/input-user-name.html.twig b/templates/forms/partials/input-user-name.html.twig
new file mode 100644
index 0000000..0b469df
--- /dev/null
+++ b/templates/forms/partials/input-user-name.html.twig
@@ -0,0 +1,10 @@
+{% block input_user_name %}
+{% if 'name' not in form.fields.hidden %}
+ {% if col_width %}
{% endif %}
+
+ {% include "forms/partials/input-user-first_name.html.twig" with { 'col_width': 'col-sm-6' } %}
+ {% include "forms/partials/input-user-last_name.html.twig" with { 'col_width': 'col-sm-6' } %}
+
+ {% if col_width %}
{% endif %}
+{% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/forms/role.html.twig b/templates/forms/role.html.twig
new file mode 100644
index 0000000..b3cb00e
--- /dev/null
+++ b/templates/forms/role.html.twig
@@ -0,0 +1,26 @@
+
+
+