diff --git a/sites/all/modules/contrib/admin/fpa/.gitignore b/sites/all/modules/contrib/admin/fpa/.gitignore
new file mode 100644
index 00000000..203f52d4
--- /dev/null
+++ b/sites/all/modules/contrib/admin/fpa/.gitignore
@@ -0,0 +1,3 @@
+.project
+.settings
+/.idea
diff --git a/sites/all/modules/contrib/admin/fpa/css/fpa.css b/sites/all/modules/contrib/admin/fpa/css/fpa.css
index 02ad3f1f..8d67dbef 100644
--- a/sites/all/modules/contrib/admin/fpa/css/fpa.css
+++ b/sites/all/modules/contrib/admin/fpa/css/fpa.css
@@ -1,95 +1 @@
-#user-admin-permissions {
- background-repeat: no-repeat;
- background-position: center -18px;
- background-image: url('data:image/gif;base64,R0lGODlhDwAoAMQAAAAAAAhzvQx7vRh7xhyExjWQykaczlqi02uv2Iu/4bW1taDO57XW7729vb3e78bGxs7OztbW1t7e3sri89bn997v9+fn5+fv9+f3/+/v7+/3//f39/f3//f//////wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBgAAACwAAAAADwAoAAAFxiAgApkUWduoAhukvC+UqlujNFD0vI2nuo8ZyRYZZW5C0VExkyiKK8DOIoooJFGAFQtwQldTJXK1FAKFGSLNpsjtbj6aC/ZMqkone3bP7/v/gIGCfhQJCAsVexUGAY2NBxorFQIBAgQBl5gcKowFFQkBCROXCCMUlRceoAkeE42RAAsBCB6qobWMDCIIt7asHrwJIqC0HgwHDLgBugCnAqm1ta4BsACdFdETA7Mqk40GCIyaKxfijgjVKxQLh4mD7/Dx8vIhAAAh+QQFBgAAACwAABQADQANAAAFRyAgAtOSANqoriI3XgQQFEchFunqcUQwvCpPImBwiC6rikxpc5wQooNKulgApIALwjVyTGQskcAzinVZ5qsNsA5HkSvkaRUCACH5BAUGAAAALAAAFAANAA0AAAU/ICAC11iNY4mKKnCKxsqtoudNwjgBweg5hZ7AIzqwjAECDSVI0BAoVYEFyK0cvFZhJ+KmRtOYSGNazlZe8igEACH5BAUGAAAALAAAFAANAA0AAAVIICACVIKMKFClooYKAIwYIsGmtkjFqOdNgIALcAI4EsiEjXFKiJyBaACARDEO2MNydwMChqLVTQUDGIoAAmd0YSHAo91JLAoBACH5BAUGAAAALAAAFAANAA0AAAVJICAC05IA2qiuIjdeBBAUx1ikbH7lgCPuqgvi4vEUeieEqCZIeGqLBaAG2AUChKNjIlM5CgGAwDOKjRyClXl6BLR5U2AQcFqFAAAh+QQFBgAAACwAABQADQANAAAFQCAgAtdYjWOJiipwigZQjNy6DuMEBLYueKIDqgaI2UQJ4AohqvAqShJAMEoEDI5dq6DjEHio7UpjOhJROhF5FAIAIfkEBQYAAAAsAAAUAA0ADQAABUUgIAIUgIwoUKWihgoAjBgiwaa2WMJ34JoiR8KBYpwSIuToqEwCEh4AjVEaJQLQSQtVuSYmAy6PEMgROKMLC/FDlU6rUQgAIfkEBQYAAAAsAAAUAA0ADQAABUYgIAITkADaqK4iN14EEBTHWKRsfuWAI+6qCwJX6J0QohoSUFssmL2AACdylAKAQmDhGQm6osHM4/GtYtlDMcsbHYBBEysEACH5BAUGAAAALAAAFAANAA0AAAVBICAC11iNY4mKKnCKBlCM3LoO4wQEti54ooPoVAPEUJwEzwYj8BCjGUkUMFCAAMeuRZhgdagWbKQxMYsoMKA8CgEAIfkEBQYAAAAsAAAUAA0ADQAABUkgIAJUgowoUKWihgoAjBgiwaa2SMU3EGgJAOFAPDBQjFNgKQoCTs5CYppwiGiMneDi8YwmrVFh1avAAIZTjTO6sBCu1O5EFoUAACH5BAUGAAAALAAAFAANAA0AAAVIICAC05IA2qiuIjdeBBAUx1ikY8yKlxwYlJGDtwAYCIEAAlcAOE4IzgmAENWKgIPHU4mqHBPZDiDw5L4sXbYJYI+zvVVvqgoBACH5BAUGAAAALAAAFAANAA0AAAVBICAC11iNY4mKKnCixcitQSwO48QWwSLqAo/o4HEABCPDyuM5rFAIFkAjspWQK2OgVdD9VlyAUkQVvVazlXeKCgEAOw==');
- height: 18px;
-}
-
-#user-admin-permissions.show {
- background-image: none;
- height: auto;
-}
-
-#user-admin-permissions > div {
- display: none;
-}
-
-#user-admin-permissions.show > div {
- display:block;
-}
-
-#user-admin-permissions.ctools-use-modal-processed .compact-link {
- display:none;
-}
-
-#user-admin-permissions .fpa-left-section {
- background-color: #F6F6F6;
- border: 1px solid #D6DBDE;
- float: left;
- margin-right: -1px;
- text-align: center;
- width: 150px;
-}
-
-#user-admin-permissions .fpa-left-section ul {
- list-style: none;
- margin:0;
- padding: 0;
- text-align: left;
-}
-
-#user-admin-permissions .fpa-left-section ul li {
- background: #EFEFEF;
- border-bottom: 1px solid #D6DBDE;
- cursor: pointer;
- font-size: 90%;
- line-height: 100%;
- margin: 0;
- overflow: hidden;
- padding: 0;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-#user-admin-permissions .fpa-left-section ul li.active {
- background: white;
- margin-right: -1px;
- position: relative;
- width: 151px;
-}
-
-#user-admin-permissions .fpa-left-section ul li div {
- padding: 0.5em;
-}
-
-#user-admin-permissions .fpa-left-section ul li div:after {
- content: ".";
- display: block;
- height: 0;
- clear: both;
- visibility: hidden;
-}
-
-#user-admin-permissions .fpa-left-section .form-submit {
- margin: 1em auto;
-}
-
-#user-admin-permissions .fpa-right-section {
- margin-left: 151px;
- border: 1px solid #D6DBDE;
-}
-
-#user-admin-permissions .fpa-right-section .fpa-filter-form .form-item {
- display: inline-block;
- padding: 3px 10px;
- vertical-align: top;
-}
-
-#user-admin-permissions .fpa-right-section .fpa-filter-form .clear-search {
- cursor: pointer;
- display: inline-block;
-}
-
-#user-admin-permissions .fpa-right-section .form-submit {
- margin-left: 0.5em;
-}
+body.page-admin-people-permissions{overflow-y:scroll}#user-admin-permissions.ctools-use-modal-processed .compact-link{display:none}.fpa-container tr.admin-menu-tweak-permissions-processed{cursor:default}.fpa-container .form-submit{margin:1em}.fpa-container .dummy-checkbox{vertical-align:middle}.fpa-container{width:100%}.fpa-container tr.element-hidden{display:table-row}.fpa-left-section,.fpa-right-section{display:table-cell;border:1px solid #d6dbde;vertical-align:top}.fpa-left-section{background-color:#f6f6f6;width:1px}.fpa-left-section .item-list ul{background-color:#efefef;list-style:none;margin:0;padding:0;text-align:left}.fpa-left-section .item-list li{background-color:inherit;border-bottom:1px solid #d6dbde;cursor:pointer;font-size:90%;line-height:100%;margin:0;padding:0;list-style:none;white-space:nowrap;position:relative}.fpa-left-section .item-list li>div{display:table;width:100%}.fpa-left-section .item-list li a,.fpa-left-section .item-list li .fpa-perm-total{display:table-cell;padding:.5em}.fpa-left-section .item-list li .fpa-perm-total{min-width:6em}.fpa-left-section .item-list li a{text-decoration:none;color:inherit}.fpa-left-section .item-list li .fpa-perm-total{text-align:right}.fpa-right-section{width:100%;background-color:#fff;border-left-width:0}.fpa-filter-form .form-item{display:inline-block;padding:3px 10px;vertical-align:top;float:none}.fpa-filter-form .form-checkboxes .form-type-checkbox,.fpa-filter-form .form-radios .form-type-radio{display:block}.fpa-filter-form .clear-search{cursor:pointer;display:inline-block}.fpa-right-section .fpa-table-wrapper{padding:10px}.fpa-table-wrapper .module-filter-inputs-wrapper .form-item-module-filter-name{display:none}div.ctools-modal-content .fpa-container .form-item label{float:none;width:auto}.fpa-container .block,.fpa-container .item-list,.fpa-container .help-items ul,.fpa-container .form-item,.fpa-container .confirmation ul,.fpa-container .admin-list,.fpa-container .node-type-list,.fpa-container .admin-panel{border-width:0}.fpa-left-section li[fpa-module]{counter-reset:fpa-module-permissions}.fpa-perm-counter{display:inline;counter-increment:fpa-module-permissions}.fpa-perm-total:before{content:counter(fpa-module-permissions)}.fpa-perm-total:after{content:" of " attr(fpa-total)}.fpa-toggle-container{padding:.5em}.fpa-toggle-container a{margin:.5em}.fpa-hide-descriptions table#permissions .description{display:none}.fpa-table-wrapper tr:before{white-space:nowrap}.fpa-hide-system-names .fpa-table-wrapper tr:before{display:none}.fpa-table-wrapper thead tr:before{content:"System Name";display:table-cell;border-bottom:3px solid #ccc;padding-right:1em;text-align:left;vertical-align:middle;padding:8px 10px;border:0;color:#000;text-transform:uppercase;background:#e1e2dc;font-weight:normal;border-width:1px;border-style:solid;border-color:#bebfb9}.fpa-table-wrapper tbody tr:before{content:attr(fpa-system-name);display:table-cell;vertical-align:middle;padding:8px 10px;border:0;color:#000;text-align:left}.fpa-table-wrapper tbody tr.fpa-module-row:before{font-weight:bold}.fpa-table-wrapper tbody tr.fpa-permission-row:before{padding-left:1.5em}.fpa-table-wrapper th.checkbox{vertical-align:bottom}.fpa-table-wrapper th input[type="checkbox"]{display:block;margin:.5em auto}.fpa-hide-descriptions a[fpa-toggle-class="fpa-hide-descriptions"]:before{content:attr(show)}a[fpa-toggle-class="fpa-hide-descriptions"]:before{content:attr(hide)}.fpa-hide-system-names a[fpa-toggle-class="fpa-hide-system-names"]:before{content:attr(show)}a[fpa-toggle-class="fpa-hide-system-names"]:before{content:attr(hide)}#user-admin-permissions .form-submit.fpa-clear-search{margin:0}.fpa-mobile input[type="checkbox"]{width:4em;height:4em}.fpa-permission-container{display:table;width:100%}.fpa-row-toggle-container{display:table-cell;vertical-align:middle;width:2em;text-align:center}td.checkbox,.fpa-row-toggle-container{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#permissions td{text-align:center}#permissions td.permission,#permissions td.module{text-align:left}.fpa-table-wrapper td.checkbox input[type="checkbox"]{border-collapse:separate}.fpa-table-wrapper td.checkbox input[type="checkbox"]:not([checked]):checked{box-shadow:0 0 10px 6px rgba(0,255,0,0.5)}.fpa-table-wrapper td.checkbox input[type="checkbox"][checked]:not(:checked){box-shadow:0 0 10px 6px rgba(255,0,0,0.5)}.fpa-checkboxes-toggle{visibility:hidden}html.js .fpa-checkboxes-toggle{visibility:inherit}.fpa-filter-form{display:none}html.js .fpa-filter-form{display:block}#permissions{display:none}.fpa-table-wrapper{background-repeat:no-repeat;background-position:center -18px;background-image:url('data:image/gif;base64,R0lGODlhDwAoAMQAAAAAAAhzvQx7vRh7xhyExjWQykaczlqi02uv2Iu/4bW1taDO57XW7729vb3e78bGxs7OztbW1t7e3sri89bn997v9+fn5+fv9+f3/+/v7+/3//f39/f3//f//////wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBgAAACwAAAAADwAoAAAFxiAgApkUWduoAhukvC+UqlujNFD0vI2nuo8ZyRYZZW5C0VExkyiKK8DOIoooJFGAFQtwQldTJXK1FAKFGSLNpsjtbj6aC/ZMqkone3bP7/v/gIGCfhQJCAsVexUGAY2NBxorFQIBAgQBl5gcKowFFQkBCROXCCMUlRceoAkeE42RAAsBCB6qobWMDCIIt7asHrwJIqC0HgwHDLgBugCnAqm1ta4BsACdFdETA7Mqk40GCIyaKxfijgjVKxQLh4mD7/Dx8vIhAAAh+QQFBgAAACwAABQADQANAAAFRyAgAtOSANqoriI3XgQQFEchFunqcUQwvCpPImBwiC6rikxpc5wQooNKulgApIALwjVyTGQskcAzinVZ5qsNsA5HkSvkaRUCACH5BAUGAAAALAAAFAANAA0AAAU/ICAC11iNY4mKKnCKxsqtoudNwjgBweg5hZ7AIzqwjAECDSVI0BAoVYEFyK0cvFZhJ+KmRtOYSGNazlZe8igEACH5BAUGAAAALAAAFAANAA0AAAVIICACVIKMKFClooYKAIwYIsGmtkjFqOdNgIALcAI4EsiEjXFKiJyBaACARDEO2MNydwMChqLVTQUDGIoAAmd0YSHAo91JLAoBACH5BAUGAAAALAAAFAANAA0AAAVJICAC05IA2qiuIjdeBBAUx1ikbH7lgCPuqgvi4vEUeieEqCZIeGqLBaAG2AUChKNjIlM5CgGAwDOKjRyClXl6BLR5U2AQcFqFAAAh+QQFBgAAACwAABQADQANAAAFQCAgAtdYjWOJiipwigZQjNy6DuMEBLYueKIDqgaI2UQJ4AohqvAqShJAMEoEDI5dq6DjEHio7UpjOhJROhF5FAIAIfkEBQYAAAAsAAAUAA0ADQAABUUgIAIUgIwoUKWihgoAjBgiwaa2WMJ34JoiR8KBYpwSIuToqEwCEh4AjVEaJQLQSQtVuSYmAy6PEMgROKMLC/FDlU6rUQgAIfkEBQYAAAAsAAAUAA0ADQAABUYgIAITkADaqK4iN14EEBTHWKRsfuWAI+6qCwJX6J0QohoSUFssmL2AACdylAKAQmDhGQm6osHM4/GtYtlDMcsbHYBBEysEACH5BAUGAAAALAAAFAANAA0AAAVBICAC11iNY4mKKnCKBlCM3LoO4wQEti54ooPoVAPEUJwEzwYj8BCjGUkUMFCAAMeuRZhgdagWbKQxMYsoMKA8CgEAIfkEBQYAAAAsAAAUAA0ADQAABUkgIAJUgowoUKWihgoAjBgiwaa2SMU3EGgJAOFAPDBQjFNgKQoCTs5CYppwiGiMneDi8YwmrVFh1avAAIZTjTO6sBCu1O5EFoUAACH5BAUGAAAALAAAFAANAA0AAAVIICAC05IA2qiuIjdeBBAUx1ikY8yKlxwYlJGDtwAYCIEAAlcAOE4IzgmAENWKgIPHU4mqHBPZDiDw5L4sXbYJYI+zvVVvqgoBACH5BAUGAAAALAAAFAANAA0AAAVBICAC11iNY4mKKnCixcitQSwO48QWwSLqAo/o4HEABCPDyuM5rFAIFkAjspWQK2OgVdD9VlyAUkQVvVazlXeKCgEAOw==');min-height:18px}.fpa-role-styles,.fpa-perm-styles{display:none}.fpa-wrapper{display:table}.dummy-checkbox{display:none}.fpa-authenticated-role-behavior input[type="checkbox"]{display:none}.fpa-authenticated-role-behavior input[type="checkbox"].rid-1,.fpa-authenticated-role-behavior input[type="checkbox"].rid-2,.fpa-authenticated-role-behavior input[type="checkbox"].dummy-checkbox{display:inline-block}
\ No newline at end of file
diff --git a/sites/all/modules/contrib/admin/fpa/css/fpa.css.less b/sites/all/modules/contrib/admin/fpa/css/fpa.css.less
new file mode 100644
index 00000000..84f2d209
--- /dev/null
+++ b/sites/all/modules/contrib/admin/fpa/css/fpa.css.less
@@ -0,0 +1,389 @@
+/**
+ * Prevent page width change when scroll is required.
+ * Helps reduce screen redraws.
+ */
+body.page-admin-people-permissions {
+ overflow-y: scroll;
+}
+
+#user-admin-permissions.ctools-use-modal-processed .compact-link {
+ display:none;
+}
+
+/* Disable the hand tool styling that admin_menu adds for module rows. */
+.fpa-container tr.admin-menu-tweak-permissions-processed {
+ cursor: default;
+}
+
+.fpa-container .form-submit {
+ margin: 1em;
+}
+
+/**
+ * Prevent vertical jitter of dummy checkboxes.
+ */
+.fpa-container .dummy-checkbox {
+ vertical-align: middle;
+}
+
+.fpa-container {
+ width: 100%;
+}
+
+/**
+ * Override admin_menu's "Collapse module groups on the Permissions page" functionality.
+ */
+.fpa-container tr.element-hidden {
+ display: table-row;
+}
+
+.fpa-left-section,
+.fpa-right-section {
+ display: table-cell;
+ border: 1px solid #D6DBDE;
+ vertical-align: top;
+}
+
+.fpa-left-section {
+ background-color: #F6F6F6;
+ width: 1px; /* Keep column as small as possible. Table cells automatically push wider. */
+}
+
+.fpa-left-section .item-list ul {
+ background-color: #EFEFEF;
+ list-style: none;
+ margin:0;
+ padding: 0;
+ text-align: left;
+}
+
+.fpa-left-section .item-list li {
+ background-color: inherit;
+ border-bottom: 1px solid #D6DBDE;
+ cursor: pointer;
+ font-size: 90%;
+ line-height: 100%;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ white-space: nowrap;
+ position: relative; /* Fix for IE disappearing bg with negative margin */
+}
+
+.fpa-left-section .item-list li > div {
+ display: table;
+ width: 100%;
+}
+
+.fpa-left-section .item-list li a,
+.fpa-left-section .item-list li .fpa-perm-total {
+ display: table-cell;
+ padding: 0.5em;
+}
+
+.fpa-left-section .item-list li .fpa-perm-total {
+ min-width: 6em;
+}
+
+.fpa-left-section .item-list li a {
+ text-decoration: none;
+ color: inherit;
+}
+
+.fpa-left-section .item-list li .fpa-perm-total {
+ text-align: right;
+}
+
+.fpa-right-section {
+ width: 100%;
+ background-color: white;
+ border-left-width: 0;
+}
+
+.fpa-filter-form {
+ .form-item {
+ display: inline-block;
+ padding: 3px 10px;
+ vertical-align: top;
+ float: none;
+ }
+
+ .form-checkboxes .form-type-checkbox,
+ .form-radios .form-type-radio {
+ display: block;
+ }
+}
+
+.fpa-filter-form .clear-search {
+ cursor: pointer;
+ display: inline-block;
+}
+
+.fpa-right-section .fpa-table-wrapper {
+ padding: 10px;
+}
+
+/**
+ * Module filter 2.x has a simple permissions filter. Hide this if FPA is enabled.
+ */
+.fpa-table-wrapper .module-filter-inputs-wrapper .form-item-module-filter-name {
+ display: none;
+}
+
+/**
+ * CTools attempts some styling to compact forms, looks bad with FPA.
+ */
+div.ctools-modal-content .fpa-container .form-item label {
+ float: none;
+ width: auto;
+}
+
+/*
+ * Rubik has some aggressive styling which makes FPA look poor, reset it here.
+ */
+.fpa-container .block,
+.fpa-container .item-list,
+.fpa-container .help-items ul,
+.fpa-container .form-item,
+.fpa-container .confirmation ul,
+.fpa-container .admin-list,
+.fpa-container .node-type-list,
+.fpa-container .admin-panel {
+ border-width: 0;
+}
+
+/**
+ * Styling for matching permissions counter.
+ */
+.fpa-left-section li[fpa-module] {
+ counter-reset: fpa-module-permissions;
+}
+
+.fpa-perm-counter {
+ display: inline;
+ counter-increment: fpa-module-permissions;
+}
+
+.fpa-perm-total:before {
+ content: counter(fpa-module-permissions);
+}
+
+.fpa-perm-total:after {
+ content: " of " attr(fpa-total);
+}
+
+.fpa-toggle-container {
+ padding: 0.5em;
+}
+
+.fpa-toggle-container a {
+ margin: 0.5em;
+}
+
+.fpa-hide-descriptions table#permissions .description {
+ display: none;
+}
+
+.fpa-table-wrapper tr:before {
+ white-space: nowrap;
+
+ .fpa-hide-system-names & {
+ display: none;
+ }
+}
+
+// Need to have this to now interfere with admin_menu.
+.fpa-table-wrapper thead tr:before {
+ content: "System Name";
+ display: table-cell;
+
+ border-bottom: 3px solid #ccc;
+ padding-right: 1em;
+ text-align: left;
+
+ vertical-align: middle;
+ padding: 8px 10px;
+ border: 0;
+ color: #000;
+
+ text-transform: uppercase;
+ background: #e1e2dc;
+ font-weight: normal;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #bebfb9;
+}
+
+.fpa-table-wrapper tbody tr {
+ &:before {
+ content: attr(fpa-system-name);
+ display: table-cell;
+
+ vertical-align: middle;
+ padding: 8px 10px;
+ border: 0;
+ color: #000;
+
+ text-align: left;
+ }
+
+ &.fpa-module-row:before {
+ font-weight: bold;
+ }
+
+ &.fpa-permission-row:before {
+ padding-left: 1.5em;
+ }
+}
+
+/**
+ * Styling for injected checkboxes.
+ */
+
+.fpa-table-wrapper th.checkbox {
+ vertical-align: bottom;
+}
+
+.fpa-table-wrapper th input[type="checkbox"] {
+ display: block;
+ margin: 0.5em auto;
+}
+
+.fpa-hide-descriptions a[fpa-toggle-class="fpa-hide-descriptions"]:before {
+ content: attr(show);
+}
+
+a[fpa-toggle-class="fpa-hide-descriptions"]:before {
+ content: attr(hide);
+}
+
+
+.fpa-hide-system-names a[fpa-toggle-class="fpa-hide-system-names"]:before {
+ content: attr(show);
+}
+
+a[fpa-toggle-class="fpa-hide-system-names"]:before {
+ content: attr(hide);
+}
+
+#user-admin-permissions .form-submit.fpa-clear-search {
+ margin: 0;
+}
+
+.fpa-mobile input[type="checkbox"] {
+ width: 4em;
+ height: 4em;
+}
+
+.fpa-permission-container {
+ display: table;
+ width: 100%;
+}
+
+.fpa-row-toggle-container {
+ display: table-cell;
+ vertical-align: middle;
+ width: 2em;
+ text-align: center;
+}
+
+td.checkbox,
+.fpa-row-toggle-container {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+#permissions td {
+ text-align: center;
+}
+
+#permissions td.permission,
+#permissions td.module {
+ text-align: left;
+}
+
+/**
+ * [checked] is HTML attribute. ':checked' selector is based on DOM property.
+ *
+ * IE: Both :checked and box-shadow are only supported on IE9+, so <=IE8 will not see this feature at all.
+ */
+
+.fpa-table-wrapper td.checkbox input[type="checkbox"] {
+ border-collapse: separate;
+
+ // [checked] = state of page load.
+ // :checked = current state.
+ &:not([checked]):checked {
+ box-shadow: 0px 0px 10px 6px rgba(0, 255, 0, 0.5);
+ }
+
+ &[checked]:not(:checked) {
+ box-shadow: 0px 0px 10px 6px rgba(255, 0, 0, 0.5);
+ }
+}
+
+/**
+ * Hide aspects of FPA that depend on JS.
+ */
+.fpa-checkboxes-toggle {
+ visibility: hidden;
+}
+
+html.js .fpa-checkboxes-toggle {
+ visibility: inherit;
+}
+
+.fpa-filter-form {
+ display: none;
+}
+
+html.js .fpa-filter-form {
+ display: block;
+}
+
+
+
+/**
+ * Tweak how the table is initially displayed to prevent premature reflow.
+ *
+ * The table is displayed with a tag immediately after in the HTML.
+ */
+#permissions {
+ display: none;
+}
+
+.fpa-table-wrapper {
+ background-repeat: no-repeat;
+ background-position: center -18px;
+ background-image: url('data:image/gif;base64,R0lGODlhDwAoAMQAAAAAAAhzvQx7vRh7xhyExjWQykaczlqi02uv2Iu/4bW1taDO57XW7729vb3e78bGxs7OztbW1t7e3sri89bn997v9+fn5+fv9+f3/+/v7+/3//f39/f3//f//////wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBgAAACwAAAAADwAoAAAFxiAgApkUWduoAhukvC+UqlujNFD0vI2nuo8ZyRYZZW5C0VExkyiKK8DOIoooJFGAFQtwQldTJXK1FAKFGSLNpsjtbj6aC/ZMqkone3bP7/v/gIGCfhQJCAsVexUGAY2NBxorFQIBAgQBl5gcKowFFQkBCROXCCMUlRceoAkeE42RAAsBCB6qobWMDCIIt7asHrwJIqC0HgwHDLgBugCnAqm1ta4BsACdFdETA7Mqk40GCIyaKxfijgjVKxQLh4mD7/Dx8vIhAAAh+QQFBgAAACwAABQADQANAAAFRyAgAtOSANqoriI3XgQQFEchFunqcUQwvCpPImBwiC6rikxpc5wQooNKulgApIALwjVyTGQskcAzinVZ5qsNsA5HkSvkaRUCACH5BAUGAAAALAAAFAANAA0AAAU/ICAC11iNY4mKKnCKxsqtoudNwjgBweg5hZ7AIzqwjAECDSVI0BAoVYEFyK0cvFZhJ+KmRtOYSGNazlZe8igEACH5BAUGAAAALAAAFAANAA0AAAVIICACVIKMKFClooYKAIwYIsGmtkjFqOdNgIALcAI4EsiEjXFKiJyBaACARDEO2MNydwMChqLVTQUDGIoAAmd0YSHAo91JLAoBACH5BAUGAAAALAAAFAANAA0AAAVJICAC05IA2qiuIjdeBBAUx1ikbH7lgCPuqgvi4vEUeieEqCZIeGqLBaAG2AUChKNjIlM5CgGAwDOKjRyClXl6BLR5U2AQcFqFAAAh+QQFBgAAACwAABQADQANAAAFQCAgAtdYjWOJiipwigZQjNy6DuMEBLYueKIDqgaI2UQJ4AohqvAqShJAMEoEDI5dq6DjEHio7UpjOhJROhF5FAIAIfkEBQYAAAAsAAAUAA0ADQAABUUgIAIUgIwoUKWihgoAjBgiwaa2WMJ34JoiR8KBYpwSIuToqEwCEh4AjVEaJQLQSQtVuSYmAy6PEMgROKMLC/FDlU6rUQgAIfkEBQYAAAAsAAAUAA0ADQAABUYgIAITkADaqK4iN14EEBTHWKRsfuWAI+6qCwJX6J0QohoSUFssmL2AACdylAKAQmDhGQm6osHM4/GtYtlDMcsbHYBBEysEACH5BAUGAAAALAAAFAANAA0AAAVBICAC11iNY4mKKnCKBlCM3LoO4wQEti54ooPoVAPEUJwEzwYj8BCjGUkUMFCAAMeuRZhgdagWbKQxMYsoMKA8CgEAIfkEBQYAAAAsAAAUAA0ADQAABUkgIAJUgowoUKWihgoAjBgiwaa2SMU3EGgJAOFAPDBQjFNgKQoCTs5CYppwiGiMneDi8YwmrVFh1avAAIZTjTO6sBCu1O5EFoUAACH5BAUGAAAALAAAFAANAA0AAAVIICAC05IA2qiuIjdeBBAUx1ikY8yKlxwYlJGDtwAYCIEAAlcAOE4IzgmAENWKgIPHU4mqHBPZDiDw5L4sXbYJYI+zvVVvqgoBACH5BAUGAAAALAAAFAANAA0AAAVBICAC11iNY4mKKnCixcitQSwO48QWwSLqAo/o4HEABCPDyuM5rFAIFkAjspWQK2OgVdD9VlyAUkQVvVazlXeKCgEAOw==');
+ min-height: 18px;
+}
+
+
+
+.fpa-role-styles,
+.fpa-perm-styles {
+ display: none;
+}
+
+.fpa-wrapper {
+ display: table;// Need this to go full width.
+}
+
+.dummy-checkbox {
+ display: none;
+}
+
+.fpa-authenticated-role-behavior {
+ input[type="checkbox"] {
+ display: none;
+
+ &.rid-1, &.rid-2, &.dummy-checkbox {
+ display: inline-block;
+ }
+ }
+}
\ No newline at end of file
diff --git a/sites/all/modules/contrib/admin/fpa/fpa.form_alter.inc b/sites/all/modules/contrib/admin/fpa/fpa.form_alter.inc
new file mode 100644
index 00000000..7a76c6ef
--- /dev/null
+++ b/sites/all/modules/contrib/admin/fpa/fpa.form_alter.inc
@@ -0,0 +1,66 @@
+type . ' content', $form, array('#group' => 'additional_settings'));
+ }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function fpa_form_user_admin_permissions_alter(&$form, &$form_state) {
+
+ // Prevent duplication of memory limit warning.
+ if (module_exists('filter_perms') && empty($form_state['input'])) {
+
+ $memory_limit = ini_get('memory_limit');
+
+ if (!drupal_check_memory_limit(_fpa_memory_required() . 'b', $memory_limit)) {
+ drupal_set_message(t('If you attempt to display all roles and permissions on this page at the same time, you will most likely exceed your PHP memory limit of %memory_limit.', array('%memory_limit' => $memory_limit)), 'warning');
+ }
+ }
+
+ $form['#theme'] = array('fpa_user_admin_permissions');
+
+ $fpa_module_path = drupal_get_path('module', 'fpa');
+
+ $form['#attached']['library'][] = array('system', 'jquery.cookie');
+
+ $form['#attached']['css'][] = $fpa_module_path . '/css/fpa.css';
+ $form['#attached']['js'][] = $fpa_module_path . '/js/fpa.min.js';
+ $form['#attached']['js'][] = array(
+ 'type' => 'setting',
+ 'data' => array(
+ 'fpa' => array(
+ 'attr' => array(
+ 'permission' => FPA_ATTR_PERMISSION,
+ 'module' => FPA_ATTR_MODULE,
+ 'role' => FPA_ATTR_ROLE,
+
+ 'checked' => FPA_ATTR_CHECKED,
+ 'not_checked' => FPA_ATTR_NOT_CHECKED,
+
+ 'system_name' => FPA_ATTR_SYSTEM_NAME,
+ ),
+ ),
+ ),
+ );
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function fpa_form_filter_perms_admin_perm_filter_alter(&$form, &$form_state) {
+
+ $form['#submit'][] = '_fpa_reset_filter_defaults';
+}
diff --git a/sites/all/modules/contrib/admin/fpa/fpa.info b/sites/all/modules/contrib/admin/fpa/fpa.info
index 98b88f4c..818218a0 100644
--- a/sites/all/modules/contrib/admin/fpa/fpa.info
+++ b/sites/all/modules/contrib/admin/fpa/fpa.info
@@ -1,10 +1,10 @@
name = "Fast Permissions Administration"
-description = "Provides quick access to a content type's or field's permissions."
+description = "Fast filtering on permissions administration form."
core = 7.x
package = Administration
-; Information added by drupal.org packaging script on 2012-04-22
-version = "7.x-2.2"
+; Information added by Drupal.org packaging script on 2015-05-21
+version = "7.x-2.6+2-dev"
core = "7.x"
project = "fpa"
-datestamp = "1335136578"
+datestamp = "1432171983"
diff --git a/sites/all/modules/contrib/admin/fpa/fpa.install b/sites/all/modules/contrib/admin/fpa/fpa.install
new file mode 100644
index 00000000..63b36262
--- /dev/null
+++ b/sites/all/modules/contrib/admin/fpa/fpa.install
@@ -0,0 +1,46 @@
+ REQUIREMENT_WARNING,
+ 'title' => t('Insufficient memory for permissions page'),
+ 'value' => t('~@permissions_memory_requiredM of memory required', array('@permissions_memory_required' => (int) ($permissions_memory_required / 1024 / 1024))),
+ 'description' => t('It is likely that you will exceed your memory limit when viewing the permissions page for all roles and permissions. If you are unable to load the permissions page, this is most likely the cause.') . '
',
+ );
+
+ $filter_perms_link = l('Filter Permissions', 'https://drupal.org/project/filter_perms');
+
+ if (!module_exists('filter_perms')) {
+
+ $requirements['fpa_memory']['description'] .= t('The !filter_perms_link module can work with Fast Permissions Administration by reducing the amount of the permissions form that is rendered and thereby reducing the memory required on the permissions page.', array('!filter_perms_link' => $filter_perms_link)) . '
';
+
+ if ($php_ini_path = get_cfg_var('cfg_file_path')) {
+ $requirements['fpa_memory']['description'] .= t('Increase the memory limit by editing the memory_limit parameter in the file %configuration-file and then restart your web server (or contact your system administrator or hosting provider for assistance).', array('%configuration-file' => $php_ini_path)) . '
';
+ }
+ }
+ else {
+
+ $requirements['fpa_memory']['severity'] = REQUIREMENT_OK;
+ $requirements['fpa_memory']['description'] = t('The !filter_perms_link module installed should prevent a memory issue as long as viewed permissions and roles are limited.', array('!filter_perms_link' => $filter_perms_link));
+ }
+ }
+ }
+
+ return $requirements;
+}
diff --git a/sites/all/modules/contrib/admin/fpa/fpa.module b/sites/all/modules/contrib/admin/fpa/fpa.module
index 3e9bccc6..d44bdb64 100644
--- a/sites/all/modules/contrib/admin/fpa/fpa.module
+++ b/sites/all/modules/contrib/admin/fpa/fpa.module
@@ -1,5 +1,26 @@
'Permissions',
'page callback' => '_fpa_ctools',
'page arguments' => array(1),
- 'access callback' => TRUE,
+ 'access arguments' => array('administer permissions'),
'type' => MENU_CALLBACK,
);
return $items;
}
+/**
+ * Implements hook_admin_paths().
+ */
function fpa_admin_paths() {
return array(
- 'fpa_modal/*/permissions' => FALSE,
+ 'fpa_modal/*/permissions' => TRUE,
);
}
-function fpa_js_load($arg) {
- if (module_exists('ctools')) {
- return ctools_js_load($arg);
+/**
+ * Implements hook_help().
+ */
+function fpa_help($path, $arg) {
+ if ($path == 'admin/people/permissions') {
+
+ $output = '';
+
+ $output .= '
' . t('Permissions and Module names will match on the readable or system name. The system name is provided as a togglable column.') . '
'; + + $output .= '' . t('Enter in the format of "permission@module", e.g. "admin@system" will show only permissions with the text "admin" in modules with the text "system".') . '
'; + + return $output; } - return 0; +} + +/** + * Loader function to wrap ctools loader function. + */ +function fpa_js_load($arg) { + $return_code = 0; + + if (module_exists('ctools')) { + $return_code = ctools_js_load($arg); + } + + return $return_code; } function fpa_l($permission = '', $text = NULL, $options = array()) { - // available modal modules in order of priority + // Available modal module system names in order of priority. $supported_modules = array( - //'dialog', // dialog api is buggy 'ctools', - 'modalframe', ); if (is_null($text)) { $text = t('Manage Permissions'); } - // gets the most prefered modal method from the available methods + // Find the most preferred modal method from the available methods. $modules = array_intersect($supported_modules, array_keys(module_list())); $method = array_shift($modules); $path = 'fpa_modal/nojs/permissions'; + + $query = array( + 'fpa_perm' => $permission, + 'fpa_method' => $method, + ); + $attributes = array(); - $query = array('fpa_perm' => $permission, 'fpa_method' => $method); switch ($method) { - case 'dialog': - dialog_add_js(); - $attributes['class'] = 'ctools-use-dialog'; - break; case 'ctools': ctools_include('ajax'); ctools_include('modal'); ctools_modal_add_js(); $attributes['class'] = 'ctools-use-modal'; break; - case 'modalframe': - modalframe_parent_js(); - drupal_add_js(drupal_get_path('module', 'fpa') . '/js/fpa.js'); - $attributes['class'] = 'fpa_modalframe'; - break; default: $path = 'admin/people/permissions'; $attributes['target'] = '_blank'; @@ -76,7 +116,7 @@ function fpa_l($permission = '', $text = NULL, $options = array()) { return l($text, $path, $options); } -function fpa_fieldset($permission, &$parent_item, $group = array()) { +function fpa_fieldset($filter_string, &$parent_item, $group = array()) { $parent_item['fpa_fieldset'] = array( '#type' => 'fieldset', @@ -90,39 +130,23 @@ function fpa_fieldset($permission, &$parent_item, $group = array()) { $parent_item['fpa_fieldset']['fpa'] = array( '#type' => 'markup', - '#markup' => fpa_l($permission), + '#markup' => fpa_l($filter_string), ); } -function fpa_form_node_type_form_alter(&$form, &$form_state) { - if (!empty($form['type']['#default_value']) && user_access('administer permissions')) { - fpa_fieldset($form['#node_type']->type . ':', $form, array('#group' => 'additional_settings')); - } -} - -function fpa_form_user_admin_permissions_alter(&$form, &$form_state) { - if (isset($_GET['fpa_method']) && $_GET['fpa_method'] == 'modalframe') { - modalframe_child_js(); - $form['#submit'][] = '_fpa_modalframe_submit'; - } - $fpa_module_path = drupal_get_path('module', 'fpa'); - drupal_add_css($fpa_module_path .'/css/fpa.css'); - drupal_add_js($fpa_module_path .'/js/fpa.js'); - drupal_add_js(array('fpa' => array('perm' => isset($_GET['fpa_perm']) && $_GET['fpa_perm'] ? check_plain($_GET['fpa_perm']) : '')), array('type' => 'setting')); -} - -function _fpa_modalframe_submit() { - modalframe_close_dialog(); -} - +/** + * Page callback for CTools modal path. + */ function _fpa_ctools($js = NULL) { // Need to include the file that contains the permissions form. module_load_include('inc', 'user', 'user.admin'); + $form_id = 'user_admin_permissions'; + // Fall back if $js is not set. if (!$js) { - return drupal_get_form('user_admin_permissions'); + return drupal_get_form($form_id); } ctools_include('modal'); @@ -133,11 +157,8 @@ function _fpa_ctools($js = NULL) { ); switch ($_GET['fpa_method']) { - case 'dialog': - $output = dialog_get_form('user_admin_permissions', $form_state); - break; case 'ctools': - $output = ctools_modal_form_wrapper('user_admin_permissions', $form_state); + $output = ctools_modal_form_wrapper($form_id, $form_state); break; } @@ -149,3 +170,37 @@ function _fpa_ctools($js = NULL) { echo ajax_render($output); exit; } + +/** + * @return int Approximate number of bytes of ram required to render the permissions form. + */ +function _fpa_memory_required() { + + $permissions_count = count(module_invoke_all('permission')); + + $user_roles_count = count(user_roles()); + + + $page_ram_required = (9 * 1024 * 1024); + + + // Takes ~26kb per row without any checkboxes. + $permission_row_overhead = 27261.028783658; + + $permissions_ram_required = $permissions_count * $permission_row_overhead; + + + // Determined by checking peak ram on permissions page, over several different number of visible roles. + $bytes_per_checkbox = 18924.508820799; + + $checkboxes_ram_required = $permissions_count * $user_roles_count * $bytes_per_checkbox; + + + return (int) ($page_ram_required + $permissions_ram_required + $checkboxes_ram_required); +} + +function _fpa_reset_filter_defaults() { + setcookie("fpa_module_match", "", time() - 3600, '/'); + setcookie("fpa_filter", "", time() - 3600, '/'); + setcookie("fpa_roles", "", time() - 3600, '/'); +} diff --git a/sites/all/modules/contrib/admin/fpa/fpa.theme.inc b/sites/all/modules/contrib/admin/fpa/fpa.theme.inc new file mode 100644 index 00000000..4bdbe6a0 --- /dev/null +++ b/sites/all/modules/contrib/admin/fpa/fpa.theme.inc @@ -0,0 +1,769 @@ + array( + 'render element' => 'form', + 'file' => 'fpa.theme.inc', + ), + ); +} + +/** + * Theme function to pre-mark rows with FPA attributes. + * + * Based on Drupal Core's permissions form theme function. + * + * @see theme_user_admin_permissions(). + */ +function theme_fpa_user_admin_permissions($variables) { + $form = $variables['form']; + + $nameless_checkbox = array( + '#type' => 'html_tag', + '#tag' => 'input', + '#attributes' => array( + 'type' => 'checkbox', + 'class' => array( + 'rid-1', // Prevents Drupal core Drupal.behaviors.permissions.toggle from applying. + 'form-checkbox', + 'fpa-checkboxes-toggle', + ), + ), + ); + + $nameless_checkbox_output = drupal_render($nameless_checkbox); + + + $dummy_checkbox = array( + '#type' => 'html_tag', + '#tag' => 'input', + '#attributes' => array( + 'type' => 'checkbox', + 'disabled' => 'disabled', + 'checked' => 'checked', + 'title' => t('This permission is inherited from the authenticated user role.'), + 'class' => array( + 'dummy-checkbox', + ), + ), + ); + + $dummy_checkbox_output = drupal_render($dummy_checkbox); + + + $permission_col_template = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'fpa-permission-container', + ), + ), + 'description' => array( + '#markup' => '', + ), + 'checkbox_cell' => array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'fpa-row-toggle-container', + ), + ), + 'checkbox_form_item' => array( + '#type' => 'container', + '#attributes' => array( + 'title' => t('Toggle visible checkboxes in this row.'), + 'class' => array( + 'form-item', + 'form-type-checkbox', + ), + ), + 'label' => array( + '#type' => 'html_tag', + '#tag' => 'label', + '#attributes' => array( + 'class' => array( + 'element-invisible', + ), + ), + '#value' => 'test', + ), + 'checkbox' => array( + '#markup' => $nameless_checkbox_output, + ), + ), + ), + ); + + $checkboxes_children = element_children($form['checkboxes']); + + // Prepare role names processed by drupal_html_class() ahead of time. + $roles_attr_values = array(); + + foreach ($checkboxes_children as $rid) { + $roles_attr_values[$rid] = drupal_html_class($form['role_names'][$rid]['#markup']); + } + + $first_role_index = array_shift($checkboxes_children); + + // Lists for wrapper. + $modules = array(); + $user_roles = array(); + + // Index of current module row. + $module = NULL; + + // Row counter. + $i = 0; + + $rows = array(); + + // Iterate over rows in form table. + foreach (element_children($form['permission']) as $key) { + + // Row template. + $row = array( + 'data' => array(), // Array of table cells. + 'title' => array(), // HTML attribute on table row tag. + FPA_ATTR_MODULE => array(), // HTML attribute on table row tag. + FPA_ATTR_PERMISSION => array(), // HTML attribute on table row tag. + FPA_ATTR_CHECKED => array(), + FPA_ATTR_NOT_CHECKED => array(), + ); + + // Determine if row is module or permission. + if (is_numeric($key)) { + // Module row. + + $row['class'][] = 'fpa-module-row'; + + // Mark current row with escaped module name. + $row[FPA_ATTR_MODULE] = array( + // System name + 0 => $form['permission'][$key]['#id'], + // Readable name + 1 => strip_tags($form['permission'][$key]['#markup']), + ); + + // Readable + $row['data'][] = array( + 'data' => drupal_render($form['permission'][$key]), + 'class' => array('module'), + 'id' => 'module-' . $form['permission'][$key]['#id'], + 'colspan' => count($form['role_names']['#value']) + 1, + ); + + $row['title'] = array($form['permission'][$key]['#id']); + + $row[FPA_ATTR_SYSTEM_NAME] = $row[FPA_ATTR_MODULE][0]; + + $row[FPA_ATTR_MODULE] = array_unique(array_map('drupal_html_class', $row[FPA_ATTR_MODULE])); + + // Add modules to left-side modules list. + $modules[$row[FPA_ATTR_MODULE][0]] = array( + 'text' => strip_tags($form['permission'][$key]['#markup']), + 'title' => array($form['permission'][$key]['#id']), + FPA_ATTR_MODULE => $row[FPA_ATTR_MODULE], + FPA_ATTR_PERMISSION => array(), + ); + + // Save row number for current module. + $module = $i; + } + else { + // Permission row. + + $row['class'][] = 'fpa-permission-row'; + + $permission_system_name = ''; + + // Might be empty if no modules are displayed in Permissions Filter module. + if (!empty($form['checkboxes'][$first_role_index])) { + $permission_system_name = $form['checkboxes'][$first_role_index][$key]['#return_value']; + } + + $label = $permission_col_template; + + $label['description']['#markup'] = drupal_render($form['permission'][$key]); + + // Permissions filter might cause no Roles to display. + if (count(element_children($form['checkboxes'])) == 0) { + unset($label['checkbox_cell']); + } + + // Readable + $row['data'][] = array( + 'data' => drupal_render($label), + 'class' => array('permission'), + ); + + foreach (element_children($form['checkboxes']) as $rid) { + $form['checkboxes'][$rid][$key]['#title'] = $form['role_names'][$rid]['#markup'] . ': ' . $form['permission'][$key]['#markup']; + $form['checkboxes'][$rid][$key]['#title_display'] = 'invisible'; + + // Filter permissions strips role id class from checkbox. Used by Drupal core functionality. + $form['checkboxes'][$rid][$key]['#attributes']['class'][] = 'rid-' . $rid; + + // Set authenticated role behavior class on page load. + if ($rid == 2 && $form['checkboxes'][$rid][$key]['#checked'] === TRUE) { + $row['class'][] = 'fpa-authenticated-role-behavior'; + } + + // For all roles that inherit permissions from 'authenticated user' role, add in dummy checkbox for authenticated role behavior. + if ($rid > 2) { + $form['checkboxes'][$rid][$key]['#suffix'] = $dummy_checkbox_output; // '#suffix' doesn't have wrapping HTML like '#field_suffix'. + } + + // Add rid's to row attribute for checked status filter. + if ($form['checkboxes'][$rid][$key]['#checked'] === TRUE) { + $row[FPA_ATTR_CHECKED][] = $rid; + } + else { + $row[FPA_ATTR_NOT_CHECKED][] = $rid; + } + + $row['data'][] = array( + 'data' => drupal_render($form['checkboxes'][$rid][$key]), + 'class' => array( + 'checkbox', + ), + 'title' => array( + $form['role_names'][$rid]['#markup'], + ), + // For role filter + FPA_ATTR_ROLE => array( + $rid, + ), + ); + } + + if (!empty($rid)) { + $row['title'] = array( + $form['checkboxes'][$rid][$key]['#return_value'], + ); + + $row[FPA_ATTR_SYSTEM_NAME] = array( + $form['checkboxes'][$rid][$key]['#return_value'], + ); + } + + // Mark current row with escaped permission name. + $row[FPA_ATTR_PERMISSION] = array( + // Permission system name. + 0 => $permission_system_name, + // Readable description. + 1 => strip_tags($form['permission'][$key]['#markup']), + ); + + // Mark current row with current module. + $row[FPA_ATTR_MODULE] = $rows[$module][FPA_ATTR_MODULE]; + + $row[FPA_ATTR_PERMISSION] = array_unique(array_map('drupal_html_class', $row[FPA_ATTR_PERMISSION])); + + // Add current permission to current module row. + $rows[$module][FPA_ATTR_PERMISSION] = array_merge($rows[$module][FPA_ATTR_PERMISSION], $row[FPA_ATTR_PERMISSION]); + + $rows[$module][FPA_ATTR_CHECKED] = array_unique(array_merge($rows[$module][FPA_ATTR_CHECKED], $row[FPA_ATTR_CHECKED])); + $rows[$module][FPA_ATTR_NOT_CHECKED] = array_unique(array_merge($rows[$module][FPA_ATTR_NOT_CHECKED], $row[FPA_ATTR_NOT_CHECKED])); + + $modules[$rows[$module][FPA_ATTR_MODULE][0]][FPA_ATTR_PERMISSION][] = $row[FPA_ATTR_PERMISSION]; + } + + $rows[$i++] = $row; + } + + $reset_button = array( + '#type' => 'html_tag', + '#tag' => 'input', + '#attributes' => array( + 'type' => 'reset', + 'class' => 'form-submit', + 'value' => t('Reset changes'), + ), + ); + + // If there is no submit button, don't add the reset button. + if (count(element_children($form['actions'])) > 0) { + + // Have the reset button appear before the submit button. + array_unshift($form['actions'], $reset_button); + } + + $actions_output = drupal_render_children($form['actions']); + + $header = array(); + + $header[] = array( + 'data' => t('Permission') . $actions_output, + ); + + foreach (element_children($form['role_names']) as $rid) { + + $header[] = array( + 'data' => drupal_render($form['role_names'][$rid]) . $nameless_checkbox_output, + 'class' => array( + 'checkbox', + ), + 'title' => array( + $form['role_names'][$rid]['#markup'], + ), + FPA_ATTR_ROLE => array( + $rid, + ), + ); + $user_roles[$rid] = $form['role_names'][$rid]['#markup']; + } + + $table = array( + 'header' => $header, + 'rows' => $rows, + ); + + $output = _fpa_wrapper($table, $modules, $user_roles, $actions_output); + + $output .= drupal_render_children($form); + + return $output; +} + +/** + * Wraps table output in the FPA filter. + */ +function _fpa_wrapper($permissions_table, $modules, $user_roles, $actions_output) { + + $same_page = trim(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_PATH), '/') == $_GET['q']; + + $render = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'fpa-container', + ), + ), + ); + + $hiders = array( + 'fpa-hide-descriptions' => array( + 'hide' => t('Hide descriptions'), + 'show' => t('Show descriptions'), + ), + 'fpa-hide-system-names' => array( + 'hide' => t('Hide system names'), + 'show' => t('Show system names'), + ), + ); + + $render['#attributes']['class'][] = 'fpa-hide-system-names'; + + $hide_container = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'fpa-toggle-container', + ), + ), + ); + + foreach ($hiders as $hide_class => $labels) { + + $hide_container[$hide_class] = array( + '#theme' => 'link', + '#text' => '', + '#path' => '', + '#options' => array( + 'attributes' => array_merge($labels, array( + 'fpa-toggle-class' => $hide_class, + )), + 'html' => TRUE, + 'fragment' => ' ', + 'external' => TRUE, // Prevent base path from being added to link. + ), + ); + } + + $render['hide_container'] = $hide_container; + + $wrapper = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'fpa-wrapper', + ), + ), + ); + + $render['wrapper'] = &$wrapper; + + /** + * block template. + */ + $style_template = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'style-wrapper-class-name', // Override on specific block. + ), + ), + ); + + $style_template['style'] = array( + '#type' => 'html_tag', + '#tag' => 'style', + '#attributes' => array( + 'type' => array( + 'text/css', + ), + ), + '#value' => '', // #value needed for closing tag. + ); + + /** + * block for role filtering. + */ + $wrapper['role_styles'] = $style_template; + $wrapper['role_styles']['#attributes']['class'][0] = 'fpa-role-styles'; + + /** + * block for permission filtering. + */ + $wrapper['perm_styles'] = $style_template; + $wrapper['perm_styles']['#attributes']['class'][0] = 'fpa-perm-styles'; + + /** + * Left section contains module list and form submission button. + */ + $left_section = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'fpa-left-section', + ), + ), + ); + + $wrapper['left_section'] = &$left_section; + + + /** + * Right section contains filter form and permissions table. + */ + $right_section = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'fpa-right-section', + ), + ), + ); + + $wrapper['right_section'] = &$right_section; + + $module_template = array( + FPA_ATTR_MODULE => array(), + FPA_ATTR_PERMISSION => array(), + 'data' => array( + '#type' => 'container', + '#attributes' => array(), + + 'link' => array( + '#type' => 'markup', + '#markup' => '', // l($module['text'], 'admin/people/permissions', $options) + ), + + 'counters' => array(), + + 'total' => array( + '#type' => 'html_tag', + '#tag' => 'span', + '#attributes' => array( + 'class' => array('fpa-perm-total'), + 'fpa-total' => 0, + ), + '#value' => '', // #value needed for closing tag. + ), + ), + ); + + $counter_template = array( + '#type' => 'html_tag', + '#tag' => 'span', + '#attributes' => array( + 'class' => array('fpa-perm-counter'), + FPA_ATTR_PERMISSION => array(), // Counters only count permissions match. + ), + '#value' => '', // #value required for closing tag. + ); + + $items = array(); + + $all_modules = array( + 'text' => t('All modules'), + FPA_ATTR_MODULE => array(), + FPA_ATTR_PERMISSION => array(), + ); + + array_unshift($modules, $all_modules); + + $all_modules_counters = array(); + + foreach ($modules as $module) { + + $module_item = $module_template; + + $module_item[FPA_ATTR_MODULE] = $module[FPA_ATTR_MODULE]; + $module_item[FPA_ATTR_PERMISSION] = array_reduce(array_pad($module[FPA_ATTR_PERMISSION], 1, array()), 'array_merge', array()); + + // Use link for accessibility and tabability. + $options = array( + 'fragment' => 'all', + ); + + if (!empty($module['title'])) { + $options['fragment'] = 'module-' . $module['title'][0]; + $options['attributes']['title'] = $module['title'][0]; + } + + $module_item['data']['link']['#markup'] = l($module['text'], 'admin/people/permissions', $options); + + foreach ($module[FPA_ATTR_PERMISSION] as $module_perm) { + + $counter_item = $counter_template; + + $counter_item['#attributes'][FPA_ATTR_PERMISSION] = $module_perm; + + $all_modules_counters[] = $counter_item; + + $module_item['data']['counters'][] = $counter_item; + } + + $module_item['data']['total']['#attributes']['fpa-total'] = count($module[FPA_ATTR_PERMISSION]); + + $items[] = $module_item; + } + + $items[0]['data']['counters'] = $all_modules_counters; + $items[0]['data']['total']['#attributes']['fpa-total'] = count($all_modules_counters); + + foreach ($items as &$item) { + $item['data'] = drupal_render($item['data']); + } + + $left_section['list'] = array( + '#items' => $items, + '#theme' => 'item_list', + ); + + $left_section['buttons'] = array( + '#type' => 'markup', + '#markup' => $actions_output, + ); + + $filter_form = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array( + 'fpa-filter-form', + ), + ), + ); + + $clear_button = array( + '#type' => 'html_tag', + '#tag' => 'input', + '#attributes' => array( + 'type' => array( + 'button', + ), + 'class' => array( + 'fpa-clear-search', + 'form-submit', + ), + 'value' => 'Clear filter', + ), + ); + + $default_filter = ''; + + if (!empty($_GET['fpa_perm'])) { + $default_filter = $_GET['fpa_perm']; + } + + if (!empty($_COOKIE['fpa_filter']) && $same_page) { + $default_filter = $_COOKIE['fpa_filter']; + } + + + $filter_form['permission_module_filter'] = array( + '#type' => 'textfield', + '#title' => t('Filter:'), + '#description' => t('Enter in the format of "permission@module",
e.g. admin@system will show only permissions with the
text "admin" in modules with the text "system".
This will also match on system name of a permission.
'), + '#size' => 25, + '#field_suffix' => drupal_render($clear_button), + '#attributes' => array( + 'placeholder' => array( + 'permission@module', + ), + 'autofocus' => 'autofocus', + ), + '#value' => $default_filter, + ); + + /* + * Populate the permission filter styles. + */ + $matches = array(); + + preg_match('/^\s*([^@]*)@?(.*?)\s*$/i', $filter_form['permission_module_filter']['#value'], $matches); + + array_shift($matches); // Remove whole match item. + + $safe_matches = array_map('drupal_html_class', $matches); + + $module_match = !empty($_COOKIE['module_match']) ? $_COOKIE['module_match'] : '*='; + + $filters = array( + drupal_strlen($safe_matches[0]) > 0 ? ('[' . FPA_ATTR_PERMISSION . '*="' . $safe_matches[0] . '"]') : '', + drupal_strlen($safe_matches[1]) > 0 ? ('[' . FPA_ATTR_MODULE . $module_match . '"' . $safe_matches[1] . '"]') : '', + ); + + $filter_styles = array( + '.fpa-table-wrapper tr[' . FPA_ATTR_MODULE . ']{display: none;}', + + '.fpa-table-wrapper tr[' . FPA_ATTR_MODULE . ']', + $filters[0], + $filters[1], + '{display: table-row;}', + + + '.fpa-perm-counter{display: none;}', + '.fpa-perm-counter', + $filters[0], + '{display: inline;}', + + + '.fpa-left-section li[' . FPA_ATTR_MODULE . ']', + drupal_strlen($filters[1]) > 0 ? $filters[1] : '[' . FPA_ATTR_MODULE . '=""]', + '{margin-right:-1px; background-color: white; border-right: solid 1px transparent;}', + ); + + $wrapper['perm_styles']['style']['#value'] = implode('', $filter_styles); + + + $cookie_roles = (!empty($_COOKIE['fpa_roles']) && $same_page) ? json_decode($_COOKIE['fpa_roles']) : array(); + + $options = array( + '*' => t('--All Roles'), + ); + + if (!empty($user_roles)) { + $options += $user_roles; // Preserves keys. + } + + if (in_array('*', $cookie_roles)) { + $cookie_roles = array('*'); + } + + $filter_form['role_filter'] = array( + '#type' => 'select', + '#title' => t('Roles:'), + '#description' => t('Select which roles to display.