diff --git a/.eslintrc.js b/.eslintrc.js index 9c3b37adfb8..fdcbea474f7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -31,6 +31,7 @@ module.exports = { curly: ['error', 'all'], eqeqeq: ['error', 'smart'], 'func-names': ['warn', 'as-needed'], + 'guard-for-in': 'warn', indent: ['error', 4, { VariableDeclarator: 'first', SwitchCase: 1 @@ -39,15 +40,19 @@ module.exports = { code: 140, ignoreComments: true }], + 'new-cap': 'warn', 'no-continue': 'warn', + 'no-global-assign': 'warn', 'no-new': 'warn', 'no-param-reassign': 'warn', - 'no-plusplus': ['error', { + 'no-plusplus': ['warn', { allowForLoopAfterthoughts: true }], + 'no-restricted-syntax': 'warn', 'no-underscore-dangle': 'warn', 'no-unused-vars': ['error', { args: 'none' }], 'no-use-before-define': ['error', 'nofunc'], + 'no-useless-escape': 'warn', 'object-shorthand': ['error', 'consistent'], 'one-var': ['error', 'consecutive'], 'prefer-arrow-callback': 'warn', diff --git a/_build/templates/default/sass/_forms.scss b/_build/templates/default/sass/_forms.scss index 77c9e97b32c..5324f929efa 100644 --- a/_build/templates/default/sass/_forms.scss +++ b/_build/templates/default/sass/_forms.scss @@ -76,7 +76,7 @@ textarea.x-form-field, border-radius: $borderRadius; border: 1px solid $borderColor; position: relative; - transition: border-color .25s; + transition: border-color 0.25s; } .x-viewport .x-trigger-wrap-focus, @@ -142,7 +142,7 @@ input::-moz-focus-inner { padding: 0 0 0 3px; top: 0; right: 0; - transition: all .25s; + transition: all 0.25s; width: 16px; height: 16px; @@ -150,7 +150,9 @@ input::-moz-focus-inner { @extend %pseudo-font; box-sizing: border-box; color: scale-color($coreFieldLabelColor, $lightness: 50%); - content: fa-content($fa-var-undo-alt); /* better match IMO for the action being taken */ + content: fa-content( + $fa-var-undo-alt + ); /* better match IMO for the action being taken */ font-size: 14px; position: relative; bottom: 2px; @@ -161,12 +163,12 @@ input::-moz-focus-inner { height: 16px; } &.modx-field-reset { - &::before { - content: fa-content($fa-var-undo-alt); - } - &:hover::before { - color: $green; - } + &::before { + content: fa-content($fa-var-undo-alt); + } + &:hover::before { + color: $green; + } } &.modx-field-clear { &::before { @@ -242,19 +244,21 @@ input::-moz-focus-inner { border-style: solid; border-width: 10px 10px 10px 0; border-color: transparent $lightGray transparent transparent; - content: ''; + content: ""; position: absolute; top: 0; left: -10px; - transform: rotate(360deg); /* for better anti-aliasing in webkit browsers */ + transform: rotate( + 360deg + ); /* for better anti-aliasing in webkit browsers */ width: 0; height: 0; } &:after { background-color: $white; - border-radius: 50%; /* make a circle */ - content: ''; + border-radius: 50%; /* make a circle */ + content: ""; position: absolute; top: 8px; left: -4px; @@ -276,7 +280,8 @@ input::-moz-focus-inner { background-color: darken($colorSplash, 6%); &:before { - border-color: transparent darken($colorSplash, 6%) transparent transparent; + border-color: transparent darken($colorSplash, 6%) transparent + transparent; } } } @@ -288,7 +293,7 @@ input::-moz-focus-inner { border: 1px solid $borderColor; border-radius: $borderRadius; padding: 5px; - transition: all .25s; + transition: all 0.25s; &:focus { border: 1px solid $borderColorFocus; @@ -332,7 +337,6 @@ input::-moz-focus-inner { } .x-window & { - .x-form-item-label { padding: 10px 0 4px 0; /* move the form fields a bit tighter together inside windows */ } @@ -352,7 +356,7 @@ input::-moz-focus-inner { &.disabled { label { - color: scale-color($coreFieldLabelColor, $lightness: 50%); + color: scale-color($coreFieldLabelColor, $lightness: 50%); } } @@ -374,8 +378,7 @@ input::-moz-focus-inner { /* prevent columns used inside form elements to have too much spacing, some custom TV types need this */ .x-column-inner > .x-column { - - ~.x-column { + ~ .x-column { margin-left: 5px; } @@ -409,7 +412,7 @@ input::-moz-focus-inner { } &.toggle-slider-above { - margin: .3em 0; + margin: 0.3em 0; padding-left: 3.9em; } @@ -421,15 +424,15 @@ input::-moz-focus-inner { .example-list { ul { - margin: .4em 0; + margin: 0.4em 0; li { position: relative; - margin-bottom: .25em; + margin-bottom: 0.25em; padding-left: 1.25em; &::before { @extend %pseudo-font; position: absolute; - left: .2em; + left: 0.2em; top: 0; content: fa-content($fa-var-angle-double-right); color: scale-color($mediumGray, $lightness: 20%); @@ -439,7 +442,7 @@ input::-moz-focus-inner { } .example-input, .copy-this { - padding: 0 .3em; + padding: 0 0.3em; border-radius: 2px; transition: width 1s; } @@ -477,7 +480,14 @@ input::-moz-focus-inner { } } } - } + &:active { + color: $darkGray; + &::after { + color: $darkGray; + } + } + } + .feedback { margin-left: 1.4rem; color: scale-color($blue, $lightness: -35%); @@ -500,13 +510,12 @@ input::-moz-focus-inner { .deemphasize { font-style: normal; } - } .fs-toggle { padding-top: 1em; margin-top: 2em; - margin-bottom: .5em; + margin-bottom: 0.5em; border-top: 1px dashed $borderColor; } @@ -565,7 +574,6 @@ input::-moz-focus-inner { } } } - } .x-form-field { @@ -635,7 +643,7 @@ input::-moz-focus-inner { transform: translate(-50%, -50%); text-align: center; width: 30px; - transition: opacity .25s; + transition: opacity 0.25s; } &.x-form-trigger-over, @@ -681,7 +689,6 @@ input::-moz-focus-inner { content: fa-content($fa-var-file-code); font-weight: 400; } - } &.x-datetime-wrap { @@ -815,7 +822,7 @@ input::-moz-focus-inner { padding-left: 3px; &:before { - content: ''; + content: ""; } } @@ -823,7 +830,7 @@ input::-moz-focus-inner { @extend %pseudo-font; box-sizing: border-box; - content: ''; + content: ""; font-size: 18px; padding-right: 3px; position: absolute; @@ -922,8 +929,7 @@ input::-moz-focus-inner { .x-form-check-wrap, .x-fieldset-checkbox-toggle legend, .x-fieldset legend { - [type="checkbox"]{ - + [type="checkbox"] { position: absolute; left: -9999px; html[dir="rtl"] & { @@ -931,11 +937,11 @@ input::-moz-focus-inner { left: unset; } - &+.x-form-cb-label, - &+.x-fieldset-header-text { + & + .x-form-cb-label, + & + .x-fieldset-header-text { position: relative; padding-left: 3.6em; - padding-top: .2em; + padding-top: 0.2em; margin-left: 0; cursor: pointer; box-sizing: border-box; @@ -943,9 +949,9 @@ input::-moz-focus-inner { &:before, &:after { - content: ''; + content: ""; position: absolute; - transition: all .2s ease; + transition: all 0.2s ease; font-size: inherit; } @@ -962,7 +968,7 @@ input::-moz-focus-inner { &:after { left: 0.1em; top: 0.8em; - margin-top: -.65em; + margin-top: -0.65em; height: 1.3em; width: 1.3em; border-radius: 50%; @@ -972,9 +978,8 @@ input::-moz-focus-inner { } &:checked { - - &+.x-form-cb-label, - &+.x-fieldset-header-text { + & + .x-form-cb-label, + & + .x-fieldset-header-text { &:after { left: 1.6em; top: 0.8em; @@ -988,9 +993,8 @@ input::-moz-focus-inner { } &.danger:checked { - - &+.x-form-cb-label, - &+.x-fieldset-header-text { + & + .x-form-cb-label, + & + .x-fieldset-header-text { &:before { background-color: $red; border-color: $red; @@ -999,9 +1003,8 @@ input::-moz-focus-inner { } &.warning:checked { - - &+.x-form-cb-label, - &+.x-fieldset-header-text { + & + .x-form-cb-label, + & + .x-fieldset-header-text { &:before { background-color: $orange; border-color: $orange; @@ -1033,12 +1036,11 @@ input::-moz-focus-inner { } /* applies to new xcheckboxgroup custom checkbox group */ &.aggregated-group { - padding-left: 1em; - padding-right: 1em; + padding-left: 1em; + padding-right: 1em; } } - /* superboxselect / multi-select field */ .x-superboxselect { height: auto !important; /* override the extjs default theme style of 18px */ @@ -1088,13 +1090,13 @@ input::-moz-focus-inner { cursor: pointer; display: inline-block; /*font-size: 1px;*/ outline: 0; /* fix firefox dotted outlines */ - opacity: .6; + opacity: 0.6; filter: alpha(opacity=60); /* for IE <= 8 */ padding: 0; position: absolute; top: 0; right: 0; - transition: opacity .25s; + transition: opacity 0.25s; width: 16px; height: 100%; @@ -1177,7 +1179,7 @@ input::-moz-focus-inner { margin-bottom: 2px; } - input[type=text], + input[type="text"], textarea { background-color: $coreFieldBg; background-image: none; @@ -1187,7 +1189,7 @@ input::-moz-focus-inner { width: 97%; } - input[type=text] { + input[type="text"] { font-size: 13px; height: 20px !important; padding: 5px; @@ -1225,21 +1227,22 @@ input::-moz-focus-inner { } .x-editor .x-form-check-wrap { - background-color: $white -} - -/* fix combo on grid editor bug */ -.x-grid-editor .x-form-field-wrap { - background: #f6f2f7 url($imgPath + 'modx-theme/form/combo-bck.png') repeat-x scroll 0 100%; -} - -.x-grid-editor .x-form-field-wrap input { - background-color: transparent !important; + background-color: $white; } -.x-grid-editor .x-form-field-wrap img { - background-color: $white; - background-image: url($imgPath + 'modx-theme/form/trigger.png'); +.x-grid-editor { + z-index: 9002 !important; + .x-form-field-wrap { + background: #f6f2f7 url($imgPath+"modx-theme/form/combo-bck.png") repeat-x + scroll 0 100%; + input { + background-color: transparent !important; + } + img { + background-color: $white; + background-image: url($imgPath+"modx-theme/form/trigger.png"); + } + } } .x-form-grow-sizer { @@ -1269,7 +1272,6 @@ input::-moz-focus-inner { .x-grid3 { .x-small-editor { - .x-form-text, .x-form-field-wrap { font: $fontSmall; @@ -1373,7 +1375,7 @@ input::-moz-focus-inner { .x-btn { padding: 1px; - transition: color .25s; + transition: color 0.25s; &.x-btn-over, &:hover, @@ -1388,7 +1390,7 @@ input::-moz-focus-inner { &.x-item-disabled { color: $buttonColor; - opacity: .4; + opacity: 0.4; } button:before { @@ -1432,7 +1434,11 @@ input::-moz-focus-inner { } /* the second text cell, "of X" */ - .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell { + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell { .xtb-text { display: inline-block; position: absolute; @@ -1444,7 +1450,15 @@ input::-moz-focus-inner { } /* the last regular button >>, yes, I know it's ugly but tell that Microsoft and say thanks for IE8 =) */ - .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell { + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell { .x-btn { margin-right: 0; } @@ -1453,13 +1467,13 @@ input::-moz-focus-inner { /* the refresh button */ .x-toolbar-cell:last-child { opacity: 0; - transition: opacity .25s; + transition: opacity 0.25s; .x-btn { font-size: 12px; line-height: 1; margin: 0; - opacity: .4; + opacity: 0.4; padding: 0; position: absolute; bottom: 2px; @@ -1501,7 +1515,7 @@ input::-moz-focus-inner { } .x-combo-list-hd { - background-image: url($imgPath + 'modx-theme/layout/panel-title-light-bg.gif'); + background-image: url($imgPath+"modx-theme/layout/panel-title-light-bg.gif"); border-bottom-color: #bcbcbc; color: #464646; } @@ -1548,18 +1562,18 @@ input::-moz-focus-inner { .x-date-mp-ybtn a.x-date-mp-prev, .x-date-mp-ybtn a.x-date-mp-next { display: inline-block; - opacity: .6; + opacity: 0.6; filter: alpha(opacity=60); /* for IE <= 8 */ margin: 0 auto; position: relative; - transition: opacity .25s; + transition: opacity 0.25s; &:before { @extend %pseudo-font; box-sizing: border-box; color: $colorSplash; - content: ''; + content: ""; font-size: 18px; position: absolute; top: 0; @@ -1788,7 +1802,7 @@ td.x-date-mp-sep { border-radius: $borderRadius; background-color: $coreFieldBg; border: 1px solid $borderColor; - background: url('../images/tp-no-preview.png') no-repeat center center; + background: url("../images/tp-no-preview.png") no-repeat center center; overflow: hidden; .x-panel-bwrap, @@ -1809,6 +1823,6 @@ td.x-date-mp-sep { bottom: 0; padding: 10px 20px; color: #fff; - background-color: rgba(0, 0, 0, .8); + background-color: rgba(0, 0, 0, 0.8); } } diff --git a/_build/templates/default/sass/_utility.scss b/_build/templates/default/sass/_utility.scss index 27c6b20ed18..c5d51c8c66f 100644 --- a/_build/templates/default/sass/_utility.scss +++ b/_build/templates/default/sass/_utility.scss @@ -145,7 +145,6 @@ } } - /* Instead of writing the same code for every nav bar */ @mixin navigation-list { list-style-type: none; @@ -212,3 +211,13 @@ } } } + +@mixin textLink { + color: $colorSplash; + text-decoration-style: dotted; + text-decoration-color: scale-color($colorSplash, $lightness: 50%); + &:hover { + color: $black; + border-bottom-color: scale-color($black, $lightness: 40%); + } +} diff --git a/_build/templates/default/sass/index.scss b/_build/templates/default/sass/index.scss index 67a987c5d60..574515ba63b 100644 --- a/_build/templates/default/sass/index.scss +++ b/_build/templates/default/sass/index.scss @@ -161,8 +161,8 @@ hr { } .wait { - background: transparent url($imgPath + "style/wait.gif") no-repeat scroll - center 55px; + background: transparent url($imgPath+"style/wait.gif") no-repeat scroll center + 55px; color: $darkestGray; font-size: 15px; font-weight: bold; @@ -443,7 +443,7 @@ textarea.x-form-field { bottom: 0; display: block; content: " "; - background: transparent url($imgPath + "restyle/dragndrop.svg") no-repeat + background: transparent url($imgPath+"restyle/dragndrop.svg") no-repeat center; background-size: 50% 50%; opacity: 0.1; @@ -476,7 +476,7 @@ textarea.x-form-field { } #modx-panel-packages.drag-n-drop:before { - background: transparent url($imgPath + "restyle/dragndrop.svg") no-repeat top; + background: transparent url($imgPath+"restyle/dragndrop.svg") no-repeat top; background-size: 50% 30%; z-index: 0; } @@ -541,6 +541,14 @@ textarea.x-form-field { } /* grids */ + +.modx-protected-row { + .x-grid3-cell-inner { + font-style: italic; + color: $colorSplash; + } +} + .x-small-editor .x-form-field { font-size: 12px !important; } @@ -553,14 +561,11 @@ textarea.x-form-field { color: #999 !important; } -a.x-grid-link { - color: $colorSplash; - text-decoration: underline; -} - -a.x-grid-link:hover, -a.x-grid-link:focus { - text-decoration: none; +.x-grid-link { + @include textLink; + &.simulated-link { + cursor: pointer; + } } .x-editable-column { @@ -910,6 +915,37 @@ a.x-grid-link:focus { } /* rowactions */ +.x-grid3-row { + &.disable-selection, + &.disable-selection.x-grid3-row-selected { + .x-grid3-row-checker { + position: relative; + &::before, + &::after { + color: $disabledTextColor; + } + &::before { + content: '\f0c8'; + } + &::after { + content: '\f715'; + font-size: 6px; + position: absolute; + left: 50%; + top: 50%; + margin-left: 2px; + margin-top: 1px; + transform: translate(-50%, -50%) rotate(98deg); + font-weight: 600; + font-family: 'Font Awesome 5 Free'; + } + &:hover { + cursor: default; + } + } + } +} + .ux-row-action-cell .x-grid3-cell-inner { padding: 1px 0 0 0; } @@ -923,8 +959,8 @@ a.x-grid-link:focus { } .ux-row-action-item span { - background: transparent url($imgPath + "style/go-next.png") no-repeat scroll - 1px 4px; + background: transparent url($imgPath+"style/go-next.png") no-repeat scroll 1px + 4px; display: inline !important; line-height: 24px; margin: 0 5px; @@ -933,22 +969,22 @@ a.x-grid-link:focus { } .icon-uninstall span { - background: url($imgPath + "style/delete.png") no-repeat scroll 1px 4px + background: url($imgPath+"style/delete.png") no-repeat scroll 1px 4px transparent; } .package-details span { - background: url($imgPath + "style/info.png") no-repeat scroll 1px 4px + background: url($imgPath+"style/info.png") no-repeat scroll 1px 4px transparent; } .package-download span { - background: url($imgPath + "style/download.png") no-repeat scroll 1px 4px + background: url($imgPath+"style/download.png") no-repeat scroll 1px 4px transparent; } .package-installed span { - background: url($imgPath + "style/accept.png") no-repeat scroll 1px 4px + background: url($imgPath+"style/accept.png") no-repeat scroll 1px 4px transparent; } @@ -1182,7 +1218,7 @@ a.x-grid-link:focus { } .x-rbtn td { - background-image: url($imgPath + "restyle/icons/rbtn.gif"); + background-image: url($imgPath+"restyle/icons/rbtn.gif"); background-repeat: no-repeat; border: 0 none; height: 21px; @@ -1418,38 +1454,36 @@ iframe[classname="x-hidden"] { /* file upload, is this the old legacy (single file) uploader? */ .ext-ux-uploaddialog-addbtn { - background: url($imgPath + "restyle/fileup/file-add.gif") no-repeat left - center !important; + background: url($imgPath+"restyle/fileup/file-add.gif") no-repeat left center !important; } .ext-ux-uploaddialog-removebtn { - background: url($imgPath + "restyle/fileup/file-remove.gif") no-repeat left + background: url($imgPath+"restyle/fileup/file-remove.gif") no-repeat left center !important; } .ext-ux-uploaddialog-resetbtn { - background: url($imgPath + "restyle/fileup/reset.gif") no-repeat left center !important; + background: url($imgPath+"restyle/fileup/reset.gif") no-repeat left center !important; } .ext-ux-uploaddialog-uploadstartbtn { - background: url($imgPath + "restyle/fileup/upload-start.gif") no-repeat left + background: url($imgPath+"restyle/fileup/upload-start.gif") no-repeat left center !important; } .ext-ux-uploaddialog-uploadstopbtn { - background: url($imgPath + "restyle/fileup/upload-stop.gif") no-repeat left + background: url($imgPath+"restyle/fileup/upload-stop.gif") no-repeat left center !important; } .ext-ux-uploaddialog-indicator-stoped { - background: url($imgPath + "restyle/fileup/done.gif") no-repeat center center; + background: url($imgPath+"restyle/fileup/done.gif") no-repeat center center; height: 16px; width: 16px; } .ext-ux-uploaddialog-indicator-processing { - background: url($imgPath + "restyle/fileup/loading.gif") no-repeat center - center; + background: url($imgPath+"restyle/fileup/loading.gif") no-repeat center center; height: 16px; width: 16px; } @@ -1461,19 +1495,19 @@ iframe[classname="x-hidden"] { } .ext-ux-uploaddialog-state-0 { - background-image: url($imgPath + "restyle/fileup/uncheck.gif"); + background-image: url($imgPath+"restyle/fileup/uncheck.gif"); } .ext-ux-uploaddialog-state-1 { - background-image: url($imgPath + "restyle/fileup/check.gif"); + background-image: url($imgPath+"restyle/fileup/check.gif"); } .ext-ux-uploaddialog-state-2 { - background-image: url($imgPath + "restyle/fileup/failed.gif"); + background-image: url($imgPath+"restyle/fileup/failed.gif"); } .ext-ux-uploaddialog-state-3 { - background-image: url($imgPath + "restyle/fileup/file-uploading.gif"); + background-image: url($imgPath+"restyle/fileup/file-uploading.gif"); } /* tree grid */ @@ -1883,6 +1917,12 @@ iframe[classname="x-hidden"] { user-select: text !important; } +.x-selectable { + &.simulated-link * { + @include textLink; + } +} + /* Lightbox */ #ux-lightbox { left: 0; @@ -1915,7 +1955,7 @@ iframe[classname="x-hidden"] { } #ux-lightbox-loading { - background: url($imgPath + "style/loading.gif") no-repeat scroll center 15% + background: url($imgPath+"style/loading.gif") no-repeat scroll center 15% transparent; height: 25%; left: 0; @@ -2004,7 +2044,7 @@ iframe[classname="x-hidden"] { } #ux-lightbox-data #ux-lightbox-navClose { - background: transparent url($imgPath + "style/close.png") no-repeat scroll 0 0; + background: transparent url($imgPath+"style/close.png") no-repeat scroll 0 0; float: right; height: 16px; outline: medium none; diff --git a/core/lexicon/en/context.inc.php b/core/lexicon/en/context.inc.php index 7352df973c2..84d25abc900 100644 --- a/core/lexicon/en/context.inc.php +++ b/core/lexicon/en/context.inc.php @@ -6,23 +6,31 @@ * @package modx * @subpackage lexicon */ +$_lang['_context_manager_description'] = 'The default manager or administration context for content management activity.'; +$_lang['_context_manager_name'] = 'Manager'; +$_lang['_context_website_description'] = 'The default front-end context for your website.'; +$_lang['_context_website_name'] = 'Website'; $_lang['context'] = 'Context'; $_lang['context_add'] = 'Add Context'; $_lang['context_data'] = 'Context Data'; +$_lang['context_edit'] = 'Edit the settings and User Group access for this Context'; $_lang['context_err_ae'] = 'A Context with that name already exists.'; $_lang['context_err_create'] = 'An error occurred while creating the Context.'; $_lang['context_err_duplicate'] = 'An error occurred while trying to duplicate the Context.'; $_lang['context_err_load_data'] = 'Error loading context data.'; +$_lang['context_err_name_reserved'] = 'The context name “[[+reservedName]]” is reserved. Please choose another name.'; $_lang['context_err_nf'] = 'Context not found!'; $_lang['context_err_nfs'] = 'Context not found with key: [[+key]]'; $_lang['context_err_ns'] = 'Context not specified.'; $_lang['context_err_ns_key'] = 'Please specify a valid key for the Context.'; +$_lang['context_err_ns_name'] = 'Please specify a valid name for the Context.'; $_lang['context_err_remove'] = 'An error occurred while trying to delete the Context.'; $_lang['context_err_reserved'] = 'The Context key you chose is reserved for system use only. Please specify a different key.'; $_lang['context_err_save'] = 'An error occurred while saving the Context.'; $_lang['context_id'] = 'Ctx ID'; $_lang['context_key'] = 'Context Key'; $_lang['context_management_message'] = 'Manage site Contexts.'; +$_lang['context_reserved_general_desc'] = 'Note that this is a protected, built-in Context. The values shown below are for informational purposes only. Its settings and assigned User Group(s) are, however, editable by users with the appropriate permissions.'; $_lang['context_settings'] = 'Context Settings'; $_lang['context_settings_desc'] = 'Here you can set settings specific to this Context. Context settings will override any System Settings with the same key. Each setting will be available via the [[++key]] placeholder.'; $_lang['context_with_key_not_found'] = 'Context with key %s not found!'; diff --git a/core/lexicon/en/dashboards.inc.php b/core/lexicon/en/dashboards.inc.php index f6cb29fd2d2..9b0f9277ecb 100644 --- a/core/lexicon/en/dashboards.inc.php +++ b/core/lexicon/en/dashboards.inc.php @@ -6,10 +6,14 @@ * @subpackage lexicon * @language en */ +$_lang['_dashboards_default_name'] = 'Default'; +$_lang['_dashboards_default_description'] = 'The built-in MODX dashboard'; + $_lang['dashboard'] = 'Dashboard'; $_lang['dashboard_desc_name'] = 'The name of the Dashboard.'; $_lang['dashboard_desc_description'] = 'A short description of the Dashboard.'; $_lang['dashboard_desc_hide_trees'] = 'Checking this will hide the left-hand trees when this Dashboard is rendered on the welcome page.'; +$_lang['dashboard_edit'] = 'Edit the settings and Widget placements for this Dashboard'; $_lang['dashboard_hide_trees'] = 'Hide Left-Hand Trees'; $_lang['dashboard_desc_customizable'] = 'Allow users to customize this dashboard for their accounts: create, delete and change position or size of widgets.'; $_lang['dashboard_customizable'] = 'Customizable'; diff --git a/core/lexicon/en/default.inc.php b/core/lexicon/en/default.inc.php index 58145332f7f..8f2386dee40 100644 --- a/core/lexicon/en/default.inc.php +++ b/core/lexicon/en/default.inc.php @@ -75,6 +75,7 @@ $_lang['confirm_delete_message'] = 'Are you sure you want to delete this message?'; $_lang['confirm_remove'] = 'Are you sure you want to delete this item?'; $_lang['confirm_remove_locks'] = 'Users sometimes close their browser while editing documents, templates, snippets or parsers, possibly leaving the item they were editing in locked state. By pressing OK you can delete ALL locks currently in place.

Proceed?'; +$_lang['confirm_remove_multiple'] = 'Are you sure you want to delete the selected items?'; $_lang['confirm_undelete'] = 'Any children documents deleted at the same time as this document will also be undeleted, but children documents deleted at an earlier time will still be deleted.'; $_lang['confirm_unpublish'] = 'Un-publishing this document now will delete any (un)publishing dates that may have been set. If you wish to set or keep publish or unpublish dates, please choose to edit the document instead.\n\nProceed?'; $_lang['console'] = 'Console'; @@ -92,6 +93,7 @@ $_lang['create_user_group'] = 'Create User Group'; $_lang['created'] = 'Created'; $_lang['createdon'] = 'Creation date'; +$_lang['creator'] = 'Creator'; $_lang['current'] = 'Current'; $_lang['dashboard'] = 'Dashboard'; $_lang['data_err_load'] = 'Error loading data.'; @@ -216,6 +218,8 @@ $_lang['general_information'] = 'General Information'; $_lang['general_settings'] = 'General Settings'; $_lang['go'] = 'Go'; +$_lang['grid_column_creator_header'] = $_lang['creator']; +$_lang['grid_column_creator_description'] = 'Indicates the entity that created the row’s data/setting (read-only)'; $_lang['group'] = 'Group'; $_lang['guid'] = 'GUID'; $_lang['handler'] = 'Handler'; diff --git a/core/lexicon/en/namespace.inc.php b/core/lexicon/en/namespace.inc.php index e569bf8120a..ec549d219d6 100644 --- a/core/lexicon/en/namespace.inc.php +++ b/core/lexicon/en/namespace.inc.php @@ -18,7 +18,7 @@ $_lang['namespace_name_desc'] = 'Specify a name for the Namespace here.'; $_lang['namespace_path'] = 'Core Path'; $_lang['namespace_path_desc'] = 'Specify an absolute path to the core for this Namespace here. You may use placeholders like {core_path}. Example: {core_path}components/democomponent/'; -$_lang['namespace_remove_confirm'] = 'Are you sure you want to delete "[[+name]]" namespace and all related content?'; +$_lang['namespace_remove_confirm'] = 'Are you sure you want to delete the "[[+name]]" namespace and all related content?'; $_lang['namespace_remove_multiple_confirm'] = 'Are you sure you want to delete these namespaces and all their related content?'; $_lang['namespaces'] = 'Namespaces'; $_lang['namespaces_desc'] = 'Namespaces are global identifiers for packages and components, registering their vehicles, lexicon entries and resources all together.'; diff --git a/core/lexicon/en/policy.inc.php b/core/lexicon/en/policy.inc.php index 6d35864b353..0415f255264 100644 --- a/core/lexicon/en/policy.inc.php +++ b/core/lexicon/en/policy.inc.php @@ -6,6 +6,89 @@ * @package modx * @subpackage lexicon */ + + // Reserved Policies +$_lang['_policy_administrator_description'] = 'Context administration policy with all permissions.'; +$_lang['_policy_administrator_name'] = 'Administrator'; + +$_lang['_policy_developer_description'] = 'Context administration policy with most Permissions except Administrator and Security functions.'; +$_lang['_policy_developer_name'] = 'Developer'; + +$_lang['_policy_content_editor_description'] = 'Context administration policy with limited, content-editing related Permissions, but no publishing.'; +$_lang['_policy_content_editor_name'] = 'Content Editor'; + +$_lang['_policy_context_description'] = 'A standard Context policy that you can apply when creating Context ACLs for basic read/write and view_unpublished access within a Context.'; +$_lang['_policy_context_name'] = 'Context'; + +$_lang['_policy_element_description'] = 'MODX Element policy with all attributes.'; +$_lang['_policy_element_name'] = 'Element'; + +$_lang['_policy_hidden_namespace_description'] = 'Hidden Namespace policy, will not show Namespace in lists.'; +$_lang['_policy_hidden_namespace_name'] = 'Hidden Namespace'; + +$_lang['_policy_load_list_view_description'] = 'Provides load, list and view permissions only.'; +$_lang['_policy_load_list_view_name'] = 'Load, List and View'; + +$_lang['_policy_load_only_description'] = 'A minimal policy with permission to load an object.'; +$_lang['_policy_load_only_name'] = 'Load Only'; + +$_lang['_policy_media_source_admin_description'] = 'Media Source administration policy.'; +$_lang['_policy_media_source_admin_name'] = 'Media Source Admin'; + +$_lang['_policy_media_source_user_description'] = 'Media Source user policy, with basic viewing and using - but no editing - of Media Sources.'; +$_lang['_policy_media_source_user_name'] = 'Media Source User'; + +$_lang['_policy_object_description'] = 'An Object policy with all permissions.'; +$_lang['_policy_object_name'] = 'Object'; + +$_lang['_policy_resource_description'] = 'MODX Resource Policy with all attributes.'; +$_lang['_policy_resource_name'] = 'Resource'; + +// Reserved Policy Templates +$_lang['_policytemplate_administrator_template_description'] = 'Context administration policy template with all permissions.'; +$_lang['_policytemplate_administrator_template_name'] = 'AdministratorTemplate'; + +$_lang['_policytemplate_context_template_description'] = 'Context Policy Template with all attributes.'; +$_lang['_policytemplate_context_template_name'] = 'ContextTemplate'; + +$_lang['_policytemplate_element_template_description'] = 'Element Policy Template with all attributes.'; +$_lang['_policytemplate_element_template_name'] = 'ElementTemplate'; + +$_lang['_policytemplate_media_source_template_description'] = 'Media Source Policy Template with all attributes.'; +$_lang['_policytemplate_media_source_template_name'] = 'MediaSourceTemplate'; + +$_lang['_policytemplate_namespace_template_description'] = 'Namespace Policy Template with all attributes.'; +$_lang['_policytemplate_namespace_template_name'] = 'NamespaceTemplate'; + +$_lang['_policytemplate_object_template_description'] = 'Object Policy Template with all attributes.'; +$_lang['_policytemplate_object_template_name'] = 'ObjectTemplate'; + +$_lang['_policytemplate_resource_template_description'] = 'Resource Policy Template with all attributes.'; +$_lang['_policytemplate_resource_template_name'] = 'ResourceTemplate'; + +// Reserved Template Groups +$_lang['_templategroup_administrator_description'] = 'All admin policy templates.'; +$_lang['_templategroup_administrator_name'] = 'Administrator'; + +$_lang['_templategroup_context_description'] = 'All Context based policy templates.'; +$_lang['_templategroup_context_name'] = 'Context'; + +$_lang['_templategroup_element_description'] = 'All Element-based policy templates.'; +$_lang['_templategroup_element_name'] = 'Element'; + +$_lang['_templategroup_mediasource_description'] = 'All Media Source-based policy templates.'; +$_lang['_templategroup_mediasource_name'] = 'Media Source'; + +$_lang['_templategroup_namespace_description'] = 'All Namespace based policy templates.'; +$_lang['_templategroup_namespace_name'] = 'Namespace'; + +$_lang['_templategroup_object_description'] = 'All Object-based policy templates.'; +$_lang['_templategroup_object_name'] = 'Object'; + +$_lang['_templategroup_resource_description'] = 'All Resource-based policy templates.'; +$_lang['_templategroup_resource_name'] = 'Resource'; + +// General $_lang['template_group'] = 'Template Group'; $_lang['active_of'] = '[[+active]] of [[+total]]'; $_lang['active_permissions'] = 'Active Permissions'; @@ -28,6 +111,7 @@ $_lang['policy_desc_template'] = 'The Policy Template used for this Policy. Policies get their Permission lists from their Template.'; $_lang['policy_desc_lexicon'] = 'Optional. The Lexicon Topic that this Policy uses to translate the Permissions it owns.'; $_lang['policy_duplicate_confirm'] = 'Are you sure you want to duplicate this policy and all of its data?'; +$_lang['policy_edit'] = 'Edit the permissions assigned to this Policy'; $_lang['policy_err_ae'] = 'A Policy already exists with the name `[[+name]]`. Please select another name.'; $_lang['policy_err_nf'] = 'Policy not found.'; $_lang['policy_err_ns'] = 'Policy not specified.'; @@ -47,6 +131,7 @@ $_lang['policy_template_desc'] = 'A Policy Template defines which Permissions will show up in the Permissions grid when editing a specific Policy. You can add or remove specific Permissions from this template below. Note that removing a Permission from a Template will remove it from any Policies that use this Template.'; $_lang['policy_template_desc_name'] = 'The name of the Access Policy Template'; $_lang['policy_template_desc_description'] = 'Optional. A short description of the Access Policy Template. Also you might use lexicon keys here.'; +$_lang['policy_template_edit'] = 'Edit the permissions assigned to this Policy Template'; $_lang['policy_template_lexicon'] = 'Lexicon Topic'; $_lang['policy_template_desc_lexicon'] = 'Optional. The Lexicon Topic that this Policy Template uses to translate the Permissions it owns.'; $_lang['policy_template_desc_template_group'] = 'The Policy Template Group to use. This is used when selecting Policies from a dropdown menu; usually they are filtered by template group. Select an appropriate group for your Policy Template.'; @@ -61,33 +146,53 @@ $_lang['policy_template_remove_confirm_in_use'] = 'Are you sure you want to delete this Policy Template? It will delete all Policies attached to this Template as well - this could break your MODX installation if any active Policies are attached to this Template.

This template is used by existing Policies ([[+count]] in total). Are you sure you want to delete this template and all attached policies?'; $_lang['policy_template_remove_multiple_confirm'] = 'Are you sure you want to delete these Policy Templates? It will delete all Policies attached to these Templates as well - this could break your MODX installation if any active Policies are attached to these Templates.'; $_lang['policy_template_remove_multiple_confirm_in_use'] = 'Are you sure you want to delete these Policy Templates? It will delete all Policies attached to these Templates as well - this could break your MODX installation if any active Policies are attached to these Templates.

Some of selected templates are still used by existing Policies ([[+count]] in total). Are you sure you want to delete these template and all attached policies?'; + +$_lang['policy_template_remove_multiple_confirm_in_use_ignoring_protected'] = 'In addition to the [[+count-templates]] Policy Templates you have selected, [[+count-policies]] Access Policies (attached to one or more of these Policy Templates) will be deleted. If any of these Access Policies are currently assigned to a permissions rule, you could break your MODX installation by removing them. (Note that the [[+protected]] protected Templates in your selection will not be removed.) +

+Are you sure you want to continue? +'; + + $_lang['policy_templates'] = 'Policy Templates'; $_lang['policy_templates.intro_msg'] = 'This is a list of Policy Templates which define lists of Permissions that are checked or unchecked in specific Policies.'; -$_lang['policy_template_administrator_desc'] = 'Context administration policy template with all permissions.'; -$_lang['policy_template_resource_desc'] = 'Resource Policy Template with all attributes.'; -$_lang['policy_template_object_desc'] = 'Object Policy Template with all attributes.'; -$_lang['policy_template_element_desc'] = 'Element Policy Template with all attributes.'; -$_lang['policy_template_mediasource_desc'] = 'Media Source Policy Template with all attributes.'; -$_lang['policy_template_context_desc'] = 'Context Policy Template with all attributes.'; -$_lang['policy_template_namespace_desc'] = 'Namespace Policy Template with all attributes.'; -$_lang['policy_template_group_administrator_desc'] = 'All admin policy templates.'; -$_lang['policy_template_group_object_desc'] = 'All Object-based policy templates.'; -$_lang['policy_template_group_resource_desc'] = 'All Resource-based policy templates.'; -$_lang['policy_template_group_element_desc'] = 'All Element-based policy templates.'; -$_lang['policy_template_group_mediasource_desc'] = 'All Media Source-based policy templates.'; -$_lang['policy_template_group_namespace_desc'] = 'All Namespace based policy templates.'; -$_lang['policy_template_group_context_desc'] = 'All Context based policy templates.'; -$_lang['policy_resource_desc'] = 'MODX Resource Policy with all attributes.'; -$_lang['policy_administrator_desc'] = 'Context administration policy with all permissions.'; -$_lang['policy_load_only_desc'] = 'A minimal policy with permission to load an object.'; -$_lang['policy_load_list_and_view_desc'] = 'Provides load, list and view permissions only.'; -$_lang['policy_object_desc'] = 'An Object policy with all permissions.'; -$_lang['policy_element_desc'] = 'MODX Element policy with all attributes.'; -$_lang['policy_content_editor_desc'] = 'Context administration policy with limited, content-editing related Permissions, but no publishing.'; -$_lang['policy_media_source_admin_desc'] = 'Media Source administration policy.'; -$_lang['policy_media_source_user_desc'] = 'Media Source user policy, with basic viewing and using - but no editing - of Media Sources.'; -$_lang['policy_developer_desc'] = 'Context administration policy with most Permissions except Administrator and Security functions.'; -$_lang['policy_context_desc'] = 'A standard Context policy that you can apply when creating Context ACLs for basic read/write and view_unpublished access within a Context.'; -$_lang['policy_hidden_namespace_desc'] = 'Hidden Namespace policy, will not show Namespace in lists.'; $_lang['policy_count'] = 'Policy Count'; $_lang['policy_query_name_only'] = 'Search by Name only'; + +// Deprecated keys, keep for BC until ... +$_lang['policy_administrator_desc'] = $_lang['_policy_administrator_description']; +$_lang['policy_context_desc'] = $_lang['_policy_context_description']; +$_lang['policy_content_editor_desc'] = $_lang['_policy_content_editor_description']; +$_lang['policy_developer_desc'] = $_lang['_policy_developer_description']; +$_lang['policy_element_desc'] = $_lang['_policy_element_description']; +$_lang['policy_hidden_namespace_desc'] = $_lang['_policy_hidden_namespace_description']; +$_lang['policy_load_list_and_view_desc'] = $_lang['_policy_load_list_view_description']; +$_lang['policy_load_only_desc'] = $_lang['_policy_load_only_description']; +$_lang['policy_media_source_admin_desc'] = $_lang['_policy_media_source_admin_description']; +$_lang['policy_media_source_user_desc'] = $_lang['_policy_media_source_user_description']; +$_lang['policy_object_desc'] = $_lang['_policy_object_description']; +$_lang['policy_resource_desc'] = $_lang['_policy_resource_description']; + +$_lang['policy_template_administrator_desc'] = $_lang['_policytemplate_administrator_template_description']; +$_lang['policy_template_context_desc'] = $_lang['_policytemplate_context_template_description']; +$_lang['policy_template_element_desc'] = $_lang['_policytemplate_element_template_description']; +$_lang['policy_template_mediasource_desc'] = $_lang['_policytemplate_media_source_template_description']; +$_lang['policy_template_namespace_desc'] = $_lang['_policytemplate_namespace_template_description']; +$_lang['policy_template_object_desc'] = $_lang['_policytemplate_object_template_description']; +$_lang['policy_template_resource_desc'] = $_lang['_policytemplate_resource_template_description']; + +// Temporary keys needed before future change of Policy Template names (adding spaces to allow translation) +$_lang['_policytemplate_administratortemplate_description'] = $_lang['_policytemplate_administrator_template_description']; +$_lang['_policytemplate_contexttemplate_description'] = $_lang['_policytemplate_context_template_description']; +$_lang['_policytemplate_elementtemplate_description'] = $_lang['_policytemplate_element_template_description']; +$_lang['_policytemplate_mediasourcetemplate_description'] = $_lang['_policytemplate_media_source_template_description']; +$_lang['_policytemplate_namespacetemplate_description'] = $_lang['_policytemplate_namespace_template_description']; +$_lang['_policytemplate_objecttemplate_description'] = $_lang['_policytemplate_object_template_description']; +$_lang['_policytemplate_resourcetemplate_description'] = $_lang['_policytemplate_resource_template_description']; + +$_lang['policy_template_group_administrator_desc'] = $_lang['_templategroup_administrator_description']; +$_lang['policy_template_group_context_desc'] = $_lang['_templategroup_context_description']; +$_lang['policy_template_group_element_desc'] = $_lang['_templategroup_element_description']; +$_lang['policy_template_group_mediasource_desc'] = $_lang['_templategroup_mediasource_description']; +$_lang['policy_template_group_namespace_desc'] = $_lang['_templategroup_namespace_description']; +$_lang['policy_template_group_object_desc'] = $_lang['_templategroup_object_description']; +$_lang['policy_template_group_resource_desc'] = $_lang['_templategroup_resource_description']; diff --git a/core/lexicon/en/source.inc.php b/core/lexicon/en/source.inc.php index 13a1f4932aa..8303bcccfab 100644 --- a/core/lexicon/en/source.inc.php +++ b/core/lexicon/en/source.inc.php @@ -6,6 +6,8 @@ * @package modx * @subpackage lexicon */ +$_lang['_source_filesystem_description'] = 'The default manager source containing all files this installation of MODX has access to.'; +$_lang['_source_filesystem_name'] = 'Filesystem'; $_lang['access'] = 'Access Permissions'; $_lang['base_path'] = 'Base Path'; $_lang['base_path_relative'] = 'Base Path Relative?'; @@ -20,7 +22,9 @@ $_lang['source_access_remove_confirm'] = 'Are you sure you want to delete Access to this Source for this User Group?'; $_lang['source_access_update'] = 'Edit Access'; $_lang['source_description_desc'] = 'A short description of the Media Source.'; +$_lang['source_edit'] = 'Edit the settings and User Group access for this Media Source'; $_lang['source_err_ae_name'] = 'A Media Source with that name already exists! Please specify a new name.'; +$_lang['source_err_name_reserved'] = 'The source name “[[+reservedName]]” is reserved. Please choose another name.'; $_lang['source_err_nf'] = 'Media Source not found!'; $_lang['source_err_init'] = 'Could not initialize "[[+source]]" Media Source!'; $_lang['source_err_nfs'] = 'No Media Source can be found with the id: [[+id]].'; @@ -30,6 +34,7 @@ $_lang['source_properties.intro_msg'] = 'Manage the properties for this Source below.'; $_lang['source_remove_confirm'] = 'Are you sure you want to delete this Media Source? This might break any TVs you have assigned to this source.'; $_lang['source_remove_multiple_confirm'] = 'Are you sure you want to delete these Media Sources? This might break any TVs you have assigned to these sources.'; +$_lang['source_reserved_general_desc'] = 'Note that this is a protected, built-in Media Source. The values shown below are for informational purposes only. Its properties and assigned User Group(s) are, however, editable by users with the appropriate permissions.'; $_lang['source_type'] = 'Source Type'; $_lang['source_type_desc'] = 'The type, or driver, of the Media Source. The Source will use this driver to connect to when gathering its data. For example: File System will grab files from the file system. S3 will get files from an S3 bucket.'; $_lang['source_type.file'] = 'File System'; diff --git a/core/lexicon/en/user.inc.php b/core/lexicon/en/user.inc.php index 984b0326fe0..d5d7f618c80 100644 --- a/core/lexicon/en/user.inc.php +++ b/core/lexicon/en/user.inc.php @@ -6,6 +6,11 @@ * @package modx * @subpackage lexicon */ +$_lang['_role_member_description'] = 'The lowest-authority role, usually a user of the site but not of the manager.'; +$_lang['_role_member_name'] = 'Member'; +$_lang['_role_super_user_description'] = 'The highest-authority role, for manager users with complete control over all aspects of the site.'; +$_lang['_role_super_user_name'] = 'Super User'; + $_lang['active'] = 'Active'; $_lang['address'] = 'Address'; $_lang['administrator'] = 'Administrator'; @@ -41,11 +46,12 @@ $_lang['role_err_ae'] = 'A role already exists with that name.'; $_lang['role_err_duplicate'] = 'An error occurred while duplicating the role.'; $_lang['role_err_has_users'] = 'There are users with this role. It cannot be deleted.'; +$_lang['role_err_name_reserved'] = 'The role name “[[+reservedName]]” is reserved. Please choose another name.'; $_lang['role_err_nf'] = 'Role not found.'; $_lang['role_err_nfs'] = 'Role not found with id: [[+role]]'; $_lang['role_err_ns'] = 'Role not specified!'; $_lang['role_err_ns_authority'] = 'Please specify an authority level for this role.'; -$_lang['role_err_ns_name'] = 'Please specify a name for the role.'; +$_lang['role_err_ns_name'] = 'Please specify a name for this role.'; $_lang['role_err_remove'] = 'An error occurred while trying to delete the role.'; $_lang['role_err_remove_admin'] = 'The role you are trying to delete is the admin role. This role cannot be deleted!'; $_lang['role_remove'] = 'Delete Role'; @@ -75,6 +81,7 @@ $_lang['user_country'] = 'Country'; $_lang['user_dob'] = 'Date of birth'; $_lang['user_doesnt_exist'] = 'User does not exist'; +$_lang['user_edit_account'] = 'Edit User’s Account'; $_lang['user_edit_self_msg'] = 'You may need to log out and log in again after saving to fully update your information.
Also, should you choose to generate a new password for yourself, it will be sent to you through email.'; $_lang['user_email'] = 'Email address'; $_lang['user_err_access_permissions_save'] = 'An error occurred while saving user access permissions.'; @@ -178,7 +185,7 @@ $_lang['user_remove_confirm'] = 'Are you sure you want to delete this user? This is irreversible!'; $_lang['user_remove_multiple_confirm'] = 'Are you sure you want to delete these users? This is irreversible!'; $_lang['user_remote_data_msg'] = 'Edit remote user data here.'; -$_lang['user_role_update'] = 'Edit User Role'; +$_lang['user_role_update'] = 'Change User’s Role'; $_lang['user_setting_err_remove'] = 'An error occurred while trying to delete user settings.'; $_lang['user_setting_err_save'] = 'An error occurred while saving user settings.'; $_lang['user_settings'] = 'User Settings'; diff --git a/core/src/Revolution/Processors/Context/Get.php b/core/src/Revolution/Processors/Context/Get.php index 624c8a77086..56f489e0af0 100644 --- a/core/src/Revolution/Processors/Context/Get.php +++ b/core/src/Revolution/Processors/Context/Get.php @@ -1,4 +1,5 @@ classKey::getCoreContexts(); + $contextKey = $this->object->get('key'); + if (in_array($contextKey, $coreContexts)) { + $contextData = $this->object->toArray(); + $reserved = $contextKey === 'mgr'; + $this->object->set('isProtected', true); + $this->object->set('reserved', $reserved); + $limitTo = []; + if ($contextKey === $this->classKey::CONTEXT_DEFAULT) { + if ($contextData['name'] === $this->classKey::CONTEXT_DEFAULT_NAME) { + $limitTo[] = 'name'; + } + if (empty($contextData['description'])) { + $limitTo[] = 'description'; + } + } + $this->modx->lexicon->setTranslatedCoreDescriptors($contextData, 'context', 'name', $limitTo); + foreach ($contextData as $key => $value) { + $this->object->set($key, $value); + } + } + } } diff --git a/core/src/Revolution/Processors/Context/GetList.php b/core/src/Revolution/Processors/Context/GetList.php index 56aafadbe55..b1ac4d2ed4b 100644 --- a/core/src/Revolution/Processors/Context/GetList.php +++ b/core/src/Revolution/Processors/Context/GetList.php @@ -1,12 +1,12 @@ setDefaultProperties([ - 'query' => '', - 'exclude' => '', + 'search' => '', + 'exclude' => 'creator' ]); + $this->isGridFilter = $this->getProperty('isGridFilter', false); + $this->canCreate = $this->modx->hasPermission('new_context'); $this->canEdit = $this->modx->hasPermission('edit_context'); $this->canRemove = $this->modx->hasPermission('delete_context'); - $this->isGridFilter = $this->getProperty('isGridFilter', false); + $this->coreContexts = $this->classKey::getCoreContexts(); + return $initialized; } @@ -76,7 +78,8 @@ public function prepareQueryBeforeCount(xPDOQuery $c) if (!empty($query)) { $c->where([ 'key:LIKE' => '%' . $query . '%', - 'OR:description:LIKE' => '%' . $query . '%', + 'OR:name:LIKE' => '%' . $query . '%', + 'OR:description:LIKE' => '%' . $query . '%' ]); } $exclude = $this->getProperty('exclude'); @@ -149,24 +152,43 @@ public function prepareQueryAfterCount(xPDOQuery $c) /** * {@inheritDoc} - * @param xPDOObject $object - * + * @param xPDOObject|modContext $object * @return array */ public function prepareRow(xPDOObject $object) { - $contextArray = $object->toArray(); - $contextArray['perm'] = []; - if ($this->canCreate) { - $contextArray['perm'][] = 'pnew'; - } - if ($this->canEdit) { - $contextArray['perm'][] = 'pedit'; + $permissions = [ + 'create' => $this->canCreate && $object->checkPolicy('save'), + 'duplicate' => $this->canCreate && $object->checkPolicy('copy'), + 'update' => $this->canEdit && $object->checkPolicy('save'), + 'delete' => $this->canRemove && $object->checkPolicy('remove') + ]; + + $contextData = $object->toArray(); + $contextKey = $contextData['key']; + $isCoreContext = $object->isCoreContext($contextKey); + + if ($isCoreContext) { + $limitTo = []; + if ($contextKey === $this->classKey::CONTEXT_DEFAULT) { + if ($contextData['name'] === $this->classKey::CONTEXT_DEFAULT_NAME) { + $limitTo[] = 'name'; + } + if (empty($contextData['description'])) { + $limitTo[] = 'description'; + } + } + $this->modx->lexicon->setTranslatedCoreDescriptors($contextData, 'context', 'name', $limitTo); } - if (!in_array($object->get('key'), $this->classKey::RESERVED_KEYS) && $this->canRemove) { - $contextArray['perm'][] = 'premove'; + + $contextData['reserved'] = ['key' => $this->coreContexts, 'name' => ['Manager']]; + $contextData['isProtected'] = $isCoreContext; + $contextData['creator'] = $isCoreContext ? 'modx' : strtolower($this->modx->lexicon('user')) ; + if ($isCoreContext) { + unset($permissions['delete']); } + $contextData['permissions'] = $permissions; - return $contextArray; + return $contextData; } } diff --git a/core/src/Revolution/Processors/Security/Access/Policy/GetList.php b/core/src/Revolution/Processors/Security/Access/Policy/GetList.php index 94c420f6b5b..2e1b584ceb8 100644 --- a/core/src/Revolution/Processors/Security/Access/Policy/GetList.php +++ b/core/src/Revolution/Processors/Security/Access/Policy/GetList.php @@ -44,6 +44,13 @@ class GetList extends GetListProcessor /** @param boolean $isGridFilter Indicates the target of this list data is a filter field */ protected $isGridFilter = false; + public $canCreate = false; + public $canEdit = false; + public $canEditTemplate = false; + public $canRemove = false; + protected $corePolicies; + protected $corePolicyTemplates; + // private $templatesTranslated = []; /** * @return bool @@ -56,8 +63,17 @@ public function initialize() 'group' => false, 'combo' => false, 'query' => '', + 'exclude' => 'creator' ]); $this->isGridFilter = $this->getProperty('isGridFilter', false); + + $this->canCreate = $this->modx->hasPermission('policy_new') && $this->modx->hasPermission('policy_save'); + $this->canEdit = $this->modx->hasPermission('policy_edit'); + $this->canEditTemplate = $this->modx->hasPermission('policy_template_edit'); + $this->canRemove = $this->modx->hasPermission('policy_delete'); + $this->corePolicies = $this->classKey::getCorePolicies(); + $this->corePolicyTemplates = modAccessPolicyTemplate::getCoreTemplates(); + return $initialized; } @@ -206,45 +222,64 @@ public function beforeIteration(array $list) } /** - * @param xPDOObject $object + * @param xPDOObject|modAccessPolicy $object * @return array */ public function prepareRow(xPDOObject $object) { - $policy = $object->toArray(); + $permissions = [ + 'create' => $this->canCreate, + 'duplicate' => $this->canCreate, + 'update' => $this->canEdit, + 'updateTemplate' => $this->canEditTemplate, + 'delete' => $this->canRemove + ]; + $policyData = $object->toArray(); + $policyName = $object->get('name'); + $isCorePolicy = $object->isCorePolicy($policyName); + $this->setActivePermissionsCount($policyData, $object->get('data')); - $policy['cls'] = $this->prepareRowClasses($object); + if ($isCorePolicy) { + $this->modx->lexicon->setTranslatedCoreDescriptors($policyData, 'policy'); + } + if (in_array($policyData['template_name'], $this->corePolicyTemplates)) { + $this->modx->lexicon->setTranslatedCoreDescriptors($policyData, 'policytemplate', 'name:template_name', true); + } + + $policyData['reserved'] = ['name' => $this->corePolicies]; + $policyData['isProtected'] = $isCorePolicy; + $policyData['creator'] = $isCorePolicy ? 'modx' : strtolower($this->modx->lexicon('user')) ; + if ($isCorePolicy) { + unset($permissions['delete']); + } + $policyData['permissions'] = $permissions; + unset($policyData['data']); - $permissions = []; + return $policyData; + } + + protected function setActivePermissionsCount(array &$policy, array $data) + { if (!empty($policy['total_permissions'])) { - $data = $object->get('data'); - $ct = 0; + $n = 0; if (!empty($data)) { foreach ($data as $k => $v) { if (!empty($v)) { - $permissions[] = $k; - $ct++; + $n++; } } } - $policy['active_permissions'] = $ct; + $policy['active_permissions'] = $n; $policy['active_of'] = $this->modx->lexicon('active_of', [ 'active' => $policy['active_permissions'], 'total' => $policy['total_permissions'], ]); - $policy['permissions'] = $permissions; } - - unset($policy['data']); - - $policy['description_trans'] = $this->modx->lexicon($policy['description']); - - return $policy; } /** * @param xPDOObject|modAccessPolicy $object - * + * @deprecated * @return string */ protected function prepareRowClasses(xPDOObject $object) diff --git a/core/src/Revolution/Processors/Security/Access/Policy/Template/GetList.php b/core/src/Revolution/Processors/Security/Access/Policy/Template/GetList.php index 877a7b5f32f..74732c29135 100644 --- a/core/src/Revolution/Processors/Security/Access/Policy/Template/GetList.php +++ b/core/src/Revolution/Processors/Security/Access/Policy/Template/GetList.php @@ -36,6 +36,14 @@ class GetList extends GetListProcessor public $permission = 'policy_template_view'; public $languageTopics = ['policy', 'en:policy']; + /** @param boolean $isGridFilter Indicates the target of this list data is a filter field */ + protected $isGridFilter = false; + public $canCreate = false; + public $canEdit = false; + public $canRemove = false; + protected $corePolicyTemplates; + protected $corePolicyTemplateGroups; + /** * @return bool */ @@ -45,7 +53,16 @@ public function initialize() $this->setDefaultProperties([ 'sortAlias' => 'modAccessPolicyTemplate', 'query' => '', + 'exclude' => 'creator' ]); + $this->isGridFilter = $this->getProperty('isGridFilter', false); + + $this->canCreate = $this->modx->hasPermission('policy_template_new') && $this->modx->hasPermission('policy_template_save'); + $this->canEdit = $this->modx->hasPermission('policy_template_edit'); + $this->canRemove = $this->modx->hasPermission('policy_template_delete'); + $this->corePolicyTemplates = $this->classKey::getCoreTemplates(); + $this->corePolicyTemplateGroups = modAccessPolicyTemplateGroup::getCoreGroups(); + return $initialized; } @@ -104,22 +121,42 @@ public function prepareQueryAfterCount(xPDOQuery $c) } /** - * @param xPDOObject $object + * @param xPDOObject|modAccessPolicyTemplate $object * @return array */ public function prepareRow(xPDOObject $object) { - $template = $object->toArray(); + $permissions = [ + 'create' => $this->canCreate, + 'duplicate' => $this->canCreate, + 'update' => $this->canEdit, + 'delete' => $this->canRemove + ]; + $templateData = $object->toArray(); + $templateName = $object->get('name'); + $isCoreTemplate = $object->isCoreTemplate($templateName); + + if ($isCoreTemplate) { + $this->modx->lexicon->setTranslatedCoreDescriptors($templateData, 'policytemplate'); + } + if (in_array($templateData['template_group_name'], $this->corePolicyTemplateGroups)) { + $this->modx->lexicon->setTranslatedCoreDescriptors($templateData, 'templategroup', 'name:template_group_name', ['name']); + } - $template['description_trans'] = $this->modx->lexicon($template['description']); - $template['cls'] = $this->prepareRowClasses($object); + $templateData['reserved'] = ['name' => $this->corePolicyTemplates]; + $templateData['isProtected'] = $isCoreTemplate; + $templateData['creator'] = $isCoreTemplate ? 'modx' : strtolower($this->modx->lexicon('user')) ; + if ($isCoreTemplate) { + unset($permissions['delete']); + } + $templateData['permissions'] = $permissions; - return $template; + return $templateData; } /** * @param xPDOObject|modAccessPolicyTemplate $object - * + * @deprecated * @return string */ protected function prepareRowClasses(xPDOObject $object) diff --git a/core/src/Revolution/Processors/Security/Role/GetList.php b/core/src/Revolution/Processors/Security/Role/GetList.php index 69693afbed5..555585fe9fb 100644 --- a/core/src/Revolution/Processors/Security/Role/GetList.php +++ b/core/src/Revolution/Processors/Security/Role/GetList.php @@ -33,7 +33,11 @@ class GetList extends GetListProcessor public $languageTopics = ['user']; public $permission = 'view_role'; public $defaultSortField = 'authority'; + + public $canCreate = false; + public $canEdit = false; public $canRemove = false; + protected $coreRoles; /** * {@inheritDoc} @@ -49,7 +53,10 @@ public function initialize() $this->setProperty('sort', 'name'); } + $this->canCreate = $this->modx->hasPermission('new_role') && $this->modx->hasPermission('save_role'); + $this->canEdit = $this->modx->hasPermission('edit_role') && $this->modx->hasPermission('save_role'); $this->canRemove = $this->modx->hasPermission('delete_role'); + $this->coreRoles = $this->classKey::getCoreRoles(); return $initialized; } @@ -98,30 +105,33 @@ public function isAssigned(int $id) /** * {@inheritDoc} - * @param xPDOObject $object + * @param xPDOObject|modUserGroupRole $object * @return array */ public function prepareRow(xPDOObject $object) { - $objectArray = $object->toArray(); - $objectId = $object->get('id'); + // Note: Role does not have a checkPolicy() method + $permissions = [ + 'create' => $this->canCreate, + 'update' => $this->canEdit, + 'delete' => $this->canRemove + ]; + + $roleData = $object->toArray(); + $roleId = $object->get('id'); $roleName = $object->get('name'); - $isCoreRole = in_array($objectId, [1, 2]) || in_array($roleName, ['Super User', 'Member']); - - $perm = []; - if (!$isCoreRole) { - $perm[] = 'edit'; - if ($this->isAssigned($objectId)) { - $objectArray['isAssigned'] = 1; - } - if ($this->canRemove) { - $perm[] = 'remove'; - } - } else { - $objectArray['isProtected'] = 1; + $isCoreRole = $object->isCoreRole($roleName); + + if ($isCoreRole) { + $this->modx->lexicon->setTranslatedCoreDescriptors($roleData, 'role:user'); + } elseif ($this->isAssigned($roleId)) { + $roleData['isAssigned'] = 1; } - $objectArray['perm'] = implode(' ', $perm); + $roleData['reserved'] = ['name' => $this->coreRoles]; + $roleData['isProtected'] = $isCoreRole; + $roleData['creator'] = $isCoreRole ? 'modx' : strtolower($this->modx->lexicon('user')) ; + $roleData['permissions'] = !$isCoreRole ? $permissions : [] ; - return $objectArray; + return $roleData; } } diff --git a/core/src/Revolution/Processors/Source/GetList.php b/core/src/Revolution/Processors/Source/GetList.php index 33cd1fbf571..d50da5063cf 100644 --- a/core/src/Revolution/Processors/Source/GetList.php +++ b/core/src/Revolution/Processors/Source/GetList.php @@ -36,6 +36,12 @@ class GetList extends GetListProcessor /** @param boolean $isGridFilter Indicates the target of this list data is a filter field */ protected $isGridFilter = false; + public $canCreate = false; + public $canEdit = false; + public $canRemove = false; + + protected $coreSources; + /** * {@inheritDoc} * @return boolean @@ -47,8 +53,15 @@ public function initialize() 'showNone' => false, 'query' => '', 'streamsOnly' => false, + 'exclude' => 'creator' ]); $this->isGridFilter = $this->getProperty('isGridFilter', false); + + $this->canCreate = $this->modx->hasPermission('source_save'); + $this->canEdit = $this->modx->hasPermission('source_edit'); + $this->canRemove = $this->modx->hasPermission('source_delete'); + $this->coreSources = $this->classKey::getCoreSources(); + return $initialized; } @@ -143,31 +156,36 @@ public function getSortClassKey() */ public function prepareRow(xPDOObject $object) { - $canEdit = $this->modx->hasPermission('source_edit'); - $canSave = $this->modx->hasPermission('source_save'); - $canRemove = $this->modx->hasPermission('source_delete'); + $permissions = [ + 'create' => $this->canCreate && $object->checkPolicy('save'), + 'duplicate' => $this->canCreate && $object->checkPolicy('copy'), + 'update' => $this->canEdit && $object->checkPolicy('save'), + 'delete' => $this->canRemove && $object->checkPolicy('remove') + ]; + + $sourceData = $object->toArray(); + $sourceName = $object->get('name'); + $isCoreSource = $object->isCoreSource($sourceName); + + if ($isCoreSource) { + $this->modx->lexicon->setTranslatedCoreDescriptors($sourceData, 'source'); + } + + $sourceData['reserved'] = ['name' => $this->coreSources]; + $sourceData['isProtected'] = $isCoreSource; + $sourceData['creator'] = $isCoreSource ? 'modx' : strtolower($this->modx->lexicon('user')) ; + if ($isCoreSource) { + unset($permissions['delete']); + } + $sourceData['permissions'] = $permissions; - $objectArray = $object->toArray(); - $objectArray['iconCls'] = $this->modx->getOption('mgr_source_icon', null, 'icon-folder-open-o'); + $sourceData['iconCls'] = $this->modx->getOption('mgr_source_icon', null, 'icon-folder-open-o'); $props = $object->getPropertyList(); if (isset($props['iconCls']) && !empty($props['iconCls'])) { - $objectArray['iconCls'] = $props['iconCls']; - } - - $cls = []; - if ($canSave && $canEdit && $object->checkPolicy('save')) { - $cls[] = 'pupdate'; + $sourceData['iconCls'] = $props['iconCls']; } - if ($canRemove && $object->checkPolicy('remove')) { - $cls[] = 'premove'; - } - if ($canSave && $object->checkPolicy('copy')) { - $cls[] = 'pduplicate'; - } - - $objectArray['cls'] = implode(' ', $cls); - return $objectArray; + return $sourceData; } } diff --git a/core/src/Revolution/Processors/Source/Update.php b/core/src/Revolution/Processors/Source/Update.php index 0b49c6944dc..341d9dc03a7 100644 --- a/core/src/Revolution/Processors/Source/Update.php +++ b/core/src/Revolution/Processors/Source/Update.php @@ -1,4 +1,5 @@ object->get('name'); + $id = $this->object->get('id'); + + if (empty($name)) { + $this->addFieldError('name', $this->modx->lexicon('source_err_ns_name')); + } elseif ($this->alreadyExists($name, $id)) { + $this->addFieldError('name', $this->modx->lexicon('source_err_ae_name', [ + 'name' => $name, + ])); + } $this->setSourceProperties(); + return parent::beforeSave(); } + /** + * Check to see if a Media Source with the specified name already exists + * @param string $name + * @return boolean + */ + public function alreadyExists($name, $id) + { + return $this->modx->getCount(modMediaSource::class, [ + 'name' => $name, + 'id:!=' => $id + ]) > 0; + } + /** * Sets the properties on the source * @return void diff --git a/core/src/Revolution/Processors/System/Dashboard/GetList.php b/core/src/Revolution/Processors/System/Dashboard/GetList.php index 199b1b00913..9832cac8f21 100644 --- a/core/src/Revolution/Processors/System/Dashboard/GetList.php +++ b/core/src/Revolution/Processors/System/Dashboard/GetList.php @@ -1,4 +1,5 @@ setDefaultProperties([ + 'query' => '', + 'exclude' => 'creator' + ]); + $canManage = $this->modx->hasPermission('dashboards'); + $this->canCreate = $canManage; + $this->canEdit = $canManage; + $this->canRemove = $canManage; + $this->coreDashboards = $this->classKey::getCoreDashboards(); + + return $initialized; + } + /** * @param xPDOQuery $c * @return xPDOQuery @@ -61,13 +87,33 @@ public function prepareQueryAfterCount(xPDOQuery $c) } /** - * @param xPDOObject $object + * @param xPDOObject|modDashboard $object * @return array */ public function prepareRow(xPDOObject $object) { - $objectArray = $object->toArray(); - $objectArray['cls'] = 'pupdate premove pduplicate'; - return $objectArray; + $permissions = [ + 'create' => $this->canCreate, + 'duplicate' => $this->canCreate, + 'update' => $this->canEdit, + 'delete' => $this->canRemove + ]; + $dashboardData = $object->toArray(); + $dashboardName = $object->get('name'); + $isCoreDashboard = $object->isCoreDashboard($dashboardName); + + if ($isCoreDashboard) { + $this->modx->lexicon->setTranslatedCoreDescriptors($dashboardData, 'dashboards'); + } + + $dashboardData['reserved'] = ['name' => $this->coreDashboards]; + $dashboardData['isProtected'] = $isCoreDashboard; + $dashboardData['creator'] = $isCoreDashboard ? 'modx' : strtolower($this->modx->lexicon('user')) ; + if ($isCoreDashboard) { + unset($permissions['delete']); + } + $dashboardData['permissions'] = $permissions; + + return $dashboardData; } } diff --git a/core/src/Revolution/Processors/Workspace/PackageNamespace/GetList.php b/core/src/Revolution/Processors/Workspace/PackageNamespace/GetList.php index dd2e7297c62..8e0255a4993 100644 --- a/core/src/Revolution/Processors/Workspace/PackageNamespace/GetList.php +++ b/core/src/Revolution/Processors/Workspace/PackageNamespace/GetList.php @@ -14,6 +14,7 @@ use MODX\Revolution\modAccessNamespace; use MODX\Revolution\modNamespace; use MODX\Revolution\modUserGroup; +use MODX\Revolution\Transport\modTransportPackage; use MODX\Revolution\Processors\Model\GetListProcessor; use xPDO\Om\xPDOObject; use xPDO\Om\xPDOQuery; @@ -40,6 +41,13 @@ class GetList extends GetListProcessor /** @param boolean $isGridFilter Indicates the target of this list data is a filter field */ protected $isGridFilter = false; + public $canCreate = false; + public $canEdit = false; + public $canRemove = false; + + protected $coreNamespaces; + protected $extrasNamespaces = []; + /** * {@inheritDoc} * @return boolean @@ -47,10 +55,27 @@ class GetList extends GetListProcessor public function initialize() { $initialized = parent::initialize(); + $this->isGridFilter = $this->getProperty('isGridFilter', false); $this->setDefaultProperties([ - 'query' => '' + 'search' => false, + 'exclude' => 'creator' ]); - $this->isGridFilter = $this->getProperty('isGridFilter', false); + + /* + Normally these would access permission like this: + $this->canCreate = $this->modx->hasPermission('[object type]_save'); + Namespaces do not currently have changeable policy permissions, so + setting each to true; consider adding new permissions settings for + - namespace_save + - namespace_edit + - namespace_delete + */ + $this->canCreate = $this->modx->hasPermission('namespaces'); + $this->canEdit = $this->modx->hasPermission('namespaces'); + $this->canRemove = $this->modx->hasPermission('namespaces'); + $this->coreNamespaces = $this->classKey::getCoreNamespaces(); + $this->extrasNamespaces = $this->getExtrasNamespaces(); + return $initialized; } @@ -168,18 +193,72 @@ public function prepareQueryAfterCount(xPDOQuery $c) return $c; } + public function getExtrasNamespaces() + { + $namespaceList = []; + + $c = $this->modx->newQuery(modTransportPackage::class); + $c->select([ + 'name' => 'DISTINCT SUBSTRING_INDEX(`signature`,"-",1)' + ]); + $namespaces = $this->modx->getIterator(modTransportPackage::class, $c); + $namespaces->rewind(); + if ($namespaces->valid()) { + foreach ($namespaces as $namespace) { + $namespaceList[] = $namespace->get('name'); + } + } + return $namespaceList; + } + /** * Prepare the Namespace for listing - * @param xPDOObject $object + * @param xPDOObject|modNamespace $object * @return array */ public function prepareRow(xPDOObject $object) { - $objectArray = $object->toArray(); - $objectArray['perm'] = []; - $objectArray['perm'][] = 'pedit'; - $objectArray['perm'][] = 'premove'; + /* + If policy permissions get added for namespaces, change to: + $permissions = [ + 'create' => $this->canCreate && $object->checkPolicy('save'), + 'duplicate' => $this->canCreate && $object->checkPolicy('copy'), + 'update' => $this->canEdit && $object->checkPolicy('save'), + 'delete' => $this->canRemove && $object->checkPolicy('remove') + ]; + */ + $permissions = [ + 'create' => $this->canCreate, + 'duplicate' => $this->canCreate, + 'update' => $this->canEdit, + 'delete' => $this->canRemove + ]; + + $namespaceData = $object->toArray(); + $namespaceName = $object->get('name'); + $isCoreNamespace = $object->isCoreNamespace($namespaceName); + + $namespaceData['reserved'] = ['name' => $this->coreNamespaces]; + $namespaceData['isProtected'] = true; + $namespaceData['isExtrasNamespace'] = in_array($namespaceName, $this->extrasNamespaces); + + switch (true) { + case $namespaceData['isExtrasNamespace']: + $namespaceData['creator'] = 'extra'; + break; + case $isCoreNamespace: + $namespaceData['creator'] = 'modx'; + break; + default: + $namespaceData['creator'] = 'user'; + $namespaceData['isProtected'] = false; + } + // Core and Extras paths should only be editable via the installation process + if ($isCoreNamespace || $namespaceData['isExtrasNamespace']) { + $permissions = []; + } + $namespaceData['permissions'] = $permissions; - return $objectArray; + return $namespaceData; } } diff --git a/core/src/Revolution/Sources/modMediaSource.php b/core/src/Revolution/Sources/modMediaSource.php index fd01c5fd39f..7c9bdb6491a 100644 --- a/core/src/Revolution/Sources/modMediaSource.php +++ b/core/src/Revolution/Sources/modMediaSource.php @@ -70,6 +70,7 @@ abstract class modMediaSource extends modAccessibleSimpleObject implements modMe /** @var Filesystem */ protected $filesystem; + public const SOURCE_FILESYSTEM = 'Filesystem'; /** * Get the default MODX filesystem source @@ -441,7 +442,6 @@ public function getContainerList($path) foreach ($directories as $dir) { $ls[] = $dir; } - array_multisort($fileNames, SORT_ASC, SORT_STRING, $files); foreach ($files as $file) { $ls[] = $file; @@ -1255,7 +1255,6 @@ public function setVisibility($path, $visibility) return false; } - /** * @param string $object * @@ -1749,10 +1748,26 @@ public function clearCache(array $options = []) $c->select($this->xpdo->escape('key')); $options[xPDO::OPT_CACHE_KEY] = $this->getOption('cache_media_sources_key', $options, 'media_sources'); - $options[xPDO::OPT_CACHE_HANDLER] = $this->getOption('cache_media_sources_handler', $options, $this->getOption(xPDO::OPT_CACHE_HANDLER, $options)); - $options[xPDO::OPT_CACHE_FORMAT] = (int)$this->getOption('cache_media_sources_format', $options, $this->getOption(xPDO::OPT_CACHE_FORMAT, $options, xPDOCacheManager::CACHE_PHP)); - $options[xPDO::OPT_CACHE_ATTEMPTS] = (int)$this->getOption('cache_media_sources_attempts', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPTS, $options, 10)); - $options[xPDO::OPT_CACHE_ATTEMPT_DELAY] = (int)$this->getOption('cache_media_sources_attempt_delay', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, $options, 1000)); + $options[xPDO::OPT_CACHE_HANDLER] = $this->getOption( + 'cache_media_sources_handler', + $options, + $this->getOption(xPDO::OPT_CACHE_HANDLER, $options) + ); + $options[xPDO::OPT_CACHE_FORMAT] = (int)$this->getOption( + 'cache_media_sources_format', + $options, + $this->getOption(xPDO::OPT_CACHE_FORMAT, $options, xPDOCacheManager::CACHE_PHP) + ); + $options[xPDO::OPT_CACHE_ATTEMPTS] = (int)$this->getOption( + 'cache_media_sources_attempts', + $options, + $this->getOption(xPDO::OPT_CACHE_ATTEMPTS, $options, 10) + ); + $options[xPDO::OPT_CACHE_ATTEMPT_DELAY] = (int)$this->getOption( + 'cache_media_sources_attempt_delay', + $options, + $this->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, $options, 1000) + ); if ($c->prepare() && $c->stmt->execute()) { while ($row = $c->stmt->fetch(PDO::FETCH_ASSOC)) { @@ -2062,18 +2077,12 @@ protected function getAllowedExtensionsArray($properties = []) /** * @param array $properties * - * @return array|mixed|string + * @return array */ protected function getSkipExtensionsArray($properties = []) { $skipExtensions = $this->getOption('skipExtensions', $properties, ''); - if (empty($skipExtensions)) { - $skipExtensions = []; - } else { - $skipExtensions = explode(',', $skipExtensions); - } - - return !empty($skipExtensions) ? explode(',', $skipExtensions) : []; + return $skipExtensions ? explode(',', $skipExtensions) : []; } @@ -2450,4 +2459,26 @@ protected function isFileImage($file, $image_extensions = []) return false; } + + /** + * Returns a list of core Media Sources + * + * @return array + */ + public static function getCoreSources() + { + return [ + self::SOURCE_FILESYSTEM + ]; + } + + /** + * @param string $name The name of the Media Source + * + * @return bool + */ + public function isCoreSource($name) + { + return in_array($name, static::getCoreSources(), true); + } } diff --git a/core/src/Revolution/modAccessPolicyTemplate.php b/core/src/Revolution/modAccessPolicyTemplate.php index 426706093fb..83720f8ab3f 100644 --- a/core/src/Revolution/modAccessPolicyTemplate.php +++ b/core/src/Revolution/modAccessPolicyTemplate.php @@ -3,6 +3,7 @@ namespace MODX\Revolution; use xPDO\Om\xPDOSimpleObject; +use xPDO\xPDO; /** * A collection of modAccessPermission records that are used as a Template for custom modAccessPolicy objects. Is @@ -16,17 +17,19 @@ * @property modAccessPermission[] $Permissions * @property modAccessPolicy[] $Policies * + * @property modX|xPDO $xpdo + * * @package MODX\Revolution */ class modAccessPolicyTemplate extends xPDOSimpleObject { - const TEMPLATE_ADMINISTRATOR = 'AdministratorTemplate'; - const TEMPLATE_CONTEXT = 'ContextTemplate'; - const TEMPLATE_ELEMENT = 'ElementTemplate'; - const TEMPLATE_MEDIA_SOURCE = 'MediaSourceTemplate'; - const TEMPLATE_NAMESPACE = 'NamespaceTemplate'; - const TEMPLATE_OBJECT = 'ObjectTemplate'; - const TEMPLATE_RESOURCE = 'ResourceTemplate'; + public const TEMPLATE_ADMINISTRATOR = 'AdministratorTemplate'; + public const TEMPLATE_CONTEXT = 'ContextTemplate'; + public const TEMPLATE_ELEMENT = 'ElementTemplate'; + public const TEMPLATE_MEDIA_SOURCE = 'MediaSourceTemplate'; + public const TEMPLATE_NAMESPACE = 'NamespaceTemplate'; + public const TEMPLATE_OBJECT = 'ObjectTemplate'; + public const TEMPLATE_RESOURCE = 'ResourceTemplate'; /** * Returns list of core Policy Templates diff --git a/core/src/Revolution/modContext.php b/core/src/Revolution/modContext.php index a86ca5c44e9..7368de82112 100644 --- a/core/src/Revolution/modContext.php +++ b/core/src/Revolution/modContext.php @@ -29,6 +29,9 @@ class modContext extends modAccessibleObject * @var array RESERVED_KEYS */ public const RESERVED_KEYS = ['mgr', 'web', 'root']; + public const CONTEXT_MANAGER = 'mgr'; + public const CONTEXT_DEFAULT = 'web'; + public const CONTEXT_DEFAULT_NAME = 'Website'; /** * An array of configuration options for this context @@ -125,14 +128,24 @@ public function prepare($regenerate = false, array $options = []) if ($this->config === null || $regenerate) { if ($this->xpdo->getCacheManager()) { $context = []; - if ($regenerate || !($context = $this->xpdo->cacheManager->get($this->getCacheKey(), [ - xPDO::OPT_CACHE_KEY => $this->xpdo->getOption('cache_context_settings_key', null, - 'context_settings'), - xPDO::OPT_CACHE_HANDLER => $this->xpdo->getOption('cache_context_settings_handler', null, - $this->xpdo->getOption(xPDO::OPT_CACHE_HANDLER, null, 'xPDO\Cache\xPDOFileCache')), - xPDO::OPT_CACHE_FORMAT => (integer)$this->xpdo->getOption('cache_context_settings_format', null, - $this->xpdo->getOption(xPDO::OPT_CACHE_FORMAT, null, xPDOCacheManager::CACHE_PHP)), - ]))) { + $cacheKey = $this->xpdo->getOption('cache_context_settings_key', null, 'context_settings'); + $cacheHandler = $this->xpdo->getOption( + 'cache_context_settings_handler', + null, + $this->xpdo->getOption(xPDO::OPT_CACHE_HANDLER, null, 'xPDO\Cache\xPDOFileCache') + ); + $cacheFormat = (int)$this->xpdo->getOption( + 'cache_context_settings_format', + null, + $this->xpdo->getOption(xPDO::OPT_CACHE_FORMAT, null, xPDOCacheManager::CACHE_PHP) + ); + if ( + $regenerate || !($context = $this->xpdo->cacheManager->get($this->getCacheKey(), [ + xPDO::OPT_CACHE_KEY => $cacheKey, + xPDO::OPT_CACHE_HANDLER => $cacheHandler, + xPDO::OPT_CACHE_FORMAT => $cacheFormat + ])) + ) { $context = $this->xpdo->cacheManager->generateContext($this->get('key'), $options); } if (!empty($context)) { @@ -202,9 +215,9 @@ public function findPolicy($context = '') $enabled = true; $context = !empty($context) ? $context : $this->xpdo->context->get('key'); if (!is_object($this->xpdo->context) || $context === $this->xpdo->context->get('key')) { - $enabled = (boolean)$this->xpdo->getOption('access_context_enabled', null, true); + $enabled = (bool)$this->xpdo->getOption('access_context_enabled', null, true); } elseif ($this->xpdo->getContext($context)) { - $enabled = (boolean)$this->xpdo->contexts[$context]->getOption('access_context_enabled', true); + $enabled = (bool)$this->xpdo->contexts[$context]->getOption('access_context_enabled', true); } if ($enabled) { if (empty($this->_policies) || !isset($this->_policies[$context])) { @@ -284,7 +297,7 @@ public function makeUrl($id, $args = '', $scheme = -1, array $options = []) } if ($config['friendly_urls'] == 1) { - if ((integer)$id === (integer)$config['site_start']) { + if ((int)$id === (int)$config['site_start']) { $alias = ($scheme === '' || $scheme === -1) ? $config['base_url'] : ''; $found = true; } else { @@ -480,4 +493,27 @@ public function getResourceURI($id) return $uri; } + + /** + * Returns a list of core Contexts + * + * @return array + */ + public static function getCoreContexts() + { + return [ + self::CONTEXT_MANAGER, + self::CONTEXT_DEFAULT + ]; + } + + /** + * @param string $key The key of the Context + * + * @return bool + */ + public function isCoreContext($key) + { + return in_array($key, static::getCoreContexts(), true); + } } diff --git a/core/src/Revolution/modDashboard.php b/core/src/Revolution/modDashboard.php index 4949054f1fa..d0fbd7243e1 100644 --- a/core/src/Revolution/modDashboard.php +++ b/core/src/Revolution/modDashboard.php @@ -19,6 +19,8 @@ */ class modDashboard extends xPDOSimpleObject { + public const DASHBOARD_DEFAULT = 'Default'; + /** * Get the default MODX dashboard * @@ -73,7 +75,6 @@ public function remove(array $ancestors = []) return $removed; } - /** * Render the Dashboard * @@ -129,7 +130,6 @@ public function render(modManagerController $controller, $user = null) return implode("\n", $output); } - /** * @param int $user * @param bool $force @@ -168,7 +168,6 @@ public function sortWidgets($user = 0, $force = false) } } - /** * @param modUser $user */ @@ -188,4 +187,26 @@ protected function addUserWidgets(modUser $user) $new->save(); } } + + /** + * Returns a list of core Dashboards + * + * @return array + */ + public static function getCoreDashboards(): array + { + return [ + self::DASHBOARD_DEFAULT + ]; + } + + /** + * @param string $name The name of the Dashboard + * + * @return bool + */ + public function isCoreDashboard($name): bool + { + return in_array($name, static::getCoreDashboards(), true); + } } diff --git a/core/src/Revolution/modLexicon.php b/core/src/Revolution/modLexicon.php index c3d6f34d956..1e1202bc259 100644 --- a/core/src/Revolution/modLexicon.php +++ b/core/src/Revolution/modLexicon.php @@ -62,6 +62,8 @@ class modLexicon */ protected $_loadedTopics = []; + protected array $config; + /** * Creates the modLexicon instance. * @@ -672,6 +674,14 @@ public function getFilterLanguageList($namespace = 'core', $topic = '') public function process($key, array $params = [], $language = '') { $language = !empty($language) ? $language : $this->modx->getOption('cultureKey', null, 'en'); + // if (strpos($key, 'name') || strpos($key, 'descr')) { + // $this->modx->log( + // \modX::LOG_LEVEL_ERROR, + // "\r\t process: + // \t\$language: {$language} + // \t\$this->_lexicon: " . print_r($this->_lexicon, true) + // ); + // } /* make sure key exists */ if (!is_string($key) || !isset($this->_lexicon[$language][$key])) { $this->modx->log(xPDO::LOG_LEVEL_DEBUG, 'Language string not found: "' . $key . '"'); @@ -794,4 +804,127 @@ public function clear($language = '') $this->_lexicon = [$this->modx->getOption('cultureKey', null, 'en') => []]; } } + + /** + * Evaluates and sets a built-in, core object's language-specific name and, optionally, description + * @param array $objectData A reference to the data being prepared for output + * @param string $objectSpec Identifies the type of object being worked with and + * optionally an alternate Lexicon topic specification (used when the object type + * does not match the Lexicon topic), i.e.: + * * Access Policy = 'policy' + * * Context = 'context' + * * Media Source = 'source' + * * Namespace = 'namespace' + * * Policy Template = 'policytemplate:policy' + * * Template Group = 'templategroup:policy' + * * Role = 'role:user' + * * etc... + * @param string $namingKey The database column (and optional alias) containing the name of the object. + * For example, pass in + * * 'name' - when there is no alias needed (only one object with a db column 'name' is being processed) + * * 'name:template_name' - when the object being processed is a secondary one having the same + * db column name as the primary one, thus requiring use of its alias of 'template_name' to avoid conflicts + * @param array $limitToFields Transforms only the field(s) given in the given list + */ + public function setTranslatedCoreDescriptors(array &$objectData, string $objectSpec, string $namingKey = 'name', array $limitToFields = []): void + { + $useTranslation = $this->modx->getOption('cultureKey') !== 'en'; + $fallbackTopic = $objectSpec; + /* + In a couple instances, more than one core object type is shown on/in the same page/grid. + For example, the Access Policies grid also displays the associated Policy Template. In + these cases, where both objects' db columns are "name," aliased names are used to differentiate + them (e.g., "template_name" is the alias for Policy Templates shown in the Policies grid). However, + all generated lexicon keys for name fields should end with their db column name (usually "_name"). + */ + if (strpos($namingKey, ':') > 0) { + $keys = explode(':', $namingKey); + $valueKey = $keys[1]; + $nameKey = $keys[0]; + } else { + $valueKey = $namingKey; + $nameKey = $namingKey; + } + + if (strpos($objectSpec, ':') > 0) { + $keys = explode(':', $objectSpec); + $fallbackTopic = $keys[1]; + $objectType = $keys[0]; + } else { + $fallbackTopic = $objectSpec; + $objectType = $objectSpec; + } + + // Gets the value of the naming field (usually "name," with a few exceptions [e.g., Context is "key"]) + $nameValue = trim($objectData[$valueKey]); + + // Set up the fields array to translate; at minimum, will include the field holding the object's name + $fieldNames = [$nameKey]; + // if (!$skipDescription) { + // $fieldNames[] = 'description'; + // } + if (empty($limitToFields)) { + // $fieldNames = [$nameKey, 'description']; + $fieldNames[] = 'description'; + } else { + $fieldNames = $limitToFields; + } + /* + Collapse any spaces, punctuation, and the words 'and' & 'or' to + provide a clean array key-comatible lexicon key based on + the object's type and name. + + For example, the built-in Access Policy with the name "Load, List and View" will + yield the following lexicon keys for that Policy's name and description: + _policy_load_list_view_name + _policy_load_list_view_description + + Note that these descriptors have the underscore prefix to denote + that they represent a typically immutable core-installed object + */ + $find = [' and ', ' or ', ',']; + $objectKey = str_replace($find, ' ', $nameValue); + $objectKey = preg_replace('/[\s]+/', '_', $objectKey); + $baseKey = '_' . $objectType . '_' . strtolower($objectKey) . '_'; + + // _templategroup_administrator_name + foreach ($fieldNames as $fieldName) { + $lexiconKey = $baseKey . $fieldName; + $dataName = $fieldName === $nameKey ? $valueKey : $fieldName ; + /* + $objectData[$dataName . '_trans'] = $this->modx->lexicon($lexiconKey); + $valueIsLexiconKey = $objectData[$dataName . '_trans'] === $lexiconKey; + $hasTranslation = !empty($objectData[$dataName . '_trans']) && !$valueIsLexiconKey; + $objectData[$dataName . '_trans_missing'] = !$hasTranslation; + + // Fall back to English entry when no translation is found for this field + if ($useTranslation && !$hasTranslation && empty($objectData[$dataName])) { + $en = $this->getFileTopic('en', 'core', $fallbackTopic); + $objectData[$dataName] = $en[$lexiconKey]; + $this->modx->log( + \modX::LOG_LEVEL_ERROR, + "\r\t setTranslatedCoreDescriptors: + \t\t\$lexiconKey: {$lexiconKey} + \t\t{$dataName}: {$objectData[$dataName]} + \t\ten arr: " . print_r($en, true) + ); + } + */ + $value = $this->modx->lexicon($lexiconKey); + $valueIsLexiconKey = $value === $lexiconKey; + + if ($useTranslation) { + $hasTranslation = !empty($value) && !$valueIsLexiconKey; + if ($hasTranslation) { + $objectData[$dataName] = $value; + } else if (empty($objectData[$dataName])) { + // Fall back to English entry when no translation is found for this field + $en = $this->getFileTopic('en', 'core', $fallbackTopic); + $objectData[$dataName] = $en[$lexiconKey]; + } + } else { + $objectData[$dataName] = $value; + } + } + } } diff --git a/core/src/Revolution/modNamespace.php b/core/src/Revolution/modNamespace.php index 7c06ba8a161..4e686af03b2 100644 --- a/core/src/Revolution/modNamespace.php +++ b/core/src/Revolution/modNamespace.php @@ -27,6 +27,8 @@ */ class modNamespace extends modAccessibleObject { + public const NAMESPACE_CORE = 'core'; + public function save($cacheFlag = null) { $saved = parent::save(); @@ -157,4 +159,26 @@ public function findPolicy($context = '') return $policy; } + + /** + * Returns a list of core Namespaces + * + * @return array + */ + public static function getCoreNamespaces() + { + return [ + self::NAMESPACE_CORE + ]; + } + + /** + * @param string $key The key of the Context + * + * @return bool + */ + public function isCoreNamespace($key) + { + return in_array($key, static::getCoreNamespaces(), true); + } } diff --git a/core/src/Revolution/modUserGroupRole.php b/core/src/Revolution/modUserGroupRole.php index b08c5a10317..d558e618787 100644 --- a/core/src/Revolution/modUserGroupRole.php +++ b/core/src/Revolution/modUserGroupRole.php @@ -13,11 +13,36 @@ * * @property string $name The name of the Role * @property string $description A user-provided description of this Role - * @property int $authority The authority of the role. Lower authority numbers have more power than higher ones, and - * lower numbers will inherit the Permissions of higher numbers. + * @property int $authority The authority of the role. Lower authority numbers have more power + * than higher ones, and lower numbers will inherit the Permissions of higher numbers. * * @package MODX\Revolution */ class modUserGroupRole extends xPDOSimpleObject { + public const ROLE_SUPERUSER = 'Super User'; + public const ROLE_MEMBER = 'Member'; + + /** + * Returns a list of core Roles + * + * @return array + */ + public static function getCoreRoles() + { + return [ + self::ROLE_SUPERUSER, + self::ROLE_MEMBER + ]; + } + + /** + * @param string $name The name of the Role + * + * @return bool + */ + public function isCoreRole($name) + { + return in_array($name, static::getCoreRoles(), true); + } } diff --git a/manager/assets/modext/util/utilities.js b/manager/assets/modext/util/utilities.js index 00371760026..0be86d8e9f5 100644 --- a/manager/assets/modext/util/utilities.js +++ b/manager/assets/modext/util/utilities.js @@ -63,8 +63,8 @@ MODx.util.UrlParams = { this.set({}) }, parse(str) { - const params = new URLSearchParams(str) - return Object.fromEntries(params.entries()) + const params = new URLSearchParams(str); + return Object.fromEntries(params.entries()); } } @@ -141,6 +141,20 @@ MODx.StaticTextField = Ext.extend(Ext.form.TextField, { }); Ext.reg('statictextfield',MODx.StaticTextField); +/** + * Static Textarea + */ +MODx.StaticTextArea = Ext.extend(Ext.form.TextArea, { + fieldClass: 'x-static-text-field', + + onRender: function() { + this.readOnly = true; + this.disabled = !this.initialConfig.submitValue; + MODx.StaticTextArea.superclass.onRender.apply(this, arguments); + } +}); +Ext.reg('statictextarea',MODx.StaticTextArea); + /** * Static Boolean */ @@ -1164,3 +1178,37 @@ MODx.util.FileDownload = function (fields) { me.isFinished(successCallback, failureCallback); } }; + +// MODx.util.Render = { +// /** +// * Get a translated field value from an object's record; primarily used +// * for core-installed objects +// * @param {String} fieldName The field's name or dataIndex +// * @param {Object} record The object's complete record +// * @param {Boolean} translateProtectedOnly Whether to translate only protected (typically core-installed) objects +// * @returns {String} The translated field or its default if not translation is available +// */ +// translatedValue: function (fieldName, record, translateProtectedOnly = true) { +// const +// isGridRecord = Object.hasOwn(record, 'json'), +// dataObject = isGridRecord ? record.json : record +// ; +// if (MODx.cultureKey === 'en' || (translateProtectedOnly && !dataObject.isProtected)) { +// // console.log(`translatedValue :: do not translate ${record[fieldName]}...`); +// /* +// To update a grid's display of a single record after a successful +// edit without refreshing the entire grid (causing a new request +// and query to the back end), a call to the grid store's commit() +// method is made to update the row. This is only available in the +// data prop (the json prop is populated from the back end) +// */ +// return isGridRecord ? record.data[fieldName] : record[fieldName] ; +// } +// // Return default value when no translation has been processed for this object +// // console.log(`translatedValue :: translating ${fieldName}...`); +// return Object.hasOwn(dataObject, `${fieldName}_trans`) && !dataObject[`${fieldName}_trans_missing`] +// ? dataObject[`${fieldName}_trans`] +// : dataObject[fieldName] +// ; +// } +// } \ No newline at end of file diff --git a/manager/assets/modext/widgets/core/modx.grid.js b/manager/assets/modext/widgets/core/modx.grid.js index fb88431782b..35422877976 100644 --- a/manager/assets/modext/widgets/core/modx.grid.js +++ b/manager/assets/modext/widgets/core/modx.grid.js @@ -5,98 +5,103 @@ MODx.grid.Grid = function(config = {}) { this._loadStore(); this._loadColumnModel(); - Ext.applyIf(config,{ - store: this.store - ,cm: this.cm - ,sm: new Ext.grid.RowSelectionModel({singleSelect:true}) - ,paging: (config.bbar ? true : false) - ,loadMask: true - ,autoHeight: true - ,collapsible: true - ,stripeRows: true - ,header: false - ,cls: 'modx-grid' - ,preventRender: true - ,preventSaveRefresh: true - ,showPerPage: true - ,stateful: false - ,showActionsColumn: true - ,disableContextMenuAction: false - ,menuConfig: { - defaultAlign: 'tl-b?' - ,enableScrolling: false - } - ,viewConfig: { - forceFit: true - ,enableRowBody: true - ,autoFill: true - ,showPreview: true - ,scrollOffset: 0 - ,emptyText: config.emptyText || _('ext_emptymsg') - } - ,groupingConfig: { + Ext.applyIf(config, { + store: this.store, + cm: this.cm, + sm: new Ext.grid.RowSelectionModel({ singleSelect: true }), + // eslint-disable-next-line no-unneeded-ternary + paging: config.bbar ? true : false, + loadMask: true, + autoHeight: true, + collapsible: true, + stripeRows: true, + header: false, + cls: 'modx-grid', + preventRender: true, + preventSaveRefresh: true, + showPerPage: true, + stateful: false, + showActionsColumn: true, + disableContextMenuAction: false, + menuConfig: { + defaultAlign: 'tl-b?', + enableScrolling: false + }, + viewConfig: { + forceFit: true, + enableRowBody: true, + autoFill: true, + showPreview: true, + scrollOffset: 0, + emptyText: config.emptyText || _('ext_emptymsg') + }, + groupingConfig: { enableGroupingMenu: true } }); if (config.paging) { - var pgItms = config.showPerPage ? [_('per_page')+':',{ - xtype: 'textfield' - ,cls: 'x-tbar-page-size' - ,value: config.pageSize || (parseInt(MODx.config.default_per_page) || 20) - ,listeners: { - 'change': {fn:this.onChangePerPage,scope:this} - ,'render': {fn: function(cmp) { - new Ext.KeyMap(cmp.getEl(), { - key: Ext.EventObject.ENTER - ,fn: this.blur - ,scope: cmp - }); - },scope:this} + const pgItms = config.showPerPage ? [`${_('per_page')}:`, { + xtype: 'textfield', + cls: 'x-tbar-page-size', + value: config.pageSize || (parseInt(MODx.config.default_per_page, 10) || 20), + listeners: { + change: { + fn: this.onChangePerPage, + scope: this + }, + render: { + fn: function(cmp) { + new Ext.KeyMap(cmp.getEl(), { + key: Ext.EventObject.ENTER, + fn: this.blur, + scope: cmp + }); + }, + scope: this + } } }] : []; if (config.pagingItems) { - for (var i=0;i 1 ? "' - + (config.pluralText || _('records')) + '" : "' - + (config.singleText || _('record')) + '"]})' + const groupingConfig = { + forceFit: true, + scrollOffset: 0, + groupTextTpl: `{text} ({[values.rs.length]} {[values.rs.length > 1 ? '${config.pluralText || _('records')}' : '${config.singleText || _('record')}']})` }; Ext.applyIf(config.groupingConfig, groupingConfig); - Ext.applyIf(config,{ + Ext.applyIf(config, { view: new Ext.grid.GroupingView(config.groupingConfig) }); } if (config.tbar) { - for (var ix = 0;ix 1)) { return false; } @@ -113,10 +118,10 @@ MODx.grid.Grid = function(config = {}) { } config.columns.push({ - id: 'modx-actions' - ,width: config.actionsColumnWidth || defaultActionsColumnWidth - ,menuDisabled: true - ,renderer: this.actionsColumnRenderer.bind(this) + id: 'modx-actions', + width: config.actionsColumnWidth || defaultActionsColumnWidth, + menuDisabled: true, + renderer: this.actionsColumnRenderer.bind(this) }); } @@ -128,26 +133,28 @@ MODx.grid.Grid = function(config = {}) { } config.cm.columns.push({ - id: 'modx-actions' - ,width: config.actionsColumnWidth || defaultActionsColumnWidth - ,menuDisabled: true - ,renderer: this.actionsColumnRenderer.bind(this) + id: 'modx-actions', + width: config.actionsColumnWidth || defaultActionsColumnWidth, + menuDisabled: true, + renderer: this.actionsColumnRenderer.bind(this) }); } } - MODx.grid.Grid.superclass.constructor.call(this,config); + MODx.grid.Grid.superclass.constructor.call(this, config); this._loadMenu(config); - this.addEvents('beforeRemoveRow','afterRemoveRow','afterAutoSave'); + this.addEvents('beforeRemoveRow', 'afterRemoveRow', 'afterAutoSave'); if (this.autosave) { this.on('afterAutoSave', this.onAfterAutoSave, this); } - if (!config.preventRender) { this.render(); } + if (!config.preventRender) { + this.render(); + } this.on({ render: { fn: function() { const topToolbar = this.getTopToolbar(); - if (topToolbar && topToolbar.initialConfig.cls && topToolbar.initialConfig.cls == 'has-nested-filters') { + if (topToolbar && topToolbar.initialConfig.cls && topToolbar.initialConfig.cls === 'has-nested-filters') { this.hasNestedFilters = true; } }, @@ -159,32 +166,57 @@ MODx.grid.Grid = function(config = {}) { } }); if (config.autosave) { - this.on('afteredit',this.saveRecord,this); + this.on('afteredit', this.saveRecord, this); } if (config.paging && config.grouping) { this.getBottomToolbar().bind(this.store); } - if (!config.paging && !config.hasOwnProperty('pageSize')) { + if (!config.paging && !Object.hasOwn(config, 'pageSize')) { config.pageSize = 0; } - this.getStore().load({ params: { - start: config.pageStart || 0 - ,limit: config.hasOwnProperty('pageSize') ? config.pageSize : (parseInt(MODx.config.default_per_page) || 20) + start: config.pageStart || 0, + limit: Object.hasOwn(config, 'pageSize') ? config.pageSize : (parseInt(MODx.config.default_per_page, 10) || 20) } }); - this.getStore().on('exception',this.onStoreException,this); + this.getStore().on('exception', this.onStoreException, this); this.config = config; this.on('click', this.onClickHandler, this); }; -Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ - windows: {} +Ext.extend(MODx.grid.Grid, Ext.grid.EditorGridPanel, { + + windows: {}, + + protectedIdentifiers: null, + + /** + * The data index, not necessarily the primary key, used + * to determine if a row can be deleted / or if the value + * of the row's data index is an un-usable, reserved value + */ + protectedDataIndex: null, + + userCanEdit: false, + + userCanCreate: false, - ,onStoreException: function(dataProxy, type, action, options, response) { + userCanDelete: false, + + gridMenuActions: [], + + /** @property {Boolean} userHasPermissions Whether user has permissions of any kind to manipulate the current grid's data */ + hasPermissions: false, + + /** @property {Boolean} userHasSavePermissions Whether user has the general ability to save (to either create or edit) */ + userHasSavePermissions: false, + + showActionsMenu: null, + + onStoreException: function(dataProxy, type, action, options, response) { const responseStatusCode = response.status || 'Unknown', responseStatusText = !Ext.isEmpty(response.statusText) ? `(${response.statusText})` : '' ; @@ -223,58 +255,76 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ } this.getView().emptyText = `
${msg}
`; this.getView().refresh(false); - } + }, - ,saveRecord: function(e) { + /** + * Executes auto save of the row after edits are complete and optional success callback + * @param {Ext.Event} e Extended event data including: + * * column + * * row + * * field (name) + * * grid (full grid object) + * * record (full Ext record object including store, data, json, etc.) + * * originalValue + * * value (current) + */ + saveRecord: function(e) { e.record.data.menu = null; - var p = this.config.saveParams || {}; - Ext.apply(e.record.data,p); - var d = Ext.util.JSON.encode(e.record.data); - var url = this.config.saveUrl || (this.config.url || this.config.connector); + const p = this.config.saveParams || {}; + Ext.apply(e.record.data, p); + const + data = Ext.util.JSON.encode(e.record.data), + url = this.config.saveUrl || (this.config.url || this.config.connector) + ; MODx.Ajax.request({ - url: url - ,params: { - action: this.config.save_action || 'updateFromGrid' - ,data: d - } - ,listeners: { + url: url, + params: { + action: this.config.save_action || 'updateFromGrid', + data: data + }, + listeners: { success: { - fn: function(r) { + fn: function(response) { if (this.config.save_callback) { - Ext.callback(this.config.save_callback,this.config.scope || this,[r]); + Ext.callback(this.config.save_callback, this.config.scope || this, [response]); } e.record.commit(); if (!this.config.preventSaveRefresh) { const gridRefresh = new Ext.util.DelayedTask(() => this.refresh()); gridRefresh.delay(200); } - this.fireEvent('afterAutoSave',r); - } - ,scope: this - } - ,failure: { - fn: function(r) { + const + /** @var {Object} eventData Plucking only the needed event props to forward in the post-save event */ + eventData = { field: e.field, originalValue: e.originalValue, value: e.value }, + responseData = { ...response, eventData } + ; + this.fireEvent('afterAutoSave', responseData); + }, + scope: this + }, + failure: { + fn: function(response) { e.record.reject(); - this.fireEvent('afterAutoSave', r); - } - ,scope: this + this.fireEvent('afterAutoSave', response); + }, + scope: this } } }); - } + }, /** * Method executed after a record has been edited/saved inline from within the grid * * @param {Object} response - The processor save response object. See modConnectorResponse::outputContent (PHP) */ - ,onAfterAutoSave: function(response) { + onAfterAutoSave: function(response) { if (!response.success && response.message === '') { - var msg = ''; + let msg = ''; if (response.data.length) { // We get some data for specific field(s) error but not regular error message Ext.each(response.data, function(data, index, list) { - msg += (msg != '' ? '
' : '') + data.msg; + msg += (msg !== '' ? '
' : '') + data.msg; }, this); } if (Ext.isEmpty(msg)) { @@ -283,93 +333,103 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ } MODx.msg.alert(_('error'), msg); } - } + }, - ,onChangePerPage: function(tf,nv) { - if (Ext.isEmpty(nv)) return false; - nv = parseInt(nv); + onChangePerPage: function(tf, nv) { + if (Ext.isEmpty(nv)) { return false; } + nv = parseInt(nv, 10); this.getBottomToolbar().pageSize = nv; - this.store.load({params:{ - start:0 - ,limit: nv - }}); - } + this.store.load({ + params: { + start: 0, + limit: nv + } + }); + }, - ,loadWindow: function(btn,e,win,or) { - var r = this.menu.record; + loadWindow: function(btn, e, win, or) { + const r = this.menu.record; if (!this.windows[win.xtype] || win.force) { - Ext.applyIf(win,{ - record: win.blankValues ? {} : r - ,grid: this - ,listeners: { - 'success': {fn:win.success || this.refresh,scope:win.scope || this} + Ext.applyIf(win, { + record: win.blankValues ? {} : r, + grid: this, + listeners: { + success: { + fn: win.success || this.refresh, + scope: win.scope || this + } } }); if (or) { - Ext.apply(win,or); + Ext.apply(win, or); } this.windows[win.xtype] = Ext.ComponentMgr.create(win); } - if (this.windows[win.xtype].setValues && win.blankValues !== true && r != undefined) { + if (this.windows[win.xtype].setValues && win.blankValues !== true && r !== undefined) { this.windows[win.xtype].setValues(r); } this.windows[win.xtype].show(e.target); - } + }, - ,confirm: function(type,text) { - var p = { action: type }; - var k = this.config.primaryKey || 'id'; + confirm: function(type, text) { + const + p = { action: type }, + k = this.config.primaryKey || 'id' + ; p[k] = this.menu.record[k]; MODx.msg.confirm({ - title: _(type) - ,text: _(text) || _('confirm_remove') - ,url: this.config.url - ,params: p - ,listeners: { - 'success': {fn:this.refresh,scope:this} + title: _(type), + text: _(text) || _('confirm_remove'), + url: this.config.url, + params: p, + listeners: { + success: { fn: this.refresh, scope: this } } }); - } + }, - ,remove: function(text, action) { + remove: function(text, action) { if (this.destroying) { return MODx.grid.Grid.superclass.remove.apply(this, arguments); } - var r = this.menu.record; + const r = this.menu.record; text = text || 'confirm_remove'; - var p = this.config.saveParams || {}; - Ext.apply(p,{ action: action || 'remove' }); - var k = this.config.primaryKey || 'id'; + const p = this.config.saveParams || {}; + Ext.apply(p, { action: action || 'remove' }); + const k = this.config.primaryKey || 'id'; p[k] = r[k]; - if (this.fireEvent('beforeRemoveRow',r)) { + if (this.fireEvent('beforeRemoveRow', r)) { MODx.msg.confirm({ - title: _('warning') - ,text: _(text, r) - ,url: this.config.url - ,params: p - ,listeners: { - 'success': {fn:function() { - this.removeActiveRow(r); - },scope:this} + title: _('warning'), + text: _(text, r), + url: this.config.url, + params: p, + listeners: { + success: { + fn: function() { + this.removeActiveRow(r); + }, + scope: this + } } }); } - } + }, - ,removeActiveRow: function(r) { - if (this.fireEvent('afterRemoveRow',r)) { - var rx = this.getSelectionModel().getSelected(); + removeActiveRow: function(r) { + if (this.fireEvent('afterRemoveRow', r)) { + const rx = this.getSelectionModel().getSelected(); this.getStore().remove(rx); } - } + }, - ,_loadMenu: function() { + _loadMenu: function() { this.menu = new Ext.menu.Menu(this.config.menuConfig); - } + }, - ,_showMenu: function(g,ri,e) { + _showMenu: function(g, ri, e) { e.stopEvent(); e.preventDefault(); this.menu.record = this.getStore().getAt(ri).data; @@ -377,41 +437,42 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ this.getSelectionModel().selectRow(ri); } this.menu.removeAll(); + let menu; if (this.getMenu) { - var m = this.getMenu(g,ri,e); - if (m && m.length && m.length > 0) { - this.addContextMenuItem(m); + menu = this.getMenu(g, ri, e); + if (menu && menu.length && menu.length > 0) { + this.addContextMenuItem(menu); } } - if ((!m || m.length <= 0) && this.menu.record.menu) { + if ((!menu || menu.length <= 0) && this.menu.record.menu) { this.addContextMenuItem(this.menu.record.menu); } if (this.menu.items.length > 0) { this.menu.showAt(e.xy); } - } + }, - ,_loadStore: function() { + _loadStore: function() { if (this.config.grouping) { this.store = new Ext.data.GroupingStore({ - url: this.config.url - ,baseParams: this.config.baseParams || { action: this.config.action || 'getList'} - ,reader: new Ext.data.JsonReader({ - totalProperty: 'total' - ,root: 'results' - ,fields: this.config.fields - }) - ,sortInfo:{ - field: this.config.sortBy || 'id' - ,direction: this.config.sortDir || 'ASC' - } - ,remoteSort: this.config.remoteSort || false - ,remoteGroup: this.config.remoteGroup || false - ,groupField: this.config.groupBy || 'name' - ,groupDir: this.config.groupDir || 'ASC' - ,storeId: this.config.storeId || Ext.id() - ,autoDestroy: true - ,listeners: { + url: this.config.url, + baseParams: this.config.baseParams || { action: this.config.action || 'getList' }, + reader: new Ext.data.JsonReader({ + totalProperty: 'total', + root: 'results', + fields: this.config.fields + }), + sortInfo: { + field: this.config.sortBy || 'id', + direction: this.config.sortDir || 'ASC' + }, + remoteSort: this.config.remoteSort || false, + remoteGroup: this.config.remoteGroup || false, + groupField: this.config.groupBy || 'name', + groupDir: this.config.groupDir || 'ASC', + storeId: this.config.storeId || Ext.id(), + autoDestroy: true, + listeners: { beforeload: function(store, options) { const changedGroupDir = store.groupField === store.sortInfo.field && store.groupDir !== store.sortInfo.direction; if (changedGroupDir) { @@ -438,15 +499,15 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ }); } else { this.store = new Ext.data.JsonStore({ - url: this.config.url - ,baseParams: this.config.baseParams || { action: this.config.action || 'getList' } - ,fields: this.config.fields - ,root: 'results' - ,totalProperty: 'total' - ,remoteSort: this.config.remoteSort || false - ,storeId: this.config.storeId || Ext.id() - ,autoDestroy: true - ,listeners:{ + url: this.config.url, + baseParams: this.config.baseParams || { action: this.config.action || 'getList' }, + fields: this.config.fields, + root: 'results', + totalProperty: 'total', + remoteSort: this.config.remoteSort || false, + storeId: this.config.storeId || Ext.id(), + autoDestroy: true, + listeners: { load: function() { const cmp = Ext.getCmp('modx-content'); if (cmp) { @@ -456,25 +517,27 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ } }); } - } + }, - ,_loadColumnModel: function() { + _loadColumnModel: function() { if (this.config.columns) { - var c = this.config.columns; - for (var i=0;i item.trim()); + columnIds.forEach(colId => { + const colIndex = colModel.getIndexById(colId); + colModel.setEditable(colIndex, false); + }); + } + }, + + /* User Group-Level Permissions Checks for the calling "class" object */ + + /** + * @property {Function} setUserCanEdit - Assigns a value to userCanEdit property based on + * the user's permissions; used to adjust which menu items are available, whether to render links + * to and item's editing page, and css cues across many grid classes + * + * @param {Array} groupPermissions - A set of permissions keys to evaluate; note that many areas currently + * rely on a pair of permissions (save_x and edit_x), both of which must be enabled to edit a grid item + * + * @return void + */ + setUserCanEdit: function(groupPermissions) { + groupPermissions = groupPermissions.map(item => item.trim()); + this.userCanEdit = groupPermissions.every(permission => MODx.perm[permission]); + if (this.userCanEdit) { + this.userHasPermissions = true; + } + }, + + /** + * @property {Function} setUserCanCreate - Assigns a value to userCanCreate property based on + * the user's permissions; used to adjust which menu items are available (namely the Duplicate item) + * and whether to render the Create button in the grid's toolbar + * + * @param {Array} groupPermissions - A set of permissions keys to evaluate; note that many areas currently + * rely on a pair of permissions (save_x and new_x), both of which must be enabled to create/duplicate a grid item + * + * @return void + */ + setUserCanCreate: function(groupPermissions) { + groupPermissions = groupPermissions.map(item => item.trim()); + this.userCanCreate = groupPermissions.every(permission => MODx.perm[permission]); + if (this.userCanCreate) { + this.userHasPermissions = true; + } + }, + + /** + * @property {Function} setUserCanDelete - Assigns a value to userCanDelete property based on + * the user's permissions; used to adjust which menu items are available in the context menus + * and whether to render the Delete menu item within a grid toolbar's Batch button + * + * @param {Array} groupPermissions - A set of permissions keys to evaluate + * + * @return void + */ + setUserCanDelete: function(groupPermissions) { + groupPermissions = groupPermissions.map(item => item.trim()); + this.userCanDelete = groupPermissions.every(permission => MODx.perm[permission]); + if (this.userCanDelete) { + this.userHasPermissions = true; + } + }, + + /* Record-Level Permissions Checks, for objects with specific policies */ + + userHasRecordPermissions: function(record) { + const objPermissions = record.json.permissions; + if (Ext.isEmpty(objPermissions)) { + return false; + } + return Object.values(objPermissions).some(permission => Boolean(permission) === true); + }, + + userCanEditRecord: function(record) { + const objPermissions = record.json.permissions; + return !Ext.isEmpty(objPermissions) && objPermissions.update === true; + }, + + userCanDeleteRecord: function(record) { + const objPermissions = record.json.permissions; + return !Ext.isEmpty(objPermissions) && !record.json.isProtected && objPermissions.delete === true; + }, - ,renderEditableColumn: function(renderer) { + userCanDuplicateRecord: function(record) { + const objPermissions = record.json.permissions; + return !Ext.isEmpty(objPermissions) && objPermissions.duplicate === true; + }, + + /** + * @property {Function} setShowActionsMenu - Based on properties set in the calling child class and the + * the current user's permissions for actions taken within that class (create, edit, delete, etc), + * evaluates whether the actions menu trigger should appear and sets boolean value on the showActionsMenu property + * + * @return void + */ + setShowActionsMenu: function() { + if (this.config.disableContextMenuAction === true) { + this.showActionsMenu = false; + return; + } + const permissionsValues = []; + this.gridMenuActions.forEach(mode => { + mode = mode === 'duplicate' ? 'userCanCreate' : `userCan${Ext.util.Format.capitalize(mode)}`; + const modePermission = mode === 'userCanExport' ? true : this[mode]; + if (['userCanCreate', 'userCanEdit'].includes(mode) && modePermission === true) { + this.userHasSavePermissions = true; + } + permissionsValues.push(modePermission); + }); + this.showActionsMenu = !(permissionsValues.length === 0 || permissionsValues.every(value => value === false) === true); + }, + + /** + * @property {Function} recordIsProtected - Used to remove the ability to delete + * specific record rows, regardless of permissions levels, based on a given record identifier + * + * @param {Number} subject - The value of the current record's identifier + * @param {Number} protectedIdentifiers - The record identifiers to be protected (making them non-editable/deletable) + * + * @return {Boolean} + */ + recordIsProtected: function(subject, protectedIdentifiers) { + if (Ext.isEmpty(protectedIdentifiers)) { + return false; + } + protectedIdentifiers = protectedIdentifiers.map(identifier => (typeof identifier === 'string' ? identifier.trim() : identifier)); + return protectedIdentifiers.includes(subject); + }, + + /** + * @property {Function} valueIsReserved - Wraps a grid value with a real or simulated link — a trigger that appears + * like an anchor link, usually to access a dropdown chooser or other control + * + * @param {Array|String} reservedValues - A set of values that can not be used for a particular object's field + * @param {Object} value - The submitted value being tested + * + * @return {Boolean} + */ + valueIsReserved: function(reservedValues, value) { + if (!Array.isArray(reservedValues)) { + reservedValues = reservedValues.split(','); + } + return reservedValues.some(reserved => reserved.toLowerCase() === value.toLowerCase()); + }, + + /** + * @property {Function} getRemovableItemsFromSelection - Prunes protected items from the current + * selection list before submitting for deletion, or for setting the state of the 'Delete Selected' + * menu item + * + * @param {String} itemIdType - The data type of the value being inspected (either string or integer) + * + * @return {Array} + */ + getRemovableItemsFromSelection: function(itemIdType = 'int') { + const selections = this.getSelectionModel().getSelections(), + pk = this.config.primaryKey || 'id', + removableItems = [] + ; + if (selections.length <= 0) { + return []; + } + selections.forEach(record => { + const deletableRecord = record.json.permissions.delete; + if (!record.json.isProtected && deletableRecord) { + const item = itemIdType === 'string' ? record.data[pk] : parseInt(record.data[pk], 10); + removableItems.push(item); + } + }); + return removableItems; + }, + + renderEditableColumn: function(renderer) { return function(value, metaData, record, rowIndex, colIndex, store) { if (renderer) { if (typeof renderer.fn === 'function') { - var scope = (renderer.scope) ? renderer.scope : false; + const scope = (renderer.scope) ? renderer.scope : false; renderer = renderer.fn.bind(scope); } - if (typeof renderer === 'function') { value = renderer(value, metaData, record, rowIndex, colIndex, store); } @@ -581,148 +829,199 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ metaData.css = ['x-editable-column', metaData.css || ''].join(' '); return value; - } - } + }; + }, - ,rendYesNo: function(v,md) { - if (v === 1 || v == '1') { v = true; } - if (v === 0 || v == '0') { v = false; } + rendYesNo: function(v, metaData) { + if (v === 1 || v === '1') { v = true; } + if (v === 0 || v === '0') { v = false; } switch (v) { case true: case 'true': case 1: - md.css = 'green'; + metaData.css = 'green'; return _('yes'); case false: case 'false': case '': case 0: - md.css = 'red'; + metaData.css = 'red'; return _('no'); // no default } - } + }, - ,getSelectedAsList: function() { - var sels = this.getSelectionModel().getSelections(); - if (sels.length <= 0) return false; + /* Depricated; remove once all grids with bulk deletion capability have been converted */ + getSelectedAsList: function() { + const sels = this.getSelectionModel().getSelections(); + if (sels.length <= 0) { return false; } - var cs = ''; - for (var i=0;i' + '' + '
    ' @@ -734,10 +1033,49 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ + '', { compiled: true }); - } + }, + + actionsColumnRenderer: function(value, metaData, record, rowIndex, colIndex, store) { + /* + Note: To maintain backward compatibility for core grids that have not yet been updated + to the new permissions checks and for extras that may extend this class in their grids, + we check showActionsMenu for strict boolean values (which will only be set by grids using + the new checks); otherwise showActionsMenu will be null (its default value set above), + indicating the legacy checks are to be used. + */ + if (this.showActionsMenu === false) { + return; + } + /* + showActionsMenu will be true if at least one user group-level permission is granted, + excluding create/new permissions (since that is not executed by our context/actions menus). + */ + if (this.showActionsMenu) { + const { isProtected } = record.json; + // Export is always available; only continue filtering if grid does not offer export + if (!this.gridMenuActions.includes('export')) { + if (!this.userHasSavePermissions && isProtected) { + return; + } + // Checking record-level permissions; this block checking for 'cls' can be removed once all grids are updated + if (Object.hasOwn(record.data, 'cls')) { + if (Ext.isEmpty(record.data.cls)) { + return; + } + } + if (Object.hasOwn(record.json, 'permissions')) { + if ( + Ext.isEmpty(record.json.permissions) + || Object.values(record.json.permissions).every(permission => !permission) + ) { + return; + } + } + } + } + // eslint-disable-next-line prefer-spread + const actions = this.getActions.apply(this, arguments); - ,actionsColumnRenderer: function(value, metaData, record, rowIndex, colIndex, store) { - var actions = this.getActions.apply(this, [record, rowIndex, colIndex, store]); if (this.config.disableContextMenuAction !== true) { actions.push({ text: _('context_menu'), @@ -745,36 +1083,53 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ icon: 'gear' }); } - return this._getActionsColumnTpl().apply({ actions: actions }); - } + }, - ,renderLink: function(v,attr) { - var el = new Ext.Element(document.createElement('a')); - el.addClass('x-grid-link'); - el.dom.title = _('edit'); - for (var i in attr) { - el.dom[i] = attr[i]; + /** + * @property {Function} renderLink - Wraps a grid value with a real or simulated link — a trigger that appears + * like an anchor link, usually to access a dropdown chooser or other control + * + * @param {String} content - The value being wrapped + * @param {Object} attributes - Html attributes to add to the link's tag + * @param {Boolean} isSimulated - Indicates whether the link is real (anchor tag) or not (simulated) + * @param {String} isSimulatedTag - The html tag name to wrap the content with + * + * @return {String} + */ + renderLink: function(content, attributes = {}, isSimulated = false, isSimulatedTag = 'span') { + const + tag = isSimulated ? isSimulatedTag : 'a', + classes = isSimulated ? 'x-grid-link simulated-link' : 'x-grid-link', + el = new Ext.Element(document.createElement(tag)) + ; + el.addClass(classes); + // Add default title if none given in attributes + if (!Object.hasOwn(attributes, 'title')) { + attributes.title = _('edit'); } - el.dom.innerHTML = Ext.util.Format.htmlEncode(v); + Object.entries(attributes).forEach(([attr, value]) => { + el.dom[attr] = value; + }); + el.dom.innerHTML = Ext.util.Format.htmlEncode(content); return el.dom.outerHTML; - } + }, /** * Deprecated; renamed checkCellIsEditable. Remove in 3.1 */ - ,checkEditable: function(e) { + checkEditable: function(e) { this.checkCellIsEditable(e); - } + }, /** * Disables cell editor under specified conditions * @param {Object} e - Ext event object containing references to grid, record, field, value, row (index), column (index), and cancel (set true to cancel edit). * @return {Boolean} Return false to cancel or true to commit the edit */ - ,checkCellIsEditable: function(e) { + checkCellIsEditable: function(e) { const permissions = e.record.data.perm || ''; if (permissions.indexOf('edit') === -1) { return false; @@ -794,20 +1149,20 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ default: } return true; - } + }, /** * Add one or more classes to a specific Editor Grid cell, typically to indicate a level of restriction - * + * * @param {Object} record - The row's data record * @param {Array} lockConditions - A set of one or more Boolean values (or ones that cast correctly to the expected Boolean value) derived from the row record or other values that indicate whether or not the subject cell should be marked as locked * @param {String} lockedClasses - One or more css class names * @param {Boolean} conditionsRequireAll - Whether all passed lockConditions need to evaluate to true to apply the locked class(es) */ - ,setEditableCellClasses: function(record, lockConditions = [], lockedClasses = 'locked', conditionsRequireAll = true) { + setEditableCellClasses: function(record, lockConditions = [], lockedClasses = '', conditionsRequireAll = true) { const - permissions = record.data.perm.trim(), - hasEditPermission = permissions.split(' ').includes('edit') + userCanEditRecord = this.userCanEditRecord(record), + lockedCSS = lockedClasses || 'locked' ; let classes = '', @@ -819,13 +1174,13 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ : lockConditions.some(condition => Boolean(condition) === true) ; } - if (Ext.isEmpty(permissions)) { + if (!this.userCanEdit || !this.userHasRecordPermissions(record) || !userCanEditRecord) { classes = 'editor-disabled'; - } else if (hasEditPermission && shouldLock) { - classes = lockedClasses; + } else if (userCanEditRecord && shouldLock) { + classes = lockedCSS; } return classes; - } + }, /** * @property {Function} getLinkTemplate - Adds a link on a grid column's value based on the passed params. @@ -837,7 +1192,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * @param {Object} options - Additional URL query parameters (linkParams) and attributes for the link's anchor tag * @return {Ext.Template} */ - ,getLinkTemplate: function(controllerPath, displayValueIndex, options = {}) { + getLinkTemplate: function(controllerPath, displayValueIndex, options = {}) { /* linkParams, if given, should be an array of objects in the following format: [{ key: 'paramKey', valueIndex: 'paramValue' }, ...{}] @@ -849,7 +1204,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ linkTarget: '_blank' }); let params = ''; - controllerPath = controllerPath.indexOf('?a=') === 0 ? controllerPath : `?a=${controllerPath}` ; + controllerPath = controllerPath.indexOf('?a=') === 0 ? controllerPath : `?a=${controllerPath}`; if (options.linkParams.length > 0) { params = []; options.linkParams.forEach(param => { @@ -861,18 +1216,18 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ `{${displayValueIndex}:htmlEncode}`, { compiled: true } ); - } + }, - ,getActions: function(record, rowIndex, colIndex, store) { + getActions: function(value, metaData, record, rowIndex, colIndex, store) { return []; - } + }, - ,onClickHandler: function(e) { - var target = e.getTarget(); - if (!target.classList.contains('x-grid-action')) return; - if (!target.dataset.action) return; + onClickHandler: function(e) { + const target = e.getTarget(); + if (!target.classList.contains('x-grid-action')) { return; } + if (!target.dataset.action) { return; } - var actionHandler = 'action' + target.dataset.action.charAt(0).toUpperCase() + target.dataset.action.slice(1); + let actionHandler = `action${target.dataset.action.charAt(0).toUpperCase()}${target.dataset.action.slice(1)}`; if (!this[actionHandler] || (typeof this[actionHandler] !== 'function')) { actionHandler = target.dataset.action; if (!this[actionHandler] || (typeof this[actionHandler] !== 'function')) { @@ -880,33 +1235,33 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ } } - var record = this.getSelectionModel().getSelected(); - var recordIndex = this.store.indexOf(record); + const record = this.getSelectionModel().getSelected(), + recordIndex = this.store.indexOf(record); this.menu.record = record.data; this[actionHandler](record, recordIndex, e); - } + }, - ,actionContextMenu: function(record, recordIndex, e) { + actionContextMenu: function(record, recordIndex, e) { this._showMenu(this, recordIndex, e); - } + }, - ,makeUrl: function () { + makeUrl: function() { if (Array.isArray(this.config.urlFilters) && this.config.urlFilters.length > 0) { - var s = this.getStore(); - var p = { - a: MODx.request.a - } + const s = this.getStore(), + p = { + a: MODx.request.a + }; if (MODx.request.id) { - p['id'] = MODx.request.id; + p.id = MODx.request.id; } if (MODx.request.key) { - p['key'] = MODx.request.key; + p.key = MODx.request.key; } - for (var i = 0; i < this.config.urlFilters.length; ++i) { - if (s.baseParams.hasOwnProperty(this.config.urlFilters[i]) && s.baseParams[this.config.urlFilters[i]]) { + for (let i = 0; i < this.config.urlFilters.length; ++i) { + if (Object.hasOwn(s.baseParams, this.config.urlFilters[i]) && s.baseParams[this.config.urlFilters[i]]) { if (this.config.urlFilters[i] === 'namespace') { - p['ns'] = s.baseParams[this.config.urlFilters[i]]; + p.ns = s.baseParams[this.config.urlFilters[i]]; } else { p[this.config.urlFilters[i]] = s.baseParams[this.config.urlFilters[i]]; } @@ -914,15 +1269,16 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ } return Ext.urlAppend(MODx.config.manager_url, Ext.urlEncode(p).replace(/%2F/g, '/')); } - } + }, - ,replaceState: function () { - if (typeof window.history.replaceState !== 'undefined' && - Array.isArray(this.config.urlFilters) && this.config.urlFilters.length > 0 + replaceState: function() { + if (typeof window.history.replaceState !== 'undefined' + && Array.isArray(this.config.urlFilters) + && this.config.urlFilters.length > 0 ) { window.history.replaceState(this.getStore().baseParams, document.title, this.makeUrl()); } - } + }, /** * @property {Function} findTabPanel - Recursively search ownerCts for this component's enclosing TabPanel @@ -930,28 +1286,28 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * @param {Object} referenceCmp - A child component of the TabPanel we're looking for * @return Ext.TabPanel */ - ,findTabPanel: function(referenceCmp) { - if (!referenceCmp.hasOwnProperty('ownerCt')) { + findTabPanel: function(referenceCmp) { + if (!Object.hasOwn(referenceCmp, 'ownerCt')) { console.error('MODx.grid.Grid::findTabPanel: This component must have an ownerCt to find its tab panel.'); return false; } const container = referenceCmp.ownerCt, - isTabPanel = container.hasOwnProperty('xtype') && container.xtype.includes('tabs') + isTabPanel = Object.hasOwn(container, 'xtype') && container.xtype.includes('tabs') ; if (isTabPanel) { return container; } return this.findTabPanel(container); - } + }, /** * @property {Boolean} hasNestedFilters - Indicates whether the top toolbar filter(s) are nested * within a secondary container; they will be nested when they have labels and those labels are * positioned above the filter's input. */ - ,hasNestedFilters: false + hasNestedFilters: false, - ,currentLanguage: MODx.config.cultureKey || 'en' // removed MODx.request.language + currentLanguage: MODx.config.cultureKey || 'en', // removed MODx.request.language /** * Applies a value persisted via URL (MODx.request) for use in grid and filter params. Used when multiple @@ -965,15 +1321,15 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * when no value is present. Set this to true for components that prefer an empty string * @returns {Number|String} Decoded param value */ - ,applyRequestFilter: function(tabPanelIndex, requestKey = 'policy', tabPanelType = 'vtab', setEmptyToString = false) { - const emptyVal = setEmptyToString ? '' : null ; + applyRequestFilter: function(tabPanelIndex, requestKey = 'policy', tabPanelType = 'vtab', setEmptyToString = false) { + const emptyVal = setEmptyToString ? '' : null; return Object.prototype.hasOwnProperty.call(MODx.request, tabPanelType) && parseInt(MODx.request[tabPanelType], 10) === tabPanelIndex && Object.prototype.hasOwnProperty.call(MODx.request, requestKey) - ? MODx.util.url.getParamValue(requestKey) - : emptyVal + ? MODx.util.url.getParamValue(requestKey) + : emptyVal ; - } + }, /** * Filters the grid data by the passed filter component (field) @@ -982,7 +1338,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * @param {String} param - The record index to apply the filter on; * may also be the general query/search field name. */ - ,applyGridFilter: function(cmp, param = 'query') { + applyGridFilter: function(cmp, param = 'query') { const filterValue = cmp.getValue(), store = this.getStore(), urlParams = {}, @@ -1005,8 +1361,8 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ } else { MODx.util.url.clearParam(cmp); } - if (param == 'ns') { - store.baseParams['namespace'] = filterValue; + if (param === 'ns') { + store.baseParams.namespace = filterValue; } else { store.baseParams[param] = filterValue; } @@ -1015,7 +1371,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ Determine if this is a vertical tab panel; if so there will also be a horizontal parent tab panel that needs to be accounted for */ - if (tabPanel.xtype == 'modx-vtabs') { + if (tabPanel.xtype === 'modx-vtabs') { const parentTabPanel = this.findTabPanel(tabPanel); if (parentTabPanel) { const activeParentTab = parentTabPanel.getActiveTab(); @@ -1036,18 +1392,16 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ if (parentTabItems.length > 1) { urlParams.tab = activeParentTabIdx; } - } else { - if (tabItems.length > 1) { - urlParams.tab = activeTabIdx; - } + } else if (tabItems.length > 1) { + urlParams.tab = activeTabIdx; } } store.load(); - MODx.util.url.setParams(urlParams) + MODx.util.url.setParams(urlParams); if (bottomToolbar) { bottomToolbar.changePage(1); } - } + }, /** * @property {Function} clearGridFilters - Clears all grid filters and sets them to their default value @@ -1058,7 +1412,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * 'filter-category:3', where '3' is the filter's default value to be applied (instead of setting to an empty value) * */ - ,clearGridFilters: function(items) { + clearGridFilters: function(items) { const store = this.getStore(), bottomToolbar = this.getBottomToolbar(), data = Array.isArray(items) ? items : items.split(',') @@ -1066,9 +1420,9 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ data.forEach(item => { const itemData = item.replace(/\s+/g, '').split(':'), itemId = itemData[0], - itemDefaultVal = itemData.length == 2 ? itemData[1] : null , + itemDefaultVal = itemData.length === 2 ? itemData[1] : null, cmp = this.getFilterComponent(itemId), - param = MODx.util.url.getParamNameFromCmp(cmp), + cmpParam = MODx.util.url.getParamNameFromCmp(cmp), isCombo = cmp?.xtype?.includes('combo') ; if (isCombo) { @@ -1083,13 +1437,14 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ if (!Ext.isEmpty(itemDefaultVal)) { const paramsList = Object.keys(cmp.baseParams); paramsList.forEach(param => { - switch(param) { + switch (param) { case 'namespace': cmp.baseParams[param] = 'core'; break; case 'topic': cmp.baseParams[param] = 'default'; break; + // no default } }); } @@ -1098,21 +1453,14 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ cmp.getStore().load(); } } - store.baseParams[param] = itemDefaultVal; + store.baseParams[cmpParam] = itemDefaultVal; }); store.load(); MODx.util.url.clearAllParams(); if (bottomToolbar) { bottomToolbar.changePage(1); } - } - - /** - * @property {Boolean} hasNestedFilters - Indicates whether the top toolbar filter(s) are nested - * within a secondary container; they will be nested when they have labels and those labels are - * positioned above the filter's input. - */ - ,hasNestedFilters: false + }, /** * @property {Function} getFilterComponent - Gets a filter component from the top toolbar by its itemId @@ -1120,17 +1468,17 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * @param {String} filterId - The Ext itemId of the filter component to fetch * @return {Ext.Component} */ - ,getFilterComponent: function(filterId) { + getFilterComponent: function(filterId) { const topToolbar = this.getTopToolbar(), cmp = this.hasNestedFilters && filterId !== 'filter-query' - ? topToolbar.find('itemId', `${filterId}-container`)[0].getComponent(filterId) - : topToolbar.getComponent(filterId) + ? topToolbar.find('itemId', `${filterId}-container`)[0].getComponent(filterId) + : topToolbar.getComponent(filterId) ; if (typeof cmp !== 'undefined') { return cmp; } console.error(`getFilterComponent: The filter component with itemId '${filterId}' could not be retrieved.`); - } + }, /** * @property {Function} refreshFilterOptions - Used to syncronize a filter's store options to those available in its target grid @@ -1138,7 +1486,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * @param {Array} filterData - An array of objects containing info needed to refresh each filter * @param {Boolean} clearDependentParams - If true, will clear values of dependentParams specified in the filterData */ - ,refreshFilterOptions: function(filterData = [], clearDependentParams = true) { + refreshFilterOptions: function(filterData = [], clearDependentParams = true) { if (filterData.length > 0) { filterData.forEach(data => { const filter = this.getFilterComponent(data.filterId); @@ -1146,10 +1494,13 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ const store = filter.getStore(); filter.setValue(''); if (store) { - if (data.hasOwnProperty('dependentParams')) { - const dependentParams = Array.isArray(data.dependentParams) ? data.dependentParams : data.dependentParams.split(','); + if (Object.hasOwn(data, 'dependentParams')) { + const dependentParams = Array.isArray(data.dependentParams) + ? data.dependentParams + : data.dependentParams.split(',') + ; dependentParams.forEach(param => { - if (clearDependentParams && store.baseParams.hasOwnProperty(param)) { + if (clearDependentParams && Object.hasOwn(store.baseParams, param)) { store.baseParams[param] = ''; } }); @@ -1158,9 +1509,9 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ } } }); - this.refresh(); + this.refresh(); } - } + }, /** * @property {Function} updateDependentFilter - Reloads a related filter's store based on the current filter's selected item @@ -1170,7 +1521,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * @param {Mixed} paramValue - Filter baseParams value for the paramKey * @param {Boolean} clearValue - Set true to clear filter's selected value */ - ,updateDependentFilter: function(filterId, paramKey, paramValue, clearValue = false) { + updateDependentFilter: function(filterId, paramKey, paramValue, clearValue = false) { const filter = this.getFilterComponent(filterId), filterStore = filter ? filter.getStore() : null ; @@ -1181,7 +1532,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ filterStore.baseParams[paramKey] = paramValue; filterStore.load(); } - } + }, /** * @property {Function} getQueryFilterField - Creates the query field component configuration @@ -1192,16 +1543,17 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * @param {String} implementation - Optional, an identifier used to assign grid-specific behavior * @return {Object} */ - ,getQueryFilterField: function(filterSpec = 'filter-query', implementation = 'default') { + getQueryFilterField: function(filterSpec = 'filter-query', implementation = 'default') { let queryValue = ''; const filterSpecs = filterSpec.split(':'), filterId = filterSpecs[0].trim() ; if (filterSpecs.length === 2) { + // eslint-disable-next-line prefer-destructuring queryValue = filterSpecs[1]; } else { - queryValue = MODx.request.query ? MODx.util.url.decodeParamValue(MODx.request.query) : '' ; + queryValue = MODx.request.query ? MODx.util.url.decodeParamValue(MODx.request.query) : ''; } return { xtype: 'textfield', @@ -1213,7 +1565,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ change: { fn: function(cmp, newValue, oldValue) { this.applyGridFilter(cmp); - const usergroupTree = Ext.getCmp('modx-tree-usergroup') + const usergroupTree = Ext.getCmp('modx-tree-usergroup'); if (implementation === 'user-group-users' && usergroupTree) { /* When the user group users grid is shown in the primary ACLs panel, @@ -1223,7 +1575,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ const selectedNode = usergroupTree.getSelectionModel().getSelectedNode(), groupId = MODx.util.tree.getGroupIdFromNode(selectedNode) ; - MODx.util.url.setParams({group: groupId}); + MODx.util.url.setParams({ group: groupId }); } }, scope: this @@ -1243,12 +1595,12 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ fn: this.blur, scope: cmp }); - } - ,scope: this + }, + scope: this } } - } - } + }; + }, /** * @property {Function} getClearFiltersButton - Creates the clear filter button component configuration @@ -1258,7 +1610,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * in the following format: 'filterItemId:relatedBaseParam, [filterItemId:relatedBaseParam,] ...' * @return {Object} */ - ,getClearFiltersButton: function(filters = 'filter-query', dependentFilterResets = null) { + getClearFiltersButton: function(filters = 'filter-query', dependentFilterResets = null) { if (Ext.isEmpty(filters)) { console.error('MODx.grid.Grid::getClearFiltersButton: There was a problem creating the Clear Filter button because the supplied filters list is invalid.'); return {}; @@ -1286,61 +1638,204 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ } } } - } + }; if (dependentFilterResets) { config.dependentResets = dependentFilterResets; } return config; + }, + + /** + * Builds the standard "Creator" column model object. This column displays for + * objects that have built-in system values as well as values installed/entered + * by Extras and/or Users + * @param {String} objectType Identifier for object being worked with + * @returns {Object} The configuration for the "Creator" column + */ + getCreatorColumnConfig: function(objectType) { + return { + header: _('grid_column_creator_header'), + dataIndex: 'creator', + id: `modx-${objectType}--creator`, + width: 70, + align: 'center', + tooltip: _('grid_column_creator_description'), + menuDisabled: true + }; + }, + + /** + * Builds the bulk actions button, containing a menu of various actions + * (typically only contains a delete action) + * @param {String} objectType Identifier for object being worked with + * @param {String} deleteAction Processor path for the removal action + * @param {String} pkType Specifies the object's primary key type (int or string) + * @param {...any} moreActions Additional button identifiers or config objects + * to add to the bulk actions menu + * @returns {Object} The complete bulk actions config + */ + getBulkActionsButton: function(objectType, deleteAction, pkType = 'int', ...moreActions) { + const + menuItems = [], + additionalMenuItems = [], + hasMoreActions = moreActions.length > 0 + ; + if (hasMoreActions) { + /** @var standardButtons Button configs for actions that are used in select grids, such as the Users and Form Customization (Sets) grids */ + const standardButtons = { + activate: { + text: _('selected_activate'), + itemId: 'modx-bulk-menu-opt-activate', + handler: this.activateSelected, + scope: this + }, + deactivate: { + text: _('selected_deactivate'), + itemId: 'modx-bulk-menu-opt-deactivate', + handler: this.deactivateSelected, + scope: this + } + }; + moreActions.forEach(action => { + if (typeof action === 'string') { + const key = action.toLowerCase(); + if (Object.hasOwn(standardButtons, key)) { + additionalMenuItems.push(standardButtons[key]); + } + } + }); + menuItems.push(...additionalMenuItems); + menuItems.push('-'); + } + menuItems.push({ + text: _('selected_remove'), + itemId: 'modx-bulk-menu-opt-remove', + handler: this.removeSelected.createDelegate(this, [objectType, deleteAction, pkType]), + scope: this + }); + return { + text: _('bulk_actions'), + menu: menuItems, + listeners: { + render: { + fn: function(btn) { + if (!this.userCanDelete && !hasMoreActions) { + btn.hide(); + } + }, + scope: this + }, + click: { + fn: function(btn) { + const + removableItems = this.getRemovableItemsFromSelection(pkType), + menuOptRemove = btn.menu.getComponent('modx-bulk-menu-opt-remove') + ; + if (removableItems.length === 0) { + menuOptRemove.disable(); + } else { + menuOptRemove.enable(); + } + if (hasMoreActions) { + const selections = this.getSelectionModel().getSelections(); + additionalMenuItems.forEach(item => { + const itemCmp = btn.menu.getComponent(item.itemId); + if (selections.length === 0) { + itemCmp.disable(); + } else { + itemCmp.enable(); + } + }); + } + }, + scope: this + } + } + }; + }, + + /** + * Gets the view configuration for grids having row-specific editing permissions + * @param {Boolean} hasBulkActions Whether the grid has a bulk actions option + * (uses the checkbox selection model to select multiple rows) + * @param {Boolean} hasObjectLevelPermissions Whether individual rows might have + * differing permissions, based on the specific object they represent + * @returns {Object} The complete view config + */ + getViewConfig: function(hasBulkActions = true, hasObjectLevelPermissions = true) { + return { + forceFit: true, + scrollOffset: 0, + getRowClass: function(record, index, rowParams, store) { + // Adds the returned class to the row container's css classes + if (hasObjectLevelPermissions && this.grid.userCanDeleteRecord(record)) { + return ''; + } + const rowClasses = hasBulkActions ? 'disable-selection' : '' ; + return record.json.isProtected ? `modx-protected-row ${rowClasses}` : rowClasses ; + } + }; } }); -/* local grid */ -MODx.grid.LocalGrid = function(config) { - config = config || {}; - +/* + Local Grid, used by: + - FC Profile Set TVs grid + - Element Properties grid + - Element Sources grid + - Source Properties + - Source Access Permissions + - Resource, Resource Groups (security) grid + - User, Access Permissions (user-groups) + - Dashboard Widget, Dashboards grid (modx-grid-dashboard-widget-dashboards) + - Dashboards (modx-grid-dashboard-widget-placements) +*/ +MODx.grid.LocalGrid = function(config = {}) { if (config.grouping) { - Ext.applyIf(config,{ + Ext.applyIf(config, { view: new Ext.grid.GroupingView({ - forceFit: true - ,scrollOffset: 0 - ,hideGroupedColumn: config.hideGroupedColumn ? true : false - ,groupTextTpl: config.groupTextTpl || ('{text} ({[values.rs.length]} {[values.rs.length > 1 ? "' - +(config.pluralText || _('records')) + '" : "' - +(config.singleText || _('record'))+'"]})' ) + forceFit: true, + scrollOffset: 0, + hideGroupedColumn: config.hideGroupedColumn, + groupTextTpl: config.groupTextTpl || (`{text} ({[values.rs.length]} {[values.rs.length > 1 ? "${ + config.pluralText || _('records')}" : "${ + config.singleText || _('record')}"]})`) }) }); } if (config.tbar) { - for (var i = 0;i' + '' + '
      ' @@ -1683,10 +2181,11 @@ Ext.extend(MODx.grid.LocalGrid,Ext.grid.EditorGridPanel,{ + '', { compiled: true }); - } + }, - ,actionsColumnRenderer: function(value, metaData, record, rowIndex, colIndex, store) { - var actions = this.getActions.apply(this, arguments); + actionsColumnRenderer: function(value, metaData, record, rowIndex, colIndex, store) { + // eslint-disable-next-line prefer-spread + const actions = this.getActions.apply(this, arguments); if (this.config.disableContextMenuAction !== true) { actions.push({ @@ -1699,29 +2198,31 @@ Ext.extend(MODx.grid.LocalGrid,Ext.grid.EditorGridPanel,{ return this._getActionsColumnTpl().apply({ actions: actions }); - } + }, - ,renderLink: function(v,attr) { - var el = new Ext.Element(document.createElement('a')); + renderLink: function(content, attributes) { + const el = new Ext.Element(document.createElement('a')); el.addClass('x-grid-link'); - el.dom.title = _('edit'); - for (var i in attr) { - el.dom[i] = attr[i]; + if (!Object.hasOwn(attributes, 'title')) { + attributes.title = _('edit'); } - el.dom.innerHTML = Ext.util.Format.htmlEncode(v); + Object.entries(attributes).forEach(([attr, value]) => { + el.dom[attr] = value; + }); + el.dom.innerHTML = Ext.util.Format.htmlEncode(content); return el.dom.outerHTML; - } + }, - ,getActions: function(value, metaData, record, rowIndex, colIndex, store) { + getActions: function(value, metaData, record, rowIndex, colIndex, store) { return []; - } + }, - ,onClick: function(e) { - var target = e.getTarget(); - if (!target.classList.contains('x-grid-action')) return; - if (!target.dataset.action) return; + onClick: function(e) { + const target = e.getTarget(); + if (!target.classList.contains('x-grid-action')) { return; } + if (!target.dataset.action) { return; } - var actionHandler = 'action' + target.dataset.action.charAt(0).toUpperCase() + target.dataset.action.slice(1); + let actionHandler = `action${target.dataset.action.charAt(0).toUpperCase()}${target.dataset.action.slice(1)}`; if (!this[actionHandler] || (typeof this[actionHandler] !== 'function')) { actionHandler = target.dataset.action; if (!this[actionHandler] || (typeof this[actionHandler] !== 'function')) { @@ -1729,8 +2230,8 @@ Ext.extend(MODx.grid.LocalGrid,Ext.grid.EditorGridPanel,{ } } - var record = this.getSelectionModel().getSelected(); - var recordIndex = this.store.indexOf(record); + const record = this.getSelectionModel().getSelected(), + recordIndex = this.store.indexOf(record); this.menu.record = record.data; this[actionHandler](record, recordIndex, e); @@ -1740,8 +2241,8 @@ Ext.extend(MODx.grid.LocalGrid,Ext.grid.EditorGridPanel,{ this._showMenu(this, recordIndex, e); } }); -Ext.reg('grid-local',MODx.grid.LocalGrid); -Ext.reg('modx-grid-local',MODx.grid.LocalGrid); +Ext.reg('grid-local', MODx.grid.LocalGrid); +Ext.reg('modx-grid-local', MODx.grid.LocalGrid); /* grid extensions */ /*! @@ -1767,26 +2268,26 @@ Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, { * true to toggle selected row(s) between expanded/collapsed when the enter * key is pressed (defaults to true). */ - expandOnEnter : true, + expandOnEnter: true, /** * @cfg {Boolean} expandOnDblClick * true to toggle a row between expanded/collapsed when double clicked * (defaults to true). */ - expandOnDblClick : true, + expandOnDblClick: true, - header : '', - width : 20, - sortable : false, - fixed : true, + header: '', + width: 20, + sortable: false, + fixed: true, hideable: false, - menuDisabled : true, - dataIndex : '', - id : 'expander', - lazyRender : true, - enableCaching : true, + menuDisabled: true, + dataIndex: '', + id: 'expander', + lazyRender: true, + enableCaching: true, - constructor: function(config){ + constructor: function(config) { Ext.apply(this, config); this.addEvents({ @@ -1830,8 +2331,8 @@ Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, { Ext.ux.grid.RowExpander.superclass.constructor.call(this); - if(this.tpl){ - if(typeof this.tpl == 'string'){ + if (this.tpl) { + if (typeof this.tpl == 'string') { this.tpl = new Ext.Template(this.tpl); } this.tpl.compile(); @@ -1841,39 +2342,40 @@ Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, { this.bodyContent = {}; }, - getRowClass : function(record, rowIndex, p, ds){ - p.cols = p.cols-1; - var content = this.bodyContent[record.id]; - if(!content && !this.lazyRender){ + getRowClass: function(record, rowIndex, p, ds) { + p.cols -= p.cols; + let content = this.bodyContent[record.id]; + if (!content && !this.lazyRender) { content = this.getBodyContent(record, rowIndex); } - if(content){ + if (content) { p.body = content; } return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed'; }, - init : function(grid){ + init: function(grid) { this.grid = grid; - var view = grid.getView(); + const view = grid.getView(); view.getRowClass = this.getRowClass.createDelegate(this); view.enableRowBody = true; - grid.on('render', this.onRender, this); grid.on('destroy', this.onDestroy, this); }, // @private onRender: function() { - var grid = this.grid; - var mainBody = grid.getView().mainBody; - mainBody.on('mousedown', this.onMouseDown, this, {delegate: '.x-grid3-row-expander'}); + const + { grid } = this, + { mainBody } = grid.getView() + ; + mainBody.on('mousedown', this.onMouseDown, this, { delegate: '.x-grid3-row-expander' }); if (this.expandOnEnter) { this.keyNav = new Ext.KeyNav(this.grid.getGridEl(), { - 'enter' : this.onEnter, + enter: this.onEnter, scope: this }); } @@ -1884,7 +2386,7 @@ Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, { // @private onDestroy: function() { - if(this.keyNav){ + if (this.keyNav) { this.keyNav.disable(); delete this.keyNav; } @@ -1893,8 +2395,8 @@ Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, { * which means the mainBody won't be available. On the off chance that the plugin * isn't destroyed with the grid, take care of removing the listener. */ - var mainBody = this.grid.getView().mainBody; - if(mainBody){ + const { mainBody } = this.grid.getView(); + if (mainBody) { mainBody.un('mousedown', this.onMouseDown, this); } }, @@ -1904,76 +2406,75 @@ Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, { }, onEnter: function(e) { - var g = this.grid; - var sm = g.getSelectionModel(); - var sels = sm.getSelections(); - for (var i = 0, len = sels.length; i < len; i++) { - var rowIdx = g.getStore().indexOf(sels[i]); + const g = this.grid, + sm = g.getSelectionModel(), + sels = sm.getSelections(); + for (let i = 0, len = sels.length; i < len; i++) { + const rowIdx = g.getStore().indexOf(sels[i]); this.toggleRow(rowIdx); } }, - getBodyContent : function(record, index){ - if(!this.enableCaching){ + getBodyContent: function(record, index) { + if (!this.enableCaching) { return this.tpl.apply(record.data); } - var content = this.bodyContent[record.id]; - if(!content){ + let content = this.bodyContent[record.id]; + if (!content) { content = this.tpl.apply(record.data); this.bodyContent[record.id] = content; } return content; }, - onMouseDown : function(e, t){ + onMouseDown: function(e, t) { e.stopEvent(); - var row = e.getTarget('.x-grid3-row'); + const row = e.getTarget('.x-grid3-row'); this.toggleRow(row); }, - renderer : function(v, p, record){ + renderer: function(v, p, record) { p.cellAttr = 'rowspan="2"'; return '
       
      '; }, - beforeExpand : function(record, body, rowIndex){ - if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){ - if(this.tpl && this.lazyRender){ + beforeExpand: function(record, body, rowIndex) { + if (this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false) { + if (this.tpl && this.lazyRender) { body.innerHTML = this.getBodyContent(record, rowIndex); } return true; - }else{ - return false; } + return false; }, - toggleRow : function(row){ - if(typeof row == 'number'){ + toggleRow: function(row) { + if (typeof row == 'number') { row = this.grid.view.getRow(row); } this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row); }, - expandRow : function(row){ - if(typeof row == 'number'){ + expandRow: function(row) { + if (typeof row == 'number') { row = this.grid.view.getRow(row); } - var record = this.grid.store.getAt(row.rowIndex); - var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row); - if(this.beforeExpand(record, body, row.rowIndex)){ + const record = this.grid.store.getAt(row.rowIndex), + body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row); + if (this.beforeExpand(record, body, row.rowIndex)) { this.state[record.id] = true; Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded'); this.fireEvent('expand', this, record, body, row.rowIndex); } }, - collapseRow : function(row){ - if(typeof row == 'number'){ + collapseRow: function(row) { + if (typeof row == 'number') { row = this.grid.view.getRow(row); } - var record = this.grid.store.getAt(row.rowIndex); - var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true); - if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){ + const record = this.grid.store.getAt(row.rowIndex), + body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true); + if (this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false) { this.state[record.id] = false; Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed'); this.fireEvent('collapse', this, record, body, row.rowIndex); @@ -1983,33 +2484,34 @@ Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, { Ext.preg('rowexpander', Ext.ux.grid.RowExpander); -//backwards compat +// backwards compat Ext.grid.RowExpander = Ext.ux.grid.RowExpander; Ext.ns('Ext.ux.grid'); -Ext.ux.grid.CheckColumn = function (a) { +Ext.ux.grid.CheckColumn = function(a) { Ext.apply(this, a); if (!this.id) { - this.id = Ext.id() + this.id = Ext.id(); } - this.renderer = this.renderer.createDelegate(this) + this.renderer = this.renderer.createDelegate(this); }; Ext.ux.grid.CheckColumn.prototype = { - init: function (b) { + init: function(b) { this.grid = b; - this.grid.on('render', function () { - var a = this.grid.getView(); - a.mainBody.on('mousedown', this.onMouseDown, this) + this.grid.on('render', function() { + const a = this.grid.getView(); + a.mainBody.on('mousedown', this.onMouseDown, this); }, this); - this.grid.on('destroy', this.onDestroy, this) - }, onMouseDown: function (e, t) { + this.grid.on('destroy', this.onDestroy, this); + }, + onMouseDown: function(e, t) { this.grid.fireEvent('rowclick'); - if (t.className && t.className.indexOf('x-grid3-cc-' + this.id) != -1) { + if (t.className && t.className.indexOf(`x-grid3-cc-${this.id}`) !== -1) { e.stopEvent(); - var a = this.grid.getView().findRowIndex(t); - var b = this.grid.store.getAt(a); - var sv = b.data[this.dataIndex]; + const a = this.grid.getView().findRowIndex(t), + b = this.grid.store.getAt(a), + sv = b.data[this.dataIndex]; b.set(this.dataIndex, !sv); this.grid.fireEvent('afteredit', { grid: this.grid, @@ -2020,11 +2522,13 @@ Ext.ux.grid.CheckColumn.prototype = { cancel: false }); } - }, renderer: function (v, p, a) { + }, + renderer: function(v, p, a) { p.css += ' x-grid3-check-col-td'; - return '
       
      ' - }, onDestroy: function () { - var mainBody = this.grid.getView().mainBody; + return `
       
      `; + }, + onDestroy: function() { + const { mainBody } = this.grid.getView(); if (mainBody) { mainBody.un('mousedown', this.onMouseDown, this); } @@ -2033,7 +2537,115 @@ Ext.ux.grid.CheckColumn.prototype = { Ext.preg('checkcolumn', Ext.ux.grid.CheckColumn); Ext.grid.CheckColumn = Ext.ux.grid.CheckColumn; -Ext.grid.PropertyColumnModel=function(a,b){var g=Ext.grid,f=Ext.form;this.grid=a;g.PropertyColumnModel.superclass.constructor.call(this,[{header:this.nameText,width:50,sortable:true,dataIndex:'name',id:'name',menuDisabled:true},{header:this.valueText,width:50,resizable:false,dataIndex:'value',id:'value',menuDisabled:true}]);this.store=b;var c=new f.Field({autoCreate:{tag:'select',children:[{tag:'option',value:'true',html:'true'},{tag:'option',value:'false',html:'false'}]},getValue:function(){return this.el.dom.value=='true'}});this.editors={'date':new g.GridEditor(new f.DateField({selectOnFocus:true})),'string':new g.GridEditor(new f.TextField({selectOnFocus:true})),'number':new g.GridEditor(new f.NumberField({selectOnFocus:true,style:'text-align:left;'})),'boolean':new g.GridEditor(c)};this.renderCellDelegate=this.renderCell.createDelegate(this);this.renderPropDelegate=this.renderProp.createDelegate(this)};Ext.extend(Ext.grid.PropertyColumnModel,Ext.grid.ColumnModel,{nameText:'Name',valueText:'Value',dateFormat:'m/j/Y',renderDate:function(a){return a.dateFormat(this.dateFormat)},renderBool:function(a){return a?'true':'false'},isCellEditable:function(a,b){return a==1},getRenderer:function(a){return a==1?this.renderCellDelegate:this.renderPropDelegate},renderProp:function(v){return this.getPropertyName(v)},renderCell:function(a){var b=a;if(Ext.isDate(a)){b=this.renderDate(a)}else if(typeof a=='boolean'){b=this.renderBool(a)}return Ext.util.Format.htmlEncode(b)},getPropertyName:function(a){var b=this.grid.propertyNames;return b&&b[a]?b[a]:a},getCellEditor:function(a,b){var p=this.store.getProperty(b),n=p.data.name,val=p.data.value;if(this.grid.customEditors[n]){return this.grid.customEditors[n]}if(Ext.isDate(val)){return this.editors.date}else if(typeof val=='number'){return this.editors.number}else if(typeof val=='boolean'){return this.editors['boolean']}else{return this.editors.string}},destroy:function(){Ext.grid.PropertyColumnModel.superclass.destroy.call(this);for(var a in this.editors){Ext.destroy(a)}}}); +Ext.grid.PropertyColumnModel = function(a, b) { + const + g = Ext.grid, + f = Ext.form + ; + this.grid = a; + g.PropertyColumnModel.superclass.constructor.call(this, [ + { + header: this.nameText, + width: 50, + sortable: true, + dataIndex: 'name', + id: 'name', + menuDisabled: true + }, + { + header: this.valueText, + width: 50, + resizable: false, + dataIndex: 'value', + id: 'value', + menuDisabled: true + } + ]); + this.store = b; + const c = new f.Field({ + autoCreate: { + tag: 'select', + children: [ + { tag: 'option', value: 'true', html: 'true' }, + { tag: 'option', value: 'false', html: 'false' } + ] + }, + getValue: function() { + // eslint-disable-next-line eqeqeq + return this.el.dom.value == 'true'; + } + }); + this.editors = { + date: new g.GridEditor(new f.DateField({ selectOnFocus: true })), + string: new g.GridEditor(new f.TextField({ selectOnFocus: true })), + number: new g.GridEditor(new f.NumberField({ selectOnFocus: true, style: 'text-align:left;' })), + boolean: new g.GridEditor(c) + }; + this.renderCellDelegate = this.renderCell.createDelegate(this); + this.renderPropDelegate = this.renderProp.createDelegate(this); +}; +Ext.extend(Ext.grid.PropertyColumnModel, Ext.grid.ColumnModel, { + nameText: 'Name', + valueText: 'Value', + dateFormat: 'm/j/Y', + renderDate: function(a) { + return a.dateFormat(this.dateFormat); + }, + renderBool: function(a) { + return a ? 'true' : 'false'; + }, + isCellEditable: function(a, b) { + // eslint-disable-next-line eqeqeq + return a == 1; + }, + getRenderer: function(a) { + // eslint-disable-next-line eqeqeq + return a == 1 ? this.renderCellDelegate : this.renderPropDelegate; + }, + renderProp: function(v) { + return this.getPropertyName(v); + }, + renderCell: function(a) { + let b = a; + if (Ext.isDate(a)) { + b = this.renderDate(a); + } else if (typeof a == 'boolean') { + b = this.renderBool(a); + } + return Ext.util.Format.htmlEncode(b); + }, + getPropertyName: function(a) { + const b = this.grid.propertyNames; + return b && b[a] ? b[a] : a; + }, + getCellEditor: function(a, b) { + const + p = this.store.getProperty(b), + n = p.data.name, + val = p.data.value + ; + if (this.grid.customEditors[n]) { + return this.grid.customEditors[n]; + } + if (Ext.isDate(val)) { + return this.editors.date; + } + if (typeof val == 'number') { + return this.editors.number; + } + if (typeof val == 'boolean') { + return this.editors.boolean; + } + return this.editors.string; + }, + destroy: function() { + Ext.grid.PropertyColumnModel.superclass.destroy.call(this); + // eslint-disable-next-line guard-for-in, no-restricted-syntax + for (const a in this.editors) { + Ext.destroy(a); + } + } +}); /** * MODx JSON Grid @@ -2054,18 +2666,18 @@ Ext.grid.PropertyColumnModel=function(a,b){var g=Ext.grid,f=Ext.form;this.grid=a * * [{name: 'key'}, {name: 'value'}] */ -MODx.grid.JsonGrid = function (config) { - config = config || {}; - this.ident = config.ident || 'jsongrid-mecitem' + Ext.id(); +MODx.grid.JsonGrid = function(config = {}) { + console.log(`Using JSON for grid: ${config.xtype}`); + this.ident = config.ident || `jsongrid-mecitem${Ext.id()}`; this.hiddenField = new Ext.form.TextArea({ name: config.hiddenName || config.name, hidden: true }); - this.fieldConfig = config.fieldConfig || [{name: 'key'}, {name: 'value'}]; - this.fieldConfig.push({name: 'id', hidden: true}); + this.fieldConfig = config.fieldConfig || [{ name: 'key' }, { name: 'value' }]; + this.fieldConfig.push({ name: 'id', hidden: true }); this.fieldColumns = []; this.fieldNames = []; - Ext.each(this.fieldConfig, function (el) { + Ext.each(this.fieldConfig, function(el) { this.fieldNames.push(el.name); this.fieldColumns.push({ header: el.header || _(el.name), @@ -2084,8 +2696,8 @@ MODx.grid.JsonGrid = function (config) { scope: this }, keyup: { - fn: function (sb) { - var record = this.getSelectionModel().getSelected(); + fn: function(sb) { + const record = this.getSelectionModel().getSelected(); if (record) { record.set(sb.fieldname, sb.el.dom.value); this.saveValue(); @@ -2103,7 +2715,7 @@ MODx.grid.JsonGrid = function (config) { }); }, this); Ext.applyIf(config, { - id: this.ident + '-json-grid', + id: `${this.ident}-json-grid`, fields: this.fieldNames, autoHeight: true, store: new Ext.data.JsonStore({ @@ -2111,12 +2723,12 @@ MODx.grid.JsonGrid = function (config) { data: this.loadValue(config.value) }), enableDragDrop: true, - ddGroup: this.ident + '-json-grid-dd', + ddGroup: `${this.ident}-json-grid-dd`, labelStyle: 'position: absolute', columns: this.fieldColumns, disableContextMenuAction: true, tbar: ['->', { - text: ' ' + _('add'), + text: ` ${_('add')}`, cls: 'primary-button', handler: this.addElement, scope: this @@ -2128,50 +2740,53 @@ MODx.grid.JsonGrid = function (config) { } } }); - MODx.grid.JsonGrid.superclass.constructor.call(this, config) + MODx.grid.JsonGrid.superclass.constructor.call(this, config); }; Ext.extend(MODx.grid.JsonGrid, MODx.grid.LocalGrid, { - getMenu: function () { - var m = []; + getMenu: function() { + const m = []; m.push({ text: _('remove'), handler: this.removeElement }); return m; }, - getActions: function () { + getActions: function() { return [{ action: 'removeElement', icon: 'trash-o', text: _('remove') - }] + }]; }, - addElement: function () { - var ds = this.getStore(); - var row = {}; - Ext.each(this.fieldNames, function (fieldname) { + addElement: function() { + const ds = this.getStore(), + row = {}; + Ext.each(this.fieldNames, function(fieldname) { row[fieldname] = ''; }); - row['id'] = this.getStore().getCount(); + row.id = this.getStore().getCount(); + // eslint-disable-next-line new-cap this.getStore().insert(this.getStore().getCount(), new ds.recordType(row)); this.getView().refresh(); this.getSelectionModel().selectRow(0); }, - removeElement: function () { - Ext.Msg.confirm(_('remove') || '', _('confirm_remove') || '', function (e) { + removeElement: function() { + Ext.Msg.confirm(_('remove') || '', _('confirm_remove') || '', function(e) { if (e === 'yes') { - var ds = this.getStore(); - var rows = this.getSelectionModel().getSelections(); + const ds = this.getStore(), + rows = this.getSelectionModel().getSelections(); if (!rows.length) { return false; } - for (var i = 0; i < rows.length; i++) { - var id = rows[i].id; - var index = ds.findBy(function (record) { - if (record.id === id) { - return true; - } - }); + for (let i = 0; i < rows.length; i++) { + const + { id } = rows[i], + index = ds.findBy(function(record) { + if (record.id === id) { + return true; + } + }) + ; ds.removeAt(index); } this.getView().refresh(); @@ -2179,20 +2794,20 @@ Ext.extend(MODx.grid.JsonGrid, MODx.grid.LocalGrid, { } }, this); }, - renderListener: function (grid) { + renderListener: function(grid) { new Ext.dd.DropTarget(grid.container, { copy: false, - ddGroup: this.ident + '-json-grid-dd', - notifyDrop: function (dd, e, data) { - var ds = grid.store; - var sm = grid.getSelectionModel(); - var rows = sm.getSelections(); + ddGroup: `${this.ident}-json-grid-dd`, + notifyDrop: function(dd, e, data) { + const ds = grid.store, + sm = grid.getSelectionModel(), + rows = sm.getSelections(), - var dragData = dd.getDragData(e); + dragData = dd.getDragData(e); if (dragData) { - var cindex = dragData.rowIndex; - if (typeof (cindex) !== "undefined") { - for (var i = 0; i < rows.length; i++) { + const cindex = dragData.rowIndex; + if (typeof (cindex) !== 'undefined') { + for (let i = 0; i < rows.length; i++) { ds.remove(ds.getById(rows[i].id)); } ds.insert(cindex, data.selections); @@ -2206,22 +2821,22 @@ Ext.extend(MODx.grid.JsonGrid, MODx.grid.LocalGrid, { this.add(this.hiddenField); this.saveValue(); }, - loadValue: function (value) { + loadValue: function(value) { value = Ext.util.JSON.decode(value); if (value && Array.isArray(value)) { - Ext.each(value, function (record, idx) { - value[idx]['id'] = idx; + Ext.each(value, function(record, idx) { + value[idx].id = idx; }); } else { value = []; } return value; }, - saveValue: function () { - var value = []; - Ext.each(this.getStore().getRange(), function (record) { - var row = {}; - Ext.each(this.fieldNames, function (fieldname) { + saveValue: function() { + const value = []; + Ext.each(this.getStore().getRange(), function(record) { + const row = {}; + Ext.each(this.fieldNames, function(fieldname) { if (fieldname !== 'id') { row[fieldname] = record.data[fieldname]; } @@ -2230,7 +2845,7 @@ Ext.extend(MODx.grid.JsonGrid, MODx.grid.LocalGrid, { }, this); this.hiddenField.setValue(Ext.util.JSON.encode(value)); }, - _getActionsColumnTpl: function () { + _getActionsColumnTpl: function() { return new Ext.XTemplate('' + '' + '
        ' @@ -2243,17 +2858,17 @@ Ext.extend(MODx.grid.JsonGrid, MODx.grid.LocalGrid, { compiled: true }); }, - actionsColumnRenderer: function (value, metaData, record, rowIndex, colIndex, store) { + actionsColumnRenderer: function(value, metaData, record, rowIndex, colIndex, store) { return this._getActionsColumnTpl().apply({ actions: this.getActions() }); }, - onClick: function (e) { - var target = e.getTarget(); - if (!target.classList.contains('x-grid-action')) return; - if (!target.dataset.action) return; + onClick: function(e) { + const target = e.getTarget(); + if (!target.classList.contains('x-grid-action')) { return; } + if (!target.dataset.action) { return; } - var actionHandler = 'action' + target.dataset.action.charAt(0).toUpperCase() + target.dataset.action.slice(1); + let actionHandler = `action${target.dataset.action.charAt(0).toUpperCase()}${target.dataset.action.slice(1)}`; if (!this[actionHandler] || (typeof this[actionHandler] !== 'function')) { actionHandler = target.dataset.action; if (!this[actionHandler] || (typeof this[actionHandler] !== 'function')) { @@ -2261,8 +2876,8 @@ Ext.extend(MODx.grid.JsonGrid, MODx.grid.LocalGrid, { } } - var record = this.getSelectionModel().getSelected(); - var recordIndex = this.store.indexOf(record); + const record = this.getSelectionModel().getSelected(), + recordIndex = this.store.indexOf(record); this.menu.record = record.data; this[actionHandler](record, recordIndex, e); diff --git a/manager/assets/modext/widgets/core/tree/modx.tree.js b/manager/assets/modext/widgets/core/tree/modx.tree.js index abfcca92230..fc243a4dbcb 100644 --- a/manager/assets/modext/widgets/core/tree/modx.tree.js +++ b/manager/assets/modext/widgets/core/tree/modx.tree.js @@ -8,96 +8,102 @@ Ext.namespace('MODx.tree'); * @param {Object} config An object of options. * @xtype modx-tree */ -MODx.tree.Tree = function (config) { - config = config || {}; - Ext.applyIf(config,{ - baseParams: {} - ,action: 'getNodes' - ,loaderConfig: {} +MODx.tree.Tree = function(config = {}) { + Ext.applyIf(config, { + baseParams: {}, + action: 'getNodes', + loaderConfig: {} }); if (config.action) { config.baseParams.action = config.action; } config.loaderConfig.dataUrl = config.url; config.loaderConfig.baseParams = config.baseParams; - Ext.applyIf(config.loaderConfig,{ - preloadChildren: true - ,clearOnLoad: true + Ext.applyIf(config.loaderConfig, { + preloadChildren: true, + clearOnLoad: true }); this.config = config; - var tl,root; + let + loader, + root + ; if (this.config.url) { - //@TODO extend TreeLoader here - tl = new MODx.tree.TreeLoader(config.loaderConfig); - tl.on('beforeload',function (l,node) { - tl.dataUrl = this.config.url + '?action=' + this.config.action + '&id=' + node.attributes.id; + // @TODO extend TreeLoader here + loader = new MODx.tree.TreeLoader(config.loaderConfig); + loader.on('beforeload', function(l, node) { + loader.dataUrl = `${this.config.url}?action=${this.config.action}&id=${node.attributes.id}`; if (node.attributes.type) { - tl.dataUrl += '&type=' + node.attributes.type; + loader.dataUrl += `&type=${node.attributes.type}`; } - },this); - tl.on('load',this.onLoad,this); + }, this); + loader.on('load', this.onLoad, this); root = { - nodeType: 'async' - ,text: config.root_name || config.rootName || '' - ,qtip: config.root_qtip || config.rootQtip || '' - ,draggable: false - ,id: config.root_id || config.rootId || 'root' - ,pseudoroot: true - ,attributes: { + nodeType: 'async', + text: config.root_name || config.rootName || '', + qtip: config.root_qtip || config.rootQtip || '', + draggable: false, + id: config.root_id || config.rootId || 'root', + pseudoroot: true, + attributes: { pseudoroot: true - } - ,cls: 'tree-pseudoroot-node' - ,iconCls: config.root_iconCls || config.rootIconCls || '' + }, + cls: 'tree-pseudoroot-node', + iconCls: config.root_iconCls || config.rootIconCls || '' }; } else { - tl = new Ext.tree.TreeLoader({ - preloadChildren: true - ,baseAttrs: { + loader = new Ext.tree.TreeLoader({ + preloadChildren: true, + baseAttrs: { uiProvider: MODx.tree.CheckboxNodeUI } }); root = new Ext.tree.TreeNode({ - text: this.config.rootName || '' - ,draggable: false - ,id: this.config.rootId || 'root' - ,children: this.config.data || [] - ,pseudoroot: true + text: this.config.rootName || '', + draggable: false, + id: this.config.rootId || 'root', + children: this.config.data || [], + pseudoroot: true }); } - Ext.applyIf(config,{ - useArrows: true - ,autoScroll: true - ,animate: true - ,enableDD: true - ,enableDrop: true - ,ddAppendOnly: false - ,containerScroll: true - ,collapsible: true - ,border: false - ,autoHeight: true - ,rootVisible: true - ,loader: tl - ,header: false - ,hideBorders: true - ,bodyBorder: false - ,cls: 'modx-tree' - ,root: root - ,preventRender: false - ,stateful: true - ,menuConfig: { + Ext.applyIf(config, { + useArrows: true, + autoScroll: true, + animate: true, + enableDD: true, + enableDrop: true, + ddAppendOnly: false, + containerScroll: true, + collapsible: true, + border: false, + autoHeight: true, + rootVisible: true, + loader: loader, + header: false, + hideBorders: true, + bodyBorder: false, + cls: 'modx-tree', + root: root, + preventRender: false, + stateful: true, + menuConfig: { defaultAlign: 'tl-b?', enableScrolling: false, listeners: { - show: function () { - var node = this.activeNode; - if (node) + show: function() { + const node = this.activeNode; + if (node) { node.ui.addClass('x-tree-selected'); + } }, - hide: function () { - var node = this.activeNode; - if (node) { - node.isSelected() || (node.ui && node.ui.removeClass('x-tree-selected')); + hide: function() { + const + node = this.activeNode, + isSelected = node ? node.isSelected() : false + ; + if (node && node.ui && !isSelected) { + node.ui.removeClass('x-tree-selected'); } } } @@ -105,179 +111,197 @@ MODx.tree.Tree = function (config) { }); if (config.remoteToolbar === true && (config.tbar === undefined || config.tbar === null)) { Ext.Ajax.request({ - url: config.remoteToolbarUrl || config.url - ,params: { + url: config.remoteToolbarUrl || config.url, + params: { action: config.remoteToolbarAction || 'getToolbar' - } - ,success: function (r) { - r = Ext.decode(r.responseText); - var itms = this._formatToolbar(r.object); - var tb = this.getTopToolbar(); - if (tb) { - for (var i = 0; i < itms.length; i++) { - tb.add(itms[i]); + }, + success: function(response) { + const + responseData = Ext.decode(response.responseText), + tools = this._formatToolbar(responseData.object), + topToolbar = this.getTopToolbar() + ; + if (topToolbar) { + for (let i = 0; i < tools.length; i++) { + topToolbar.add(tools[i]); } - tb.doLayout(); + topToolbar.doLayout(); } - } - ,scope: this + }, + scope: this }); - config.tbar = {bodyStyle: 'padding: 0'}; + config.tbar = { + bodyStyle: 'padding: 0' + }; } else { - var tb = this.getToolbar(); + let toolbar = this.getToolbar(); if (config.tbar && config.useDefaultToolbar) { - for (var i = 0; i < config.tbar.length; i++) { - tb.push(config.tbar[i]); + for (let i = 0; i < config.tbar.length; i++) { + toolbar.push(config.tbar[i]); } } else if (config.tbar) { - tb = config.tbar; + toolbar = config.tbar; } - Ext.apply(config,{tbar: tb}); + Ext.apply(config, { + tbar: toolbar + }); } this.setup(config); this.config = config; - this.on('append',this._onAppend,this) - + this.on('append', this._onAppend, this); }; -Ext.extend(MODx.tree.Tree,Ext.tree.TreePanel,{ - menu: null - ,options: {} - ,disableHref: false +Ext.extend(MODx.tree.Tree, Ext.tree.TreePanel, { + menu: null, + options: {}, + disableHref: false, - ,onLoad: function (ldr,node,resp) { + onLoad: function(ldr, node, response) { // add custom buttons to child nodes this.prepareNodes(node); // no select() here, just addClass, using Active Input Cookie Value to set focus - var r = Ext.decode(resp.responseText); - if (r.message) { - var el = this.getTreeEl(); + const responseData = Ext.decode(response.responseText); + if (responseData.message) { + const + el = this.getTreeEl(), + width = this.config.width > 150 ? this.config.width : 270 + ; el.addClass('modx-tree-load-msg'); - el.update(r.message); - var w = 270; - if (this.config.width > 150) { - w = this.config.width; - } - el.setWidth(w); + el.update(responseData.message); + el.setWidth(width); this.doLayout(); } - } + }, /** * Sets up the tree and initializes it with the specified options. */ - ,setup: function (config) { + setup: function(config) { config.listeners = config.listeners || {}; config.listeners.render = { - fn: function () { - if (config.autoExpandRoot !== false || !config.hasOwnProperty('autoExpandRoot')) { + fn: function() { + if (config.autoExpandRoot !== false || !Object.hasOwn(config, 'autoExpandRoot')) { this.root.expand(); } - var tl = this.getLoader(); - Ext.apply(tl,{fullMask: new Ext.LoadMask(this.getEl())}); - tl.fullMask.removeMask = false; - tl.on({ - 'load': function () { + const loader = this.getLoader(); + Ext.apply(loader, { + fullMask: new Ext.LoadMask(this.getEl()) + }); + loader.fullMask.removeMask = false; + loader.on({ + load: function() { this.fullMask.hide(); - } - ,'loadexception': function () { + }, + loadexception: function() { this.fullMask.hide(); - } - ,'beforeload': function () { + }, + beforeload: function() { this.fullMask.show(); - } - ,scope: tl + }, + scope: loader }); - } - ,scope: this + }, + scope: this }; - MODx.tree.Tree.superclass.constructor.call(this,config); - this.addEvents('afterSort','beforeSort','refresh'); - this.cm = new Ext.menu.Menu(config.menuConfig); - this.on('contextmenu',this._showContextMenu,this); - this.on('beforenodedrop',this._handleDrop,this); - this.on('nodedragover',this._handleDrop,this); - this.on('nodedrop',this._handleDrag,this); - this.on('click',this._saveState,this); - this.on('contextmenu',this._saveState,this); - this.on('click',this._handleClick,this); + MODx.tree.Tree.superclass.constructor.call(this, config); - this.treestate_id = this.config.id || Ext.id(); - this.on('load',this._initExpand,this,{single: true}); - this.on('expandnode',this._saveState,this); - this.on('collapsenode',this._saveState,this); + this.addEvents('afterSort', 'beforeSort', 'refresh'); + this.cm = new Ext.menu.Menu(config.menuConfig); + this.treestate_id = this.config.id || Ext.id(); - /* Absolute positionning fix */ - this.on('expandnode',function () { - var cnt = Ext.getCmp('modx-content'); - if (cnt) { - cnt.doLayout(); - } - },this); - } + this.on({ + load: { + fn: this._initExpand, + single: true + }, + contextmenu: { + fn: function(node, e) { + this._showContextMenu(node, e); + this._saveState(node); + } + }, + click: { + fn: function(node, e) { + this._handleClick(node, e); + this._saveState(node); + }, + scope: this + }, + expandnode: { + fn: function(node) { + // Absolute positionning fix + const contentCmp = Ext.getCmp('modx-content'); + if (contentCmp) { + contentCmp.doLayout(); + } + this._saveState(node); + } + }, + collapsenode: this._saveState, + beforenodedrop: this._handleDrop, + // Note that these next two handler assignments are correct, though unintuitive + nodedrop: this._handleDrag, + nodedragover: this._handleDrop + }); + }, /** * Expand the tree upon initialization. */ - ,_initExpand: function () { - var treeState = Ext.state.Manager.get(this.treestate_id); + _initExpand: function() { + const treeState = Ext.state.Manager.get(this.treestate_id); if (Ext.isEmpty(treeState) && this.root) { this.root.expand(); if (this.root.firstChild && this.config.expandFirst) { this.root.firstChild.expand(); } } else { - for (var i = 0; i < treeState.length; i++) { + for (let i = 0; i < treeState.length; i++) { this.expandPath(treeState[i]); } } - } + }, /** * Add context menu items to the tree. * @param {Object, Array} items Either an Object config or array of Object configs. */ - ,addContextMenuItem: function (items) { - var a = items,l = a.length; - for (var i = 0; i < l; i++) { - a[i].scope = a[i].scope || this; - if (a[i].handler && typeof a[i].handler == 'string') { - a[i].handler = eval(a[i].handler); - } - this.cm.add(a[i]); - } - } + addContextMenuItem: function(items) { + items.forEach(item => { + const action = item; + action.scope = action.scope || this; + if (action.handler && typeof action.handler === 'string') { + // eslint-disable-next-line no-eval + action.handler = eval(action.handler); + } + this.cm.add(action); + }); + }, /** - * + * Iterates visible nodes, adding direct action button(s) and + * setting style on the active node when applicable * * @param node */ - ,prepareNodes: function(node) { - var params = {}; - if (location.search) { - var parts = location.search.substring(1).split('&'); - - for (var i = 0; i < parts.length; i++) { - var nv = parts[i].split('='); - if (!nv[0]) continue; - params[nv[0]] = nv[1] || true; + prepareNodes: function(node) { + let activeFile = null; + if (window.location.search) { + const params = MODx.util.UrlParams.get(); + if (Object.hasOwn(params, 'file')) { + activeFile = encodeURIComponent(params.file); } } - var activeFile = params.file; - - Ext.each(node.childNodes,function (node) { - if (node.attributes.selected || node.id == activeFile) { - //node.select(); - node.ui.addClass('x-tree-selected'); + node.childNodes.forEach(childNode => { + if (childNode.attributes.selected || childNode.id === activeFile) { + childNode.ui.addClass('x-tree-selected'); } - // add the special buttons to node - this.addNodeButtons(node); - }, this); - } + this.addNodeButtons(childNode); + }); + }, /** * Adds direct access buttons to a node. Currently the only added button is @@ -285,78 +309,81 @@ Ext.extend(MODx.tree.Tree,Ext.tree.TreePanel,{ * * @param node */ - ,addNodeButtons: function(node) { - var elId = node.ui.elNode.id + '_tools'; - var el = document.createElement('div'); + addNodeButtons: function(node) { + const + elId = `${node.ui.elNode.id}_tools`, + el = document.createElement('div') + ; el.id = elId; el.className = 'modx-tree-node-btn-create'; if (!node.attributes.pseudoroot && node.ui.hasClass('pnew_modDocument')) { node.ui.elNode.appendChild(el); - var btn = MODx.load({ + MODx.load({ xtype: 'modx-button', text: '', scope: this, tooltip: new Ext.ToolTip({ // TODO if childtemplate property is available, directly use that instead of "document" - title: _('create_document_inside')+" " + node.attributes.text + "" - ,target: this + title: `${_('create_document_inside')} ${node.attributes.text}`, + target: this }), node: node, - handler: function (btn,evt) { - evt.stopPropagation(evt); + handler: function(button, e) { + e.stopPropagation(e); node.getOwnerTree().handleDirectCreateClick(node); }, iconCls: 'icon-plus-circle', renderTo: elId, listeners: { - mouseover: function (button,e) { + mouseover: function(button, e) { button.tooltip.onTargetOver(e); - } - ,mouseout: function (button,e) { + }, + mouseout: function(button, e) { button.tooltip.onTargetOut(e); } } }); } - } + }, /** * Shows the current context menu. * @param {Ext.tree.TreeNode} node The * @param {Ext.EventObject} e The event object run. */ - ,_showContextMenu: function (node,e) { + _showContextMenu: function(node, e) { this.cm.activeNode = node; this.cm.removeAll(); - var m; - var handled = false; - + let + menu, + handled = false + ; if (!Ext.isEmpty(node.attributes.treeHandler) || (node.isRoot && !Ext.isEmpty(node.childNodes[0].attributes.treeHandler))) { - var h = Ext.getCmp(node.isRoot ? node.childNodes[0].attributes.treeHandler : node.attributes.treeHandler); - if (h) { + const handler = Ext.getCmp(node.isRoot ? node.childNodes[0].attributes.treeHandler : node.attributes.treeHandler); + if (handler) { if (node.isRoot) { node.attributes.type = 'root'; } - m = h.getMenu(this,node,e); + menu = handler.getMenu(this, node, e); handled = true; } } if (!handled) { if (this.getMenu) { - m = this.getMenu(node,e); + menu = this.getMenu(node, e); } else if (node.attributes.menu && node.attributes.menu.items) { - m = node.attributes.menu.items; + menu = node.attributes.menu.items; } } - if (m && m.length > 0) { - this.addContextMenuItem(m); + if (menu && menu.length > 0) { + this.addContextMenuItem(menu); this.cm.showAt(e.xy); } e.preventDefault(); e.stopEvent(); - } + }, /** * Checks to see if a node exists in a tree node's children. @@ -364,9 +391,9 @@ Ext.extend(MODx.tree.Tree,Ext.tree.TreePanel,{ * @param {Object} n The node to find. * @return {Boolean} True if the node exists in the parent's children. */ - ,hasNode: function (t,n) { - return (t.findChild('id',n.id)) || (t.leaf === true && t.parentNode.findChild('id',n.id)); - } + hasNode: function(t, n) { + return (t.findChild('id', n.id)) || (t.leaf === true && t.parentNode.findChild('id', n.id)); + }, /** * Refreshes the tree and runs an optional func. @@ -375,318 +402,361 @@ Ext.extend(MODx.tree.Tree,Ext.tree.TreePanel,{ * @param {Array} args An array of arguments to run with. * @return {Boolean} True if successful. */ - ,refresh: function (func,scope,args) { - var treeState = Ext.state.Manager.get(this.treestate_id); + refresh: function(func, scope, args) { + /* + Used in Contexts, Resource Groups + */ + const expandedPaths = Ext.state.Manager.get(this.treestate_id); this.root.reload(); this.fireEvent('refresh', {}); - if (treeState === undefined) { + if (expandedPaths === undefined) { this.root.expand(); - } else { - // Make sure we have a valid state array - if (Ext.isArray(treeState)) { - Ext.each(treeState,function (path,idx) { - this.expandPath(path); - },this); - } + } else if (Array.isArray(expandedPaths)) { + expandedPaths.forEach(path => this.expandPath(path)); } if (func) { scope = scope || this; args = args || []; - this.root.on('load',function () { - Ext.callback(func,scope,args); - },scope); + this.root.on('load', function() { + Ext.callback(func, scope, args); + }, scope); } return true; - } + }, - ,removeChildren: function (node) { + removeChildren: function(node) { while (node.firstChild) { - var c = node.firstChild; - node.removeChild(c); - c.destroy(); + const child = node.firstChild; + node.removeChild(child); + child.destroy(); } - } + }, - ,loadRemoteData: function (data) { + /* + Is this used? Only other place found as of 3.1-dev is + in modx.tree.checkbox.js, where only this same definition + is found; no calls made to this method + + REMOVE? + */ + loadRemoteData: function(data) { this.removeChildren(this.getRootNode()); - for (var c in data) { + for (const c in data) { if (typeof data[c] === 'object') { this.getRootNode().appendChild(data[c]); } } - } + }, - ,reloadNode: function (n) { + reloadNode: function(n) { this.getLoader().load(n); n.expand(); - } + }, /** * Abstracted remove function */ - ,remove: function (text,substr,split) { + remove: function(text, substr, split) { if (this.destroying) { - return MODx.tree.Tree.superclass.remove.apply(this,arguments); - } - var node = this.cm.activeNode; - var id = this._extractId(node.id,substr,split); - var p = {action: this.config.removeAction || 'remove'}; - var pk = this.config.primaryKey || 'id'; - p[pk] = id; + return MODx.tree.Tree.superclass.remove.apply(this, arguments); + } + const + node = this.cm.activeNode, + id = this._extractId(node.id, substr, split), + params = { + action: this.config.removeAction || 'remove' + }, + pk = this.config.primaryKey || 'id' + ; + params[pk] = id; MODx.msg.confirm({ - title: this.config.removeTitle || _('warning') - ,text: _(text) - ,url: this.config.url - ,params: p - ,listeners: { - 'success': {fn: this.refresh,scope: this} + title: this.config.removeTitle || _('warning'), + text: _(text), + url: this.config.url, + params: params, + listeners: { + success: { + fn: this.refresh, + scope: this + } } }); - } + }, - ,_extractId: function (id,substr,split) { + _extractId: function(nodeId, substr, split) { + let id; substr = substr || false; split = split || false; if (substr !== false) { - id = node.id.substr(substr); + id = nodeId.substr(substr); } if (split !== false) { - id = node.id.split('_'); + id = nodeId.split('_'); id = id[split]; } return id; - } + }, /** * Expand the tree and all children. */ - ,expandNodes: function () { + expandNodes: function() { if (this.root) { this.root.expand(); this.root.expandChildNodes(true); } - } + }, /** * Completely collapse the tree. */ - ,collapseNodes: function () { + collapseNodes: function() { if (this.root) { this.root.collapseChildNodes(true); this.root.collapse(); } - } + }, /** * Save the state of the tree's open children. - * @param {Ext.tree.TreeNode} n The most recent expanded or collapsed node. + * @param {Ext.tree.TreeNode} node The most recent expanded or collapsed node. */ - ,_saveState: function (n) { + _saveState: function(node) { if (!this.stateful) { return true; } - var s = Ext.state.Manager.get(this.treestate_id); - var p = n.getPath(); - var i; - if (!Ext.isObject(s) && !Ext.isArray(s)) { - s = [s]; - /* backwards compat */ + const currentPath = node.getPath(); + let treeState = Ext.state.Manager.get(this.treestate_id); + + if (!Ext.isObject(treeState) && !Ext.isArray(treeState)) { + treeState = [treeState]; + // backwards compat } else { - s = s.slice(); - } - if (Ext.isEmpty(p) || p == undefined) return; - /* ignore invalid paths */ - if (n.expanded) { /* if expanding, add to state */ - if (Ext.isString(p) && s.indexOf(p) === -1) { - var f = false; - var sr; - for (i = 0; i < s.length; i++) { - if (s[i] == undefined || typeof s[i] != 'string') { - s.splice(i,1); - continue; - } - sr = s[i].search(p); - if (sr !== -1 && s[sr]) { /* dont add if already in */ - if (s[sr].length > s[i].length) { - f = true; + treeState = treeState.slice(); + } + if (Ext.isEmpty(currentPath) || currentPath === undefined) { + return; + } + if (node.expanded) { + // On expand, add this node's path to state + if (Ext.isString(currentPath) && treeState.indexOf(currentPath) === -1) { + let + found = false, + existingPath + ; + for (let i = 0; i < treeState.length; i++) { + if (treeState[i] === undefined || typeof treeState[i] !== 'string') { + treeState.splice(i, 1); + } else { + existingPath = treeState[i].search(currentPath); + if ( + existingPath !== -1 + && treeState[existingPath] + && treeState[existingPath].length > treeState[i].length + ) { + found = true; } } } - if (!f) { /* if not in, add */ - s.push(p); + if (!found) { + treeState.push(currentPath); } } - } else { /* if collapsing, remove from state */ - s = s.remove(p); - /* remove all children of node */ - for (i = 0; i < s.length; i++) { - if (s[i] == undefined || typeof s[i] != 'string') { - s.splice(i,1); - continue; - } - if (s[i].search(p) !== -1) { - delete s[i]; + } else { + // On collapse, remove path of this node and any of its children from state + treeState = treeState.remove(currentPath); + for (let i = 0; i < treeState.length; i++) { + if (treeState[i] === undefined || typeof treeState[i] !== 'string') { + treeState.splice(i, 1); + } else if (treeState[i].search(currentPath) !== -1) { + delete treeState[i]; } } } - /* clear out undefineds */ - for (i = 0; i < s.length; i++) { - if (s[i] == undefined || typeof s[i] != 'string') { - s.splice(i,1); - continue; + // Remove any remaining invalid paths from state + for (let i = 0; i < treeState.length; i++) { + if (treeState[i] === undefined || typeof treeState[i] !== 'string') { + treeState.splice(i, 1); } } - Ext.state.Manager.set(this.treestate_id,s); - } + Ext.state.Manager.set(this.treestate_id, treeState); + }, /** * Handles tree clicks - * @param {Object} n The node clicked + * @param {Object} node The node clicked * @param {Object} e The event object */ - ,_handleClick: function (n,e) { + _handleClick: function(node, e) { e.stopEvent(); e.preventDefault(); - if (this.disableHref) { return true; } - if (n.attributes.page && n.attributes.page !== '') { - if (e.button == 1) return window.open(n.attributes.page,'_blank'); - else if (e.ctrlKey == 1 || e.metaKey == 1 || e.shiftKey == 1) return window.open(n.attributes.page); - else if (e.target.tagName == 'SPAN') MODx.loadPage(n.attributes.page); // only open the edit page when clicking on the text and nothing else (e.g. icon/empty space) - else if (n.isExpandable()) n.toggle(); // when clicking anything except the node-text, just open (if available) the node - else MODx.loadPage(n.attributes.page); // for non container nodes, they can be edited by clicking anywhere on the node - } else if (n.attributes.type && n.attributes.type === 'dir') { - if (!n.expanded) { - n.toggle(); + if (node.attributes.page && node.attributes.page !== '') { + if (e.button === 1) { + return window.open(node.attributes.page, '_blank'); + } + if (e.ctrlKey === 1 || e.metaKey === 1 || e.shiftKey === 1) { + return window.open(node.attributes.page); } - } else if (n.isExpandable()) { - n.toggle(); + if (e.target.tagName === 'SPAN') { + // only open the edit page when clicking on the text and nothing else (e.g. icon/empty space) + MODx.loadPage(node.attributes.page); + } else if (node.isExpandable()) { + // when clicking anything except the node-text, just open (if available) the node + node.toggle(); + } else { + // for non container nodes, they can be edited by clicking anywhere on the node + MODx.loadPage(node.attributes.page); + } + } else if ( + (node.attributes.type + && node.attributes.type === 'dir' + && !node.expanded) + || node.isExpandable() + ) { + node.toggle(); } return true; - } + }, - ,encode: function (node) { + encode: function(node) { if (!node) { node = this.getRootNode(); } - var _encode = function (node) { - var resultNode = {}; - var kids = node.childNodes; - for (var i = 0; i < kids.length; i = i + 1) { - var n = kids[i]; - resultNode[n.id] = { - id: n.id - ,checked: n.ui.isChecked() - ,type: n.attributes.type || '' - ,data: n.attributes.data || {} - ,children: _encode(n) - }; - } - return resultNode; - }; - var nodes = _encode(node); + const + encodeRecursive = function(currentNode) { + const + resultNode = {}, + { childNodes } = currentNode + ; + childNodes.forEach(childNode => { + resultNode[childNode.id] = { + id: childNode.id, + checked: childNode.ui.isChecked(), + type: childNode.attributes.type || '', + data: childNode.attributes.data || {}, + children: encodeRecursive(childNode) + }; + }); + return resultNode; + }, + nodes = encodeRecursive(node) + ; return Ext.encode(nodes); - } + }, /** * Handles all drag events into the tree. * @param {Object} dropEvent The node dropped on the parent node. */ - ,_handleDrag: function (dropEvent) { - function simplifyNodes(node) { - var resultNode = {}; - var kids = node.childNodes; - var len = kids.length; - for (var i = 0; i < len; i++) { - resultNode[kids[i].id] = simplifyNodes(kids[i]); - } - return resultNode; - } - - var encNodes = Ext.encode(simplifyNodes(dropEvent.tree.root)) - ,source = dropEvent.dropNode; - - this.fireEvent('beforeSort',encNodes); + _handleDrag: function(dropEvent) { + const + simplifyNodes = function(currentNode) { + const + resultNode = {}, + { childNodes } = currentNode + ; + childNodes.forEach(childNode => { + resultNode[childNode.id] = simplifyNodes(childNode); + }); + return resultNode; + }, + encNodes = Ext.encode(simplifyNodes(dropEvent.tree.root)), + source = dropEvent.dropNode + ; + this.fireEvent('beforeSort', encNodes); MODx.Ajax.request({ - url: this.config.url - ,params: { - data: encodeURIComponent(encNodes) - ,action: this.config.sortAction || 'sort' - ,source_pk: source.attributes.pk - ,source_type: source.attributes.type - } - ,listeners: { - 'success': { - fn: function (r) { - var el = dropEvent.dropNode.getUI().getTextEl(); + url: this.config.url, + params: { + data: encodeURIComponent(encNodes), + action: this.config.sortAction || 'sort', + source_pk: source.attributes.pk, + source_type: source.attributes.type + }, + listeners: { + success: { + fn: function(response) { + const el = dropEvent.dropNode.getUI().getTextEl(); if (el) { - if(dropEvent.target.childNodes.length === 1) dropEvent.dropNode.ensureVisible(); - + if (dropEvent.target.childNodes.length === 1) { + dropEvent.dropNode.ensureVisible(); + } Ext.get(el).frame(); } - this.fireEvent('afterSort',{event: dropEvent,result: r}); - },scope: this - } - ,'failure': { - fn: function (r) { - MODx.form.Handler.errorJSON(r); + this.fireEvent('afterSort', { + event: dropEvent, + result: response + }); + }, + scope: this + }, + failure: { + fn: function(response) { + MODx.form.Handler.errorJSON(response); this.refresh(); return false; - },scope: this + }, + scope: this } } }); - } + }, /** * Abstract definition to handle drop events. */ - ,_handleDrop: function (dropEvent) { - var node = dropEvent.dropNode; - if (node.isRoot) return false; - + _handleDrop: function(dropEvent) { + const node = dropEvent.dropNode; + if (node.isRoot) { + return false; + } if (!Ext.isEmpty(node.attributes.treeHandler)) { - var h = Ext.getCmp(node.attributes.treeHandler); - if (h) { - return h.handleDrop(this,dropEvent); + const handler = Ext.getCmp(node.attributes.treeHandler); + if (handler) { + return handler.handleDrop(this, dropEvent); } } - } + }, /** * Semi unique ids across edits * @param {String} prefix Prefix the guid. * @return {String} The newly generated guid. */ - ,_guid: function (prefix) { + _guid: function(prefix) { return prefix + (new Date().getTime()); - } + }, /** * Redirects the page or the content frame to the correct location. * @param {String} loc The URL to direct to. */ - ,redirect: function (loc) { + redirect: function(loc) { MODx.loadPage(loc); - } + }, - ,loadAction: function (p) { - var id = ''; + /** + * Loads a page based on the passed param(s) and an id extracted from the active tree node. + * Currently only Resource/Context related pages are loaded via this method + * @param {String} urlParams The action and other optional params used for loading the page + */ + loadAction: function(urlParams) { + let id = ''; if (this.cm.activeNode && this.cm.activeNode.id) { - var pid = this.cm.activeNode.id.split('_'); - id = 'id=' + pid[1]; + const pid = this.cm.activeNode.id.split('_'); + id = `id=${pid[1]}`; } - MODx.loadPage('?' + id + '&' + p); - } + MODx.loadPage(`?${id}&${urlParams}`); + }, /** * Loads the default toolbar for the tree. * @access private * @see Ext.Toolbar */ - ,_loadToolbar: function () { - } + _loadToolbar: function() { + }, /** * Refreshes a given tree node. @@ -694,100 +764,110 @@ Ext.extend(MODx.tree.Tree,Ext.tree.TreePanel,{ * @param {String} id The ID of the node * @param {Boolean} self If true, will refresh self rather than parent. */ - ,refreshNode: function (id,self) { - var node = this.getNodeById(id); + refreshNode: function(id, self) { + const node = this.getNodeById(id); if (node) { - var n = self ? node : node.parentNode; - this.getLoader().load(n,function () { - n.expand(); - },this); + const targetNode = self ? node : node.parentNode; + this.getLoader().load(targetNode, function() { + targetNode.expand(); + }, this); } - } + }, /** * Refreshes selected active node * @access public */ - ,refreshActiveNode: function () { + refreshActiveNode: function() { if (this.cm.activeNode) { - this.getLoader().load(this.cm.activeNode,this.cm.activeNode.expand); + this.getLoader().load(this.cm.activeNode, this.cm.activeNode.expand); } else { this.refresh(); } - } + }, /** * Refreshes selected active node's parent * @access public */ - ,refreshParentNode: function () { + refreshParentNode: function() { if (this.cm.activeNode) { this.getLoader().load(this.cm.activeNode.parentNode || this.cm.activeNode, this.cm.activeNode.expand); } else { this.refresh(); } - } + }, /** * Removes specified node * @param {String} id The node's ID */ - ,removeNode: function (id) { - var node = this.getNodeById(id); + removeNode: function(id) { + const node = this.getNodeById(id); if (node) { node.remove(); } - } + }, /** * Dynamically removes active node */ - ,removeActiveNode: function () { + removeActiveNode: function() { this.cm.activeNode.remove(); - } + }, /** * Gets a default toolbar setup */ - ,getToolbar: function () { - var iu = MODx.config.manager_url + 'templates/default/images/restyle/icons/'; + getToolbar: function() { + const iu = `${MODx.config.manager_url}templates/default/images/restyle/icons/`; return [{ - icon: iu + 'arrow_down.png' - ,cls: 'x-btn-icon arrow_down' - ,tooltip: {text: _('tree_expand')} - ,handler: this.expandNodes - ,scope: this - },{ - icon: iu + 'arrow_up.png' - ,cls: 'x-btn-icon arrow_up' - ,tooltip: {text: _('tree_collapse')} - ,handler: this.collapseNodes - ,scope: this - },{ - icon: iu + 'refresh.png' - ,cls: 'x-btn-icon refresh' - ,tooltip: {text: _('tree_refresh')} - ,handler: this.refresh - ,scope: this + icon: `${iu}arrow_down.png`, + cls: 'x-btn-icon arrow_down', + tooltip: { + text: _('tree_expand') + }, + handler: this.expandNodes, + scope: this + }, { + icon: `${iu}arrow_up.png`, + cls: 'x-btn-icon arrow_up', + tooltip: { + text: _('tree_collapse') + }, + handler: this.collapseNodes, + scope: this + }, { + icon: `${iu}refresh.png`, + cls: 'x-btn-icon refresh', + tooltip: { + text: _('tree_refresh') + }, + handler: this.refresh, + scope: this }]; - } + }, /** * Add Items to the toolbar. + * @param {Object} a Contains the tools config + * @todo Consider removal, as this appears to not be used in the core install; is only + * used for remote toolbars which do not seem to be a part of MODX 3.x */ - ,_formatToolbar: function (a) { - var l = a.length; - for (var i = 0; i < l; i++) { + _formatToolbar: function(a) { + const l = a.length; + for (let i = 0; i < l; i++) { if (a[i].handler) { + // eslint-disable-next-line no-eval a[i].handler = eval(a[i].handler); } - Ext.applyIf(a[i],{ - scope: this - ,cls: this.config.toolbarItemCls || 'x-btn-icon' + Ext.applyIf(a[i], { + scope: this, + cls: this.config.toolbarItemCls || 'x-btn-icon' }); } return a; - } + }, /** * Allow pseudoroot actions @@ -795,86 +875,78 @@ Ext.extend(MODx.tree.Tree,Ext.tree.TreePanel,{ * @param parent {Ext.tree.TreeNode} Parent node * @param node {Ext.tree.TreeNode} Node to be inserted */ - ,_onAppend: function (tree,parent,node) { + _onAppend: function(tree, parent, node) { if (node.attributes.pseudoroot) { - - setTimeout((function (tree) { - return function () { - - var elId = node.ui.elNode.id + '_tools'; - var el = document.createElement('div'); + setTimeout((function() { + return function() { + let btn; + const + elId = `${node.ui.elNode.id}_tools`, + el = document.createElement('div') + ; el.id = elId; el.className = 'modx-tree-node-tool-ct'; node.ui.elNode.appendChild(el); - var inlineButtonsLang = tree.getInlineButtonsLang(node); + const inlineButtonsLang = tree.getInlineButtonsLang(node); - var btn = MODx.load({ + btn = MODx.load({ xtype: 'modx-button', text: '', scope: this, tooltip: new Ext.ToolTip({ - title: inlineButtonsLang.add - ,target: this + title: inlineButtonsLang.add, + target: this }), node: node, - handler: function (btn,evt) { + handler: function(cmp, evt) { evt.stopPropagation(evt); node.getOwnerTree().handleCreateClick(node); }, iconCls: 'icon-plus-circle', renderTo: elId, listeners: { - mouseover: function (button,e) { + mouseover: function(button, e) { button.tooltip.onTargetOver(e); - } - ,mouseout: function (button,e) { + }, + mouseout: function(button, e) { button.tooltip.onTargetOut(e); } } }); - var btn = MODx.load({ + btn = MODx.load({ xtype: 'modx-button', text: '', scope: this, tooltip: new Ext.ToolTip({ - title: inlineButtonsLang.refresh - ,target: this + title: inlineButtonsLang.refresh, + target: this }), node: node, - handler: function (btn,evt) { + handler: function(cmp, evt) { evt.stopPropagation(evt); node.reload(); }, iconCls: 'icon-refresh', renderTo: elId, listeners: { - mouseover: function (button,e) { + mouseover: function(button, e) { button.tooltip.onTargetOver(e); - } - ,mouseout: function (button,e) { + }, + mouseout: function(button, e) { button.tooltip.onTargetOut(e); } } }); - - + /** @todo What is the use of this array? Don't see it utilized anywhere. Remove? */ window.BTNS.push(btn); - - - } - }(this)),200); - + }; + }(this)), 200); return false; - - var btn = document.createElement('div'); - btn.innerHTML = 'H'; - - node.el.appendChild(btn); } - } + }, /** * Handled inline add button click @@ -882,41 +954,42 @@ Ext.extend(MODx.tree.Tree,Ext.tree.TreePanel,{ * * @param Ext.tree.AsyncTreeNode node */ - ,handleCreateClick: function (node) { - } - - ,getInlineButtonsLang: function (node) { - var langs = {}; - if (node.id != undefined) { - var type = node.id.substr(2).split('_'); - if (type[0] == 'type') { - langs.add = _('new_' + type[1]); - } else if (type[0] == 'category') { - langs.add = _('new_' + type[0]); + handleCreateClick: function(node) { + }, + + getInlineButtonsLang: function(node) { + const langs = {}; + if (node.id !== undefined) { + const type = node.id.substr(2).split('_'); + if (type[0] === 'type') { + langs.add = _(`new_${type[1]}`); + } else if (type[0] === 'category') { + langs.add = _(`new_${type[0]}`); } else { langs.add = _('new_document'); } } - langs.refresh = _('ext_refresh'); return langs; - } + }, - ,expandTreePath(dir = '/') { - const root = this.getRootNode().getPath('text') - const path = root.replace(/\/$/, '') + '/' + dir.replace(/^\//, '') + expandTreePath: function(dir = '/') { + const + root = this.getRootNode().getPath('text'), + path = `${root.replace(/\/$/, '')}/${dir.replace(/^\//, '')}` + ; this.expandPath(path, 'text', () => { - let node = this.getNodeById(encodeURIComponent(dir)) + let node = this.getNodeById(encodeURIComponent(dir)); if (!node) { - node = this.getRootNode() + node = this.getRootNode(); } - node.select() - this.cm.activeNode = node - }) + node.select(); + this.cm.activeNode = node; + }); } }); -Ext.reg('modx-tree',MODx.tree.Tree); - +Ext.reg('modx-tree', MODx.tree.Tree); +/** @todo What is the use of this array? Remove? */ window.BTNS = []; diff --git a/manager/assets/modext/widgets/security/modx.grid.access.policy.js b/manager/assets/modext/widgets/security/modx.grid.access.policy.js index 4631ca43d44..bac37187755 100644 --- a/manager/assets/modext/widgets/security/modx.grid.access.policy.js +++ b/manager/assets/modext/widgets/security/modx.grid.access.policy.js @@ -7,30 +7,30 @@ * @xtype modx-panel-access-policies */ MODx.panel.AccessPolicies = function(config = {}) { - Ext.applyIf(config,{ - id: 'modx-panel-access-policies' - ,bodyStyle: '' - ,defaults: { collapsible: false, autoHeight: true } - ,items: [{ - html: _('policies') - ,id: 'modx-policies-header' - ,xtype: 'modx-header' - },{ - layout: 'form' - ,cls: 'main-wrapper' - ,items: [{ - html: '

        '+_('policy_management_msg')+'

        ' - ,border: false - },{ - xtype: 'modx-grid-access-policy' - ,preventRender: true + Ext.applyIf(config, { + id: 'modx-panel-access-policies', + bodyStyle: '', + defaults: { collapsible: false, autoHeight: true }, + items: [{ + html: _('policies'), + id: 'modx-policies-header', + xtype: 'modx-header' + }, { + layout: 'form', + cls: 'main-wrapper', + items: [{ + html: `

        ${_('policy_management_msg')}

        `, + border: false + }, { + xtype: 'modx-grid-access-policy', + preventRender: true }] }] }); - MODx.panel.AccessPolicies.superclass.constructor.call(this,config); + MODx.panel.AccessPolicies.superclass.constructor.call(this, config); }; -Ext.extend(MODx.panel.AccessPolicies,MODx.FormPanel); -Ext.reg('modx-panel-access-policies',MODx.panel.AccessPolicies); +Ext.extend(MODx.panel.AccessPolicies, MODx.FormPanel); +Ext.reg('modx-panel-access-policies', MODx.panel.AccessPolicies); /** * Loads a grid of modAccessPolicies. @@ -43,141 +43,291 @@ Ext.reg('modx-panel-access-policies',MODx.panel.AccessPolicies); MODx.grid.AccessPolicy = function(config = {}) { const queryValue = this.applyRequestFilter(2, 'query', 'tab', true); this.sm = new Ext.grid.CheckboxSelectionModel(); - Ext.applyIf(config,{ - id: 'modx-grid-access-policy' - ,url: MODx.config.connector_url - ,baseParams: { + Ext.applyIf(config, { + id: 'modx-grid-access-policy', + url: MODx.config.connector_url, + baseParams: { action: 'Security/Access/Policy/GetList' - } - ,fields: [ + }, + fields: [ 'id', 'name', 'description', 'description_trans', - 'class', - 'data', 'parent', 'template', 'template_name', 'active_permissions', 'total_permissions', 'active_of', - 'cls' - ] - ,paging: true - ,autosave: true - ,save_action: 'Security/Access/Policy/UpdateFromGrid' - ,remoteSort: true - ,sm: this.sm - ,columns: [this.sm,{ - header: _('policy_name') - ,dataIndex: 'name' - ,width: 200 - ,editor: { xtype: 'textfield' ,allowBlank: false } - ,sortable: true - ,renderer: { fn: function(v,md,record) { - return this.renderLink(v, { - href: '?a=security/access/policy/update&id=' + record.data.id - }); - }, scope: this } - },{ - header: _('description') - ,dataIndex: 'description' - ,width: 375 - ,renderer: function(value, metaData, record) { - return Ext.util.Format.htmlEncode(record['data']['description_trans']); + 'creator' + ], + paging: true, + autosave: true, + save_action: 'Security/Access/Policy/UpdateFromGrid', + remoteSort: true, + sm: this.sm, + columns: [this.sm, { + header: _('policy_name'), + dataIndex: 'name', + id: 'modx-policy--name', + width: 200, + editor: { + xtype: 'textfield', + allowBlank: false + }, + sortable: true, + renderer: { + fn: function(value, metaData, record) { + const renderValue = MODx.util.Render.translatedValue('name', record.json); + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return this.userCanEditRecord(record) + ? this.renderLink(renderValue, { + href: `?a=security/access/policy/update&id=${record.data.id}`, + title: _('policy_edit') + }) + : Ext.util.Format.htmlEncode(renderValue) + ; + }, + scope: this } - ,editable: false - },{ - header: _('policy_template') - ,dataIndex: 'template_name' - ,width: 375 - ,renderer: { fn: function(v,md,record) { - return this.renderLink(v, { - href: '?a=security/access/policy/template/update&id=' + record.data.template - ,target: '_blank' - }); - }, scope: this } - },{ - header: _('active_permissions') - ,dataIndex: 'active_of' - ,width: 100 - ,editable: false - }] - ,tbar: [ + }, { + header: _('description'), + dataIndex: 'description', + id: 'modx-policy--description', + width: 375, + editor: { + xtype: 'textarea' + }, + renderer: { + fn: function(value, metaData, record) { + const renderValue = MODx.util.Render.translatedValue('description', record.json); + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return Ext.util.Format.htmlEncode(renderValue); + }, + scope: this + } + }, { + header: _('policy_template'), + dataIndex: 'template_name', + width: 375, + renderer: { + fn: function(value, metaData, record) { + const + renderValue = MODx.util.Render.translatedValue('template_name', record.json, false), + objPermissions = record.json.permissions + ; + // console.log('tpl record', record); + return !Ext.isEmpty(objPermissions) && objPermissions.updateTemplate === true + ? this.renderLink(renderValue, { + href: `?a=security/access/policy/template/update&id=${record.data.template}`, + title: _('policy_template_edit'), + target: '_blank' + }) + : Ext.util.Format.htmlEncode(renderValue) + ; + }, + scope: this + } + }, + this.getCreatorColumnConfig('policy'), + { + header: _('active_permissions'), + dataIndex: 'active_of', + width: 100, + editable: false + }], + tbar: [ { - text: _('create') - ,cls:'primary-button' - ,scope: this - ,handler: this.createPolicy - },{ - text: _('import') - ,scope: this - ,handler: this.importPolicy - },{ - text: _('bulk_actions') - ,menu: [{ - text: _('selected_remove') - ,handler: this.removeSelected - ,scope: this - }] + text: _('create'), + cls: 'primary-button', + scope: this, + handler: this.createPolicy + }, { + text: _('import'), + scope: this, + handler: this.importPolicy + }, { + text: _('bulk_actions'), + menu: [{ + text: _('selected_remove'), + itemId: 'modx-bulk-menu-opt-remove', + handler: this.removeSelected.createDelegate(this, ['policy', 'Security/Access/Policy/RemoveMultiple', 'int']), + scope: this + }], + listeners: { + render: { + fn: function(btn) { + if (!this.userCanDelete) { + btn.hide(); + } + }, + scope: this + }, + click: { + fn: function(btn) { + const + removableItems = this.getRemovableItemsFromSelection('int'), + menuOptRemove = btn.menu.getComponent('modx-bulk-menu-opt-remove') + ; + if (removableItems.length === 0) { + menuOptRemove.disable(); + } else { + menuOptRemove.enable(); + } + }, + scope: this + } + } }, '->', this.getQueryFilterField(`filter-query-policy:${queryValue}`), this.getClearFiltersButton('filter-query-policy') - ] + ], + viewConfig: { + forceFit: true, + scrollOffset: 0, + getRowClass: function(record, index, rowParams, store) { + // Adds the returned class to the row container's css classes + if (this.grid.userCanDeleteRecord(record)) { + return ''; + } + const rowClasses = 'disable-selection'; + return record.json.isProtected ? `modx-protected-row ${rowClasses}` : rowClasses ; + } + } + }); + MODx.grid.AccessPolicy.superclass.constructor.call(this, config); + + this.gridMenuActions = ['edit', 'delete', 'duplicate', 'export']; + + this.setUserCanEdit(['policy_save', 'policy_edit']); + this.setUserCanCreate(['policy_save', 'policy_new']); + this.setUserCanDelete(['policy_delete']); + this.setShowActionsMenu(); + + this.on({ + render: function(grid) { + this.setEditableColumnAccess( + ['modx-policy--name', 'modx-policy--description'] + ); + }, + beforeedit: function(e) { + if (e.record.json.isProtected || !this.userCanEditRecord(e.record)) { + return false; + } + }, + afteredit: function(e) { + this.refresh(); + } }); - MODx.grid.AccessPolicy.superclass.constructor.call(this,config); }; -Ext.extend(MODx.grid.AccessPolicy,MODx.grid.Grid,{ - editPolicy: function(itm,e) { - MODx.loadPage('security/access/policy/update', 'id='+this.menu.record.id); - } +Ext.extend(MODx.grid.AccessPolicy, MODx.grid.Grid, { + getMenu: function() { + const + record = this.getSelectionModel().getSelected(), + menu = [] + ; + if (this.getSelectionModel().getCount() > 1) { + menu.push({ + text: _('selected_remove'), + handler: this.removeSelected + }); + } else { + if (this.userCanEdit && this.userCanEditRecord(record)) { + menu.push({ + text: _('edit'), + handler: this.editPolicy + }); + } + if (this.userCanCreate && this.userCanDuplicateRecord(record)) { + menu.push({ + text: _('duplicate'), + handler: this.confirm.createDelegate(this, ['Security/Access/Policy/Duplicate', 'policy_duplicate_confirm']) + }); + } + if (menu.length > 0) { + menu.push('-'); + } + menu.push({ + text: _('export'), + handler: this.exportPolicy + }); + if (this.userCanDelete && this.userCanDeleteRecord(record)) { + if (menu.length > 0) { + menu.push('-'); + } + menu.push({ + text: _('delete'), + handler: this.confirm.createDelegate(this, ['Security/Access/Policy/Remove', 'policy_remove_confirm']) + }); + } + } - ,createPolicy: function(btn,e) { - var r = this.menu.record; + if (menu.length > 0) { + this.addContextMenuItem(menu); + } + }, + + editPolicy: function(itm, e) { + MODx.loadPage('security/access/policy/update', `id=${this.menu.record.id}`); + }, + + createPolicy: function(btn, e) { + const r = this.menu.record; if (!this.windows.apc) { this.windows.apc = MODx.load({ - xtype: 'modx-window-access-policy-create' - ,record: r - ,plugin: this.config.plugin - ,listeners: { - 'success': {fn:function(r) { - this.refresh(); - },scope:this} + xtype: 'modx-window-access-policy-create', + record: r, + plugin: this.config.plugin, + listeners: { + success: { + fn: function() { + this.refresh(); + }, + scope: this + } } }); } this.windows.apc.reset(); this.windows.apc.show(e.target); - } + }, - ,exportPolicy: function(btn,e) { - var id = this.menu.record.id; + exportPolicy: function(btn, e) { + const { id } = this.menu.record; MODx.Ajax.request({ - url: this.config.url - ,params: { - action: 'Security/Access/Policy/Export' - ,id: id - } - ,listeners: { - 'success': {fn:function(r) { - location.href = this.config.url+'?action=Security/Access/Policy/Export&download=1&id='+id+'&HTTP_MODAUTH='+MODx.siteId; - },scope:this} + url: this.config.url, + params: { + action: 'Security/Access/Policy/Export', + id: id + }, + listeners: { + success: { + fn: function(r) { + window.location.href = `${this.config.url}?action=Security/Access/Policy/Export&download=1&id=${id}&HTTP_MODAUTH=${MODx.siteId}`; + }, + scope: this + } } }); - } + }, - ,importPolicy: function(btn,e) { - var r = {}; + importPolicy: function(btn, e) { + const r = {}; if (!this.windows.importPolicy) { this.windows.importPolicy = MODx.load({ - xtype: 'modx-window-policy-import' - ,record: r - ,listeners: { - 'success': {fn:function(o) { - this.refresh(); - },scope:this} + xtype: 'modx-window-policy-import', + record: r, + listeners: { + success: { + fn: function(o) { + this.refresh(); + }, + scope: this + } } }); } @@ -185,70 +335,8 @@ Ext.extend(MODx.grid.AccessPolicy,MODx.grid.Grid,{ this.windows.importPolicy.setValues(r); this.windows.importPolicy.show(e.target); } - - ,getMenu: function() { - var r = this.getSelectionModel().getSelected(); - var p = r.data.cls; - - var m = []; - if (this.getSelectionModel().getCount() > 1) { - m.push({ - text: _('selected_remove') - ,handler: this.removeSelected - }); - } else { - if (p.indexOf('pedit') != -1) { - m.push({ - text: _('edit') - ,handler: this.editPolicy - }); - m.push({ - text: _('duplicate') - ,handler: this.confirm.createDelegate(this,["Security/Access/Policy/Duplicate","policy_duplicate_confirm"]) - }); - } - if (m.length > 0) { m.push('-'); } - m.push({ - text: _('export') - ,handler: this.exportPolicy - }); - if (p.indexOf('premove') != -1) { - if (m.length > 0) m.push('-'); - m.push({ - text: _('delete') - ,handler: this.confirm.createDelegate(this,["Security/Access/Policy/Remove","policy_remove_confirm"]) - }); - } - } - - if (m.length > 0) { - this.addContextMenuItem(m); - } - } - - ,removeSelected: function() { - var cs = this.getSelectedAsList(); - if (cs === false) return false; - - MODx.msg.confirm({ - title: _('selected_remove') - ,text: _('policy_remove_multiple_confirm') - ,url: this.config.url - ,params: { - action: 'Security/Access/Policy/RemoveMultiple' - ,policies: cs - } - ,listeners: { - 'success': {fn:function(r) { - this.getSelectionModel().clearSelections(true); - this.refresh(); - },scope:this} - } - }); - return true; - } }); -Ext.reg('modx-grid-access-policy',MODx.grid.AccessPolicy); +Ext.reg('modx-grid-access-policy', MODx.grid.AccessPolicy); /** * Generates a window for creating Access Policies. @@ -259,65 +347,60 @@ Ext.reg('modx-grid-access-policy',MODx.grid.AccessPolicy); * @xtype modx-window-access-policy-create */ MODx.window.CreateAccessPolicy = function(config = {}) { - this.ident = config.ident || 'cacp'+Ext.id(); - Ext.applyIf(config,{ - title: _('create') - ,url: MODx.config.connector_url - ,action: 'Security/Access/Policy/Create' - ,fields: [{ - fieldLabel: _('name') - ,description: MODx.expandHelp ? '' : _('policy_desc_name') - ,name: 'name' - ,id: 'modx-'+this.ident+'-name' - ,xtype: 'textfield' - ,allowBlank: false - ,anchor: '100%' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-'+this.ident+'-name' - ,html: _('policy_desc_name') - ,cls: 'desc-under' - },{ - fieldLabel: _('policy_template') - ,description: MODx.expandHelp ? '' : _('policy_desc_template') - ,name: 'template' - ,hiddenName: 'template' - ,id: 'modx-'+this.ident+'-template' - ,xtype: 'modx-combo-access-policy-template' - ,anchor: '100%' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-'+this.ident+'-template' - ,html: _('policy_desc_template') - ,cls: 'desc-under' - },{ - fieldLabel: _('description') - ,description: MODx.expandHelp ? '' : _('policy_desc_description') - ,name: 'description' - ,id: 'modx-'+this.ident+'-description' - ,xtype: 'textarea' - ,anchor: '100%' - ,height: 50 - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-'+this.ident+'-description' - ,html: _('policy_desc_description') - ,cls: 'desc-under' - },{ - name: 'class' - ,id: 'modx-'+this.ident+'-class' - ,xtype: 'hidden' - },{ - name: 'id' - ,id: 'modx-'+this.ident+'-id' - ,xtype: 'hidden' - }] - ,keys: [] + this.ident = config.ident || `window--create-policy-${Ext.id()}`; + Ext.applyIf(config, { + title: _('create'), + url: MODx.config.connector_url, + action: 'Security/Access/Policy/Create', + fields: [{ + fieldLabel: _('name'), + description: MODx.expandHelp ? '' : _('policy_desc_name'), + name: 'name', + xtype: 'textfield', + allowBlank: false, + anchor: '100%' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('policy_desc_name'), + cls: 'desc-under' + }, { + fieldLabel: _('policy_template'), + description: MODx.expandHelp ? '' : _('policy_desc_template'), + name: 'template', + hiddenName: 'template', + xtype: 'modx-combo-access-policy-template', + anchor: '100%' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('policy_desc_template'), + cls: 'desc-under' + }, { + fieldLabel: _('description'), + description: MODx.expandHelp ? '' : _('policy_desc_description'), + name: 'description', + xtype: 'textarea', + anchor: '100%', + height: 50 + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('policy_desc_description'), + cls: 'desc-under' + }, { + name: 'class', + xtype: 'hidden' + }, { + name: 'id', + xtype: 'hidden' + }], + keys: [] }); - MODx.window.CreateAccessPolicy.superclass.constructor.call(this,config); + MODx.window.CreateAccessPolicy.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.CreateAccessPolicy,MODx.Window); -Ext.reg('modx-window-access-policy-create',MODx.window.CreateAccessPolicy); +Ext.extend(MODx.window.CreateAccessPolicy, MODx.Window); +Ext.reg('modx-window-access-policy-create', MODx.window.CreateAccessPolicy); /** * @class MODx.window.AccessPolicyTemplate @@ -326,26 +409,28 @@ Ext.reg('modx-window-access-policy-create',MODx.window.CreateAccessPolicy); * @xtype modx-combo-access-policy-template */ MODx.combo.AccessPolicyTemplate = function(config = {}) { - Ext.applyIf(config,{ - name: 'template' - ,hiddenName: 'template' - ,fields: ['id','name','description','description_trans'] - ,forceSelection: true - ,typeAhead: false - ,editable: false - ,allowBlank: false - ,pageSize: 20 - ,url: MODx.config.connector_url - ,baseParams: { + Ext.applyIf(config, { + name: 'template', + hiddenName: 'template', + fields: ['id', 'name', 'description', 'description_trans'], + forceSelection: true, + typeAhead: false, + editable: false, + allowBlank: false, + pageSize: 20, + url: MODx.config.connector_url, + baseParams: { action: 'Security/Access/Policy/Template/GetList' - } - ,tpl: new Ext.XTemplate('
        {name:htmlEncode}' - ,'

        {description_trans:htmlEncode}

        ') + }, + tpl: new Ext.XTemplate( + '
        {name:htmlEncode}', + '

        {description_trans:htmlEncode}

        ' + ) }); - MODx.combo.AccessPolicyTemplate.superclass.constructor.call(this,config); + MODx.combo.AccessPolicyTemplate.superclass.constructor.call(this, config); }; -Ext.extend(MODx.combo.AccessPolicyTemplate,MODx.combo.ComboBox); -Ext.reg('modx-combo-access-policy-template',MODx.combo.AccessPolicyTemplate); +Ext.extend(MODx.combo.AccessPolicyTemplate, MODx.combo.ComboBox); +Ext.reg('modx-combo-access-policy-template', MODx.combo.AccessPolicyTemplate); /** * @class MODx.window.ImportPolicy @@ -354,29 +439,27 @@ Ext.reg('modx-combo-access-policy-template',MODx.combo.AccessPolicyTemplate); * @xtype modx-window-policy-import */ MODx.window.ImportPolicy = function(config = {}) { - this.ident = config.ident || 'imppol-'+Ext.id(); - Ext.applyIf(config,{ - title: _('import') - ,id: 'modx-window-policy-import' - ,url: MODx.config.connector_url - ,action: 'Security/Access/Policy/Import' - ,fileUpload: true - ,saveBtnText: _('import') - ,fields: [{ - html: _('policy_import_msg') - ,id: this.ident+'-desc' - ,xtype: 'modx-description' - ,style: 'margin-bottom: 10px;' - },{ - xtype: 'fileuploadfield' - ,fieldLabel: _('file') - ,buttonText: _('upload.buttons.upload') - ,name: 'file' - ,id: this.ident+'-file' - ,anchor: '100%' + this.ident = config.ident || `window--import-policy-${Ext.id()}`; + Ext.applyIf(config, { + title: _('import'), + id: 'modx-window-policy-import', + url: MODx.config.connector_url, + action: 'Security/Access/Policy/Import', + fileUpload: true, + saveBtnText: _('import'), + fields: [{ + html: _('policy_import_msg'), + xtype: 'modx-description', + style: 'margin-bottom: 10px;' + }, { + xtype: 'fileuploadfield', + fieldLabel: _('file'), + buttonText: _('upload.buttons.upload'), + name: 'file', + anchor: '100%' }] }); - MODx.window.ImportPolicy.superclass.constructor.call(this,config); + MODx.window.ImportPolicy.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.ImportPolicy,MODx.Window); -Ext.reg('modx-window-policy-import',MODx.window.ImportPolicy); +Ext.extend(MODx.window.ImportPolicy, MODx.Window); +Ext.reg('modx-window-policy-import', MODx.window.ImportPolicy); diff --git a/manager/assets/modext/widgets/security/modx.grid.access.policy.template.js b/manager/assets/modext/widgets/security/modx.grid.access.policy.template.js index 8fee8c4f595..2046ece194a 100644 --- a/manager/assets/modext/widgets/security/modx.grid.access.policy.template.js +++ b/manager/assets/modext/widgets/security/modx.grid.access.policy.template.js @@ -7,30 +7,30 @@ * @xtype modx-panel-access-policy-templates */ MODx.panel.AccessPolicyTemplates = function(config = {}) { - Ext.applyIf(config,{ - id: 'modx-panel-access-policy-templates' - ,bodyStyle: '' - ,defaults: { collapsible: false ,autoHeight: true } - ,items: [{ - html: _('policies') - ,id: 'modx-policy-templates-header' - ,xtype: 'modx-header' - },{ - layout: 'form' - ,bodyStyle: 'padding: 15px' - ,items: [{ - html: '

        '+_('policy_templates.intro_msg')+'

        ' - ,border: false - },{ - xtype: 'modx-grid-access-policy-templates' - ,preventRender: true + Ext.applyIf(config, { + id: 'modx-panel-access-policy-templates', + bodyStyle: '', + defaults: { collapsible: false, autoHeight: true }, + items: [{ + html: _('policies'), + id: 'modx-policy-templates-header', + xtype: 'modx-header' + }, { + layout: 'form', + bodyStyle: 'padding: 15px', + items: [{ + html: `

        ${_('policy_templates.intro_msg')}

        `, + border: false + }, { + xtype: 'modx-grid-access-policy-templates', + preventRender: true }] }] }); - MODx.panel.AccessPolicyTemplates.superclass.constructor.call(this,config); + MODx.panel.AccessPolicyTemplates.superclass.constructor.call(this, config); }; -Ext.extend(MODx.panel.AccessPolicyTemplates,MODx.FormPanel); -Ext.reg('modx-panel-access-policy-templates',MODx.panel.AccessPolicyTemplates); +Ext.extend(MODx.panel.AccessPolicyTemplates, MODx.FormPanel); +Ext.reg('modx-panel-access-policy-templates', MODx.panel.AccessPolicyTemplates); /** * Loads a grid of modAccessPolicyTemplates. @@ -43,13 +43,13 @@ Ext.reg('modx-panel-access-policy-templates',MODx.panel.AccessPolicyTemplates); MODx.grid.AccessPolicyTemplate = function(config = {}) { const queryValue = this.applyRequestFilter(3, 'query', 'tab', true); this.sm = new Ext.grid.CheckboxSelectionModel(); - Ext.applyIf(config,{ - id: 'modx-grid-access-policy-template' - ,url: MODx.config.connector_url - ,baseParams: { + Ext.applyIf(config, { + id: 'modx-grid-access-policy-template', + url: MODx.config.connector_url, + baseParams: { action: 'Security/Access/Policy/Template/GetList' - } - ,fields: [ + }, + fields: [ 'id', 'name', 'description', @@ -58,214 +58,353 @@ MODx.grid.AccessPolicyTemplate = function(config = {}) { 'template_group_name', 'total_permissions', 'policy_count', - 'cls' - ] - ,paging: true - ,autosave: true - ,save_action: 'Security/Access/Policy/Template/UpdateFromGrid' - ,remoteSort: true - ,sm: this.sm - ,columns: [this.sm,{ - header: _('name') - ,dataIndex: 'name' - ,width: 200 - ,editor: { xtype: 'textfield' ,allowBlank: false } - ,sortable: true - ,renderer: { fn: function(v,md,record) { - return this.renderLink(v, { - href: '?a=security/access/policy/template/update&id=' + record.data.id - }); - }, scope: this } - },{ - header: _('description') - ,dataIndex: 'description' - ,width: 375 - ,editable: false - ,renderer: function(value, metaData, record) { - return Ext.util.Format.htmlEncode(record['data']['description_trans']); + 'creator' + ], + paging: true, + autosave: true, + save_action: 'Security/Access/Policy/Template/UpdateFromGrid', + remoteSort: true, + sm: this.sm, + columns: [this.sm, { + header: _('name'), + dataIndex: 'name', + id: 'modx-policy-template--name', + width: 200, + editor: { + xtype: 'textfield', + allowBlank: false + }, + sortable: true, + renderer: { + fn: function(value, metaData, record) { + const renderValue = MODx.util.Render.translatedValue('name', record.json); + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return this.userCanEditRecord(record) + ? this.renderLink(renderValue, { + href: `?a=security/access/policy/template/update&id=${record.data.id}`, + title: _('policy_template_edit') + }) + : Ext.util.Format.htmlEncode(renderValue) + ; + }, + scope: this } - ,sortable: true - },{ - header: _('template_group') - ,dataIndex: 'template_group_name' - ,width: 375 - ,sortable: true - },{ - header: _('policy_count') - ,dataIndex: 'policy_count' - ,width: 100 - ,editable: false - ,sortable: true - },{ - header: _('permissions') - ,dataIndex: 'total_permissions' - ,width: 100 - ,editable: false - ,sortable: true - }] - ,tbar: [ + }, { + header: _('description'), + dataIndex: 'description', + id: 'modx-policy-template--description', + width: 375, + editor: { + xtype: 'textarea' + }, + renderer: { + fn: function(value, metaData, record) { + const renderValue = MODx.util.Render.translatedValue('description', record.json); + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return Ext.util.Format.htmlEncode(renderValue); + }, + scope: this + } + }, { + header: _('template_group'), + dataIndex: 'template_group_name', + width: 375, + sortable: true, + renderer: { + fn: function(value, metaData, record) { + const + renderValue = MODx.util.Render.translatedValue('template_group_name', record.json, false) + ; + // console.log('tpl record', record); + return Ext.util.Format.htmlEncode(renderValue); + }, + scope: this + } + }, { + header: _('policy_count'), + dataIndex: 'policy_count', + width: 100, + editable: false, + sortable: true + }, { + header: _('permissions'), + dataIndex: 'total_permissions', + width: 100, + editable: false, + sortable: true + }, + this.getCreatorColumnConfig('policy-template') + ], + tbar: [ { - text: _('create') - ,cls:'primary-button' - ,scope: this - ,handler: this.createPolicyTemplate - },{ - text: _('import') - ,scope: this - ,handler: this.importPolicyTemplate - },{ - text: _('bulk_actions') - ,menu: [{ - text: _('selected_remove') - ,handler: this.removeSelected - ,scope: this - }] + text: _('create'), + cls: 'primary-button', + scope: this, + handler: this.createPolicyTemplate + }, { + text: _('import'), + scope: this, + handler: this.importPolicyTemplate + }, { + text: _('bulk_actions'), + menu: [{ + text: _('selected_remove'), + itemId: 'modx-bulk-menu-opt-remove', + handler: this.removeSelected, + scope: this + }], + listeners: { + render: { + fn: function(btn) { + if (!this.userCanDelete) { + btn.hide(); + } + }, + scope: this + }, + click: { + fn: function(btn) { + const + removableItems = this.getRemovableItemsFromSelection('int'), + menuOptRemove = btn.menu.getComponent('modx-bulk-menu-opt-remove') + ; + if (removableItems.length === 0) { + menuOptRemove.disable(); + } else { + menuOptRemove.enable(); + } + }, + scope: this + } + } }, '->', this.getQueryFilterField(`filter-query-policy-template:${queryValue}`), this.getClearFiltersButton('filter-query-policy-template') - ] + ], + viewConfig: { + forceFit: true, + scrollOffset: 0, + getRowClass: function(record, index, rowParams, store) { + // Adds the returned class to the row container's css classes + if (this.grid.userCanDeleteRecord(record)) { + return ''; + } + const rowClasses = 'disable-selection'; + return record.json.isProtected ? `modx-protected-row ${rowClasses}` : rowClasses ; + } + } + }); + MODx.grid.AccessPolicyTemplate.superclass.constructor.call(this, config); + + this.gridMenuActions = ['edit', 'delete', 'duplicate', 'export']; + + this.setUserCanEdit(['policy_template_save', 'policy_template_edit']); + this.setUserCanCreate(['policy_template_save', 'policy_template_new']); + this.setUserCanDelete(['policy_template_delete']); + this.setShowActionsMenu(); + + this.on({ + render: function(grid) { + this.setEditableColumnAccess( + ['modx-policy-template--name', 'modx-policy-template--description'] + ); + }, + beforeedit: function(e) { + if (e.record.json.isProtected || !this.userCanEditRecord(e.record)) { + return false; + } + }, + afteredit: function(e) { + this.refresh(); + } }); - MODx.grid.AccessPolicyTemplate.superclass.constructor.call(this,config); }; -Ext.extend(MODx.grid.AccessPolicyTemplate,MODx.grid.Grid,{ +Ext.extend(MODx.grid.AccessPolicyTemplate, MODx.grid.Grid, { getMenu: function() { - var r = this.getSelectionModel().getSelected(); - var p = r.data.cls; - - var m = []; + const + record = this.getSelectionModel().getSelected(), + menu = [] + ; if (this.getSelectionModel().getCount() > 1) { - m.push({ - text: _('selected_remove') - ,handler: this.removeSelected + menu.push({ + text: _('selected_remove'), + handler: this.removeSelected }); } else { - if (p.indexOf('pedit') !== -1) { - m.push({ - text: _('edit') - ,handler: this.editPolicyTemplate + if (this.userCanEdit && this.userCanEditRecord(record)) { + menu.push({ + text: _('edit'), + handler: this.editPolicyTemplate }); - m.push({ - text: _('duplicate') - ,handler: this.confirm.createDelegate(this,["Security/Access/Policy/Template/Duplicate","policy_template_duplicate_confirm"]) + } + if (this.userCanCreate && this.userCanDuplicateRecord(record)) { + menu.push({ + text: _('duplicate'), + handler: this.confirm.createDelegate( + this, + [ + 'Security/Access/Policy/Template/Duplicate', + 'policy_template_duplicate_confirm' + ] + ) }); } - if (m.length > 0) { m.push('-'); } - m.push({ - text: _('export') - ,handler: this.exportPolicyTemplate + if (menu.length > 0) { + menu.push('-'); + } + menu.push({ + text: _('export'), + handler: this.exportPolicyTemplate }); - - if (p.indexOf('premove') !== -1) { - if (m.length > 0) m.push('-'); - m.push({ + if (this.userCanDelete && this.userCanDeleteRecord(record)) { + if (menu.length > 0) { + menu.push('-'); + } + menu.push({ text: _('delete'), handler: this.removePolicyTemplate }); } } - if (m.length > 0) { - this.addContextMenuItem(m); + if (menu.length > 0) { + this.addContextMenuItem(menu); } - } + }, - ,createPolicyTemplate: function(btn,e) { - var r = this.menu.record; - if (!this.windows.aptc) { - this.windows.aptc = MODx.load({ - xtype: 'modx-window-access-policy-template-create' - ,record: r - ,plugin: this.config.plugin - ,listeners: { - 'success': {fn:function(r) { - this.refresh(); - },scope:this} + createPolicyTemplate: function(btn, e) { + const { record } = this.menu; + if (!this.windows.create_policy_template) { + this.windows.create_policy_template = MODx.load({ + xtype: 'modx-window-access-policy-template-create', + record: record, + plugin: this.config.plugin, + listeners: { + success: { + fn: function(response) { + this.refresh(); + }, + scope: this + } } }); } - this.windows.aptc.reset(); - this.windows.aptc.show(e.target); - } + this.windows.create_policy_template.reset(); + this.windows.create_policy_template.show(e.target); + }, - ,importPolicyTemplate: function(btn,e) { - var r = {}; + importPolicyTemplate: function(btn, e) { + const record = {}; if (!this.windows.importPolicyTemplate) { this.windows.importPolicyTemplate = MODx.load({ - xtype: 'modx-window-policy-template-import' - ,record: r - ,listeners: { - 'success': {fn:function(o) { - this.refresh(); - },scope:this} + xtype: 'modx-window-policy-template-import', + record: record, + listeners: { + success: { + fn: function(response) { + this.refresh(); + }, + scope: this + } } }); } this.windows.importPolicyTemplate.reset(); - this.windows.importPolicyTemplate.setValues(r); + this.windows.importPolicyTemplate.setValues(record); this.windows.importPolicyTemplate.show(e.target); - } + }, - ,exportPolicyTemplate: function(btn,e) { - var id = this.menu.record.id; + exportPolicyTemplate: function(btn, e) { + const { id } = this.menu.record; MODx.Ajax.request({ - url: this.config.url - ,params: { - action: 'Security/Access/Policy/Template/Export' - ,id: id - } - ,listeners: { - 'success': {fn:function(r) { - location.href = this.config.url+'?action=Security/Access/Policy/Template/Export&download=1&id='+id+'&HTTP_MODAUTH='+MODx.siteId; - },scope:this} + url: this.config.url, + params: { + action: 'Security/Access/Policy/Template/Export', + id: id + }, + listeners: { + success: { + fn: function(r) { + window.location.href = `${this.config.url}?action=Security/Access/Policy/Template/Export&download=1&id=${id}&HTTP_MODAUTH=${MODx.siteId}`; + }, + scope: this + } } }); - } - - ,editPolicyTemplate: function(itm,e) { - MODx.loadPage('security/access/policy/template/update', 'id='+this.menu.record.id); - } + }, - ,removeSelected: function() { - var cs = this.getSelectedAsList(); - if (cs === false) return false; + editPolicyTemplate: function(itm, e) { + MODx.loadPage('security/access/policy/template/update', `id=${this.menu.record.id}`); + }, - var store = this.getStore(); - var policiesCount = 0; - cs.split(',').forEach(function(item){ + removeSelected: function() { + const selectedTemplates = this.getSelectedAsList(); + if (selectedTemplates === false) { + return false; + } + const + store = this.getStore(), + selectedTemplatesArr = selectedTemplates.split(','), + totalSelected = selectedTemplatesArr.length + ; + let + policiesCount = 0, + selectionsProtected = 0, + confirmationMessage + ; + selectedTemplatesArr.forEach(item => { const record = store.getById(item); - + console.log(`removeSelected :: record for ${item}:`, record); if (record) { - policiesCount += parseInt(record.data.policy_count); + if (!record.json.isProtected) { + policiesCount += parseInt(record.data.policy_count, 10); + } else { + selectionsProtected++; + } } - - }) - + }); + if (policiesCount) { + confirmationMessage = selectionsProtected > 0 + ? _('policy_template_remove_multiple_confirm_in_use_ignoring_protected', { 'count-policies': policiesCount, protected: selectionsProtected, 'count-templates': totalSelected }) + : _('policy_template_remove_multiple_confirm_in_use', { count: policiesCount, total: totalSelected }) + ; + } else { + confirmationMessage = _('policy_template_remove_multiple_confirm'); + } MODx.msg.confirm({ - title: _('selected_remove') - ,text: policiesCount ? _('policy_template_remove_multiple_confirm_in_use', {count: policiesCount}) : _('policy_template_remove_multiple_confirm') - ,url: this.config.url - ,params: { - action: 'Security/Access/Policy/Template/RemoveMultiple' - ,templates: cs - } - ,listeners: { - 'success': {fn:function(r) { - this.getSelectionModel().clearSelections(true); - this.refresh(); - },scope:this} + title: _('selected_remove'), + text: confirmationMessage, + url: this.config.url, + params: { + action: 'Security/Access/Policy/Template/RemoveMultiple', + templates: selectedTemplates + }, + listeners: { + success: { + fn: function(response) { + this.getSelectionModel().clearSelections(true); + this.refresh(); + }, + scope: this + } } }); return true; - } - - ,removePolicyTemplate: function() { - if (!this.menu.record) return; + }, + removePolicyTemplate: function() { + if (!this.menu.record) { + return; + } MODx.msg.confirm({ title: _('warning'), - text: parseInt(this.menu.record.policy_count) ? _('policy_template_remove_confirm_in_use', {count: this.menu.record.policy_count}) : _('policy_template_remove_confirm'), + text: parseInt(this.menu.record.policy_count, 10) + ? _('policy_template_remove_confirm_in_use', { count: this.menu.record.policy_count }) + : _('policy_template_remove_confirm'), url: this.config.url, params: { action: 'Security/Access/Policy/Template/Remove', @@ -274,13 +413,13 @@ Ext.extend(MODx.grid.AccessPolicyTemplate,MODx.grid.Grid,{ listeners: { success: { fn: this.refresh, - scope:this + scope: this } } }); } }); -Ext.reg('modx-grid-access-policy-templates',MODx.grid.AccessPolicyTemplate); +Ext.reg('modx-grid-access-policy-templates', MODx.grid.AccessPolicyTemplate); /** * Generates a window for creating Access Policies. @@ -291,53 +430,50 @@ Ext.reg('modx-grid-access-policy-templates',MODx.grid.AccessPolicyTemplate); * @xtype modx-window-access-policy-create */ MODx.window.CreateAccessPolicyTemplate = function(config = {}) { - this.ident = config.ident || 'cacpt'+Ext.id(); - Ext.applyIf(config,{ - title: _('create') - ,url: MODx.config.connector_url - ,action: 'Security/Access/Policy/Template/Create' - ,fields: [{ - fieldLabel: _('name') - ,name: 'name' - ,id: 'modx-'+this.ident+'-name' - ,xtype: 'textfield' - ,anchor: '100%' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-'+this.ident+'-name' - ,html: _('policy_template_desc_name') - ,cls: 'desc-under' - },{ - fieldLabel: _('template_group') - ,name: 'template_group' - ,id: 'modx-'+this.ident+'-template-group' - ,xtype: 'modx-combo-access-policy-template-group' - ,anchor: '100%' - ,value: 1 - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-'+this.ident+'-template-group' - ,html: _('policy_template_desc_template_group') - ,cls: 'desc-under' - },{ - fieldLabel: _('description') - ,name: 'description' - ,id: 'modx-'+this.ident+'-description' - ,xtype: 'textarea' - ,anchor: '100%' - ,height: 50 - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-'+this.ident+'-description' - ,html: _('policy_template_desc_description') - ,cls: 'desc-under' - }] - ,keys: [] + this.ident = config.ident || `window-import-policy-template-${Ext.id()}`; + Ext.applyIf(config, { + title: _('create'), + url: MODx.config.connector_url, + action: 'Security/Access/Policy/Template/Create', + fields: [{ + fieldLabel: _('name'), + name: 'name', + xtype: 'textfield', + anchor: '100%' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('policy_template_desc_name'), + cls: 'desc-under' + }, { + fieldLabel: _('template_group'), + name: 'template_group', + xtype: 'modx-combo-access-policy-template-group', + anchor: '100%', + value: 1 + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('policy_template_desc_template_group'), + cls: 'desc-under' + }, { + fieldLabel: _('description'), + name: 'description', + xtype: 'textarea', + anchor: '100%', + height: 50 + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('policy_template_desc_description'), + cls: 'desc-under' + }], + keys: [] }); - MODx.window.CreateAccessPolicyTemplate.superclass.constructor.call(this,config); + MODx.window.CreateAccessPolicyTemplate.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.CreateAccessPolicyTemplate,MODx.Window); -Ext.reg('modx-window-access-policy-template-create',MODx.window.CreateAccessPolicyTemplate); +Ext.extend(MODx.window.CreateAccessPolicyTemplate, MODx.Window); +Ext.reg('modx-window-access-policy-template-create', MODx.window.CreateAccessPolicyTemplate); /** * @class MODx.window.ImportPolicyTemplate @@ -346,29 +482,27 @@ Ext.reg('modx-window-access-policy-template-create',MODx.window.CreateAccessPoli * @xtype modx-window-policy-template-import */ MODx.window.ImportPolicyTemplate = function(config = {}) { - this.ident = config.ident || 'imppt-'+Ext.id(); - Ext.applyIf(config,{ - title: _('import') - ,id: 'modx-window-policy-template-import' - ,url: MODx.config.connector_url - ,action: 'Security/Access/Policy/Template/Import' - ,fileUpload: true - ,saveBtnText: _('import') - ,fields: [{ - html: _('policy_template_import_msg') - ,id: this.ident+'-desc' - ,xtype: 'modx-description' - ,style: 'margin-bottom: 10px;' - },{ - xtype: 'fileuploadfield' - ,fieldLabel: _('file') - ,buttonText: _('upload.buttons.upload') - ,name: 'file' - ,id: this.ident+'-file' - ,anchor: '100%' + this.ident = config.ident || `window-import-policy-template-${Ext.id()}`; + Ext.applyIf(config, { + title: _('import'), + id: 'modx-window-policy-template-import', + url: MODx.config.connector_url, + action: 'Security/Access/Policy/Template/Import', + fileUpload: true, + saveBtnText: _('import'), + fields: [{ + html: _('policy_template_import_msg'), + xtype: 'modx-description', + style: 'margin-bottom: 10px;' + }, { + xtype: 'fileuploadfield', + fieldLabel: _('file'), + buttonText: _('upload.buttons.upload'), + name: 'file', + anchor: '100%' }] }); - MODx.window.ImportPolicyTemplate.superclass.constructor.call(this,config); + MODx.window.ImportPolicyTemplate.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.ImportPolicyTemplate,MODx.Window); -Ext.reg('modx-window-policy-template-import',MODx.window.ImportPolicyTemplate); +Ext.extend(MODx.window.ImportPolicyTemplate, MODx.Window); +Ext.reg('modx-window-policy-template-import', MODx.window.ImportPolicyTemplate); diff --git a/manager/assets/modext/widgets/security/modx.grid.role.js b/manager/assets/modext/widgets/security/modx.grid.role.js index c3323f3a385..54f27a6a3a2 100644 --- a/manager/assets/modext/widgets/security/modx.grid.role.js +++ b/manager/assets/modext/widgets/security/modx.grid.role.js @@ -1,5 +1,5 @@ /** - * Loads a grid of roles. + * Loads a grid of Roles. * * @class MODx.grid.Role * @extends MODx.grid.Grid @@ -20,7 +20,7 @@ MODx.grid.Role = function(config = {}) { 'name', 'description', 'authority', - 'perm' + 'creator' ], paging: true, autosave: true, @@ -33,34 +33,59 @@ MODx.grid.Role = function(config = {}) { }, { header: _('name'), dataIndex: 'name', + id: 'modx-role--name', width: 150, sortable: true, editor: { - xtype: 'textfield' + xtype: 'textfield', + allowBlank: false, + blankText: _('role_err_ns_name'), + validationEvent: 'change', + validator: function(value) { + const + grid = Ext.getCmp('modx-grid-role'), + reserved = this.gridEditor.record.json.reserved.name + ; + if (grid.valueIsReserved(reserved, value)) { + const msg = _('role_err_name_reserved', { reservedName: value }); + Ext.Msg.alert(_('error'), msg); + return false; + } + return true; + } }, renderer: { - fn: function(value, metaData, record, rowIndex, colIndex, store) { - metaData.css = this.setEditableCellClasses(record); - return Ext.util.Format.htmlEncode(value); + fn: function(value, metaData, record) { + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return value; }, scope: this } }, { header: _('description'), dataIndex: 'description', + id: 'modx-role--description', width: 350, - editor: { xtype: 'textarea' }, + editor: { + xtype: 'textarea' + }, renderer: { - fn: function(value, metaData, record, rowIndex, colIndex, store) { - metaData.css = this.setEditableCellClasses(record); - return Ext.util.Format.htmlEncode(value); + fn: function(value, metaData, record) { + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return value; }, scope: this } - }, { + }, + this.getCreatorColumnConfig('role'), + { header: _('authority'), dataIndex: 'authority', + id: 'modx-role--authority', width: 60, + align: 'center', sortable: true, editor: { xtype: 'numberfield', @@ -72,7 +97,8 @@ MODx.grid.Role = function(config = {}) { }, renderer: { fn: function(value, metaData, record, rowIndex, colIndex, store) { - metaData.css = this.setEditableCellClasses(record, [record.json.isAssigned]); + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isAssigned, record.json.isProtected], '', false); return value; }, scope: this @@ -102,20 +128,51 @@ MODx.grid.Role = function(config = {}) { text: _('create'), cls: 'primary-button', handler: this.createRole, - scope: this - }] + scope: this, + listeners: { + render: { + fn: function(btn) { + if (!this.userCanCreate) { + btn.hide(); + } + }, + scope: this + } + } + }], + viewConfig: this.getViewConfig(false, false) }); MODx.grid.Role.superclass.constructor.call(this, config); - this.on('beforeedit', this.checkCellIsEditable, this); + + this.gridMenuActions = ['delete']; + + this.setUserCanEdit(['save_role', 'edit_role']); + this.setUserCanCreate(['save_role', 'new_role']); + this.setUserCanDelete(['delete_role']); + this.setShowActionsMenu(); + + this.on({ + render: function() { + this.setEditableColumnAccess( + ['modx-role--name', 'modx-role--description', 'modx-role--authority'] + ); + }, + beforeedit: function(e) { + if (e.record.json.isProtected) { + return false; + } + } + }); }; Ext.extend(MODx.grid.Role, MODx.grid.Grid, { + getMenu: function() { const record = this.getSelectionModel().getSelected(), - permissions = record.data.perm || '', + permissions = record.json.permissions || '', menu = [] ; - if (permissions.indexOf('remove') !== -1) { + if (permissions.delete) { menu.push({ text: _('delete'), handler: this.remove.createDelegate(this, ['role_remove_confirm', 'Security/Role/Remove']) @@ -137,6 +194,7 @@ Ext.extend(MODx.grid.Role, MODx.grid.Grid, { } }); } + }); Ext.reg('modx-grid-role', MODx.grid.Role); @@ -160,17 +218,20 @@ MODx.window.CreateRole = function(config = {}) { fieldLabel: _('name'), xtype: 'textfield' }, { - xtype: MODx.expandHelp ? 'box' : 'hidden', + xtype: 'box', + hidden: !MODx.expandHelp, html: _('role_desc_name'), cls: 'desc-under' }, { name: 'authority', fieldLabel: _('authority'), - xtype: 'textfield', + xtype: 'numberfield', allowNegative: false, - value: 0 + value: 0, + maxValue: 9999 }, { - xtype: MODx.expandHelp ? 'box' : 'hidden', + xtype: 'box', + hidden: !MODx.expandHelp, html: _('role_desc_authority'), cls: 'desc-under' }, { @@ -180,7 +241,8 @@ MODx.window.CreateRole = function(config = {}) { allowBlank: true, grow: true }, { - xtype: MODx.expandHelp ? 'box' : 'hidden', + xtype: 'box', + hidden: !MODx.expandHelp, html: _('role_desc_description'), cls: 'desc-under' }], diff --git a/manager/assets/modext/widgets/security/modx.grid.user.js b/manager/assets/modext/widgets/security/modx.grid.user.js index 5c7a7157b4b..5c3610cdd10 100644 --- a/manager/assets/modext/widgets/security/modx.grid.user.js +++ b/manager/assets/modext/widgets/security/modx.grid.user.js @@ -124,7 +124,9 @@ MODx.grid.User = function(config = {}) { ,handler: this.createUser ,scope: this ,cls:'primary-button' - },{ + }, + this.getBulkActionsButton('user', 'Security/User/RemoveMultiple', 'int', 'activate', 'deactivate'), + /* { text: _('bulk_actions') ,menu: [ { @@ -141,7 +143,7 @@ MODx.grid.User = function(config = {}) { ,scope: this } ] - }, + }, */ '->', { xtype: 'modx-combo-usergroup' @@ -294,28 +296,6 @@ Ext.extend(MODx.grid.User,MODx.grid.Grid,{ return true; } - ,removeSelected: function() { - var cs = this.getSelectedAsList(); - if (cs === false) return false; - - MODx.msg.confirm({ - title: _('selected_remove') - ,text: _('user_remove_multiple_confirm') - ,url: this.config.url - ,params: { - action: 'Security/User/RemoveMultiple' - ,users: cs - } - ,listeners: { - 'success': {fn:function(r) { - this.getSelectionModel().clearSelections(true); - this.refresh(); - },scope:this} - } - }); - return true; - } - ,rendGender: function(d,c) { switch(d.toString()) { case '0': diff --git a/manager/assets/modext/widgets/security/modx.panel.access.policy.js b/manager/assets/modext/widgets/security/modx.panel.access.policy.js index 0febb519eb7..5589e57da95 100644 --- a/manager/assets/modext/widgets/security/modx.panel.access.policy.js +++ b/manager/assets/modext/widgets/security/modx.panel.access.policy.js @@ -5,155 +5,219 @@ * @param {Object} config An object of config properties * @xtype modx-panel-access-policy */ -MODx.panel.AccessPolicy = function(config) { - config = config || {}; - Ext.applyIf(config,{ - url: MODx.config.connector_url - ,baseParams: { - action: 'Security/Access/Policy/Update' - ,id: MODx.request.id - } - ,id: 'modx-panel-access-policy' - ,cls: 'container form-with-labels' - ,class_key: 'modAccessPolicy' - ,plugin: '' - ,bodyStyle: '' - ,defaults: { collapsible: false ,autoHeight: true } - ,items: [this.getPageHeader(config),{ - xtype: 'modx-tabs' - ,defaults: { - autoHeight: true - ,border: true - ,bodyCssClass: 'tab-panel-wrapper' +MODx.panel.AccessPolicy = function(config = {}) { + console.log('config:', config); + const + protectedClassName = 'protected', + formItems = [{ + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('policy_desc_name'), + cls: 'desc-under' + }, { + xtype: 'textarea', + itemCls: config.record.isProtected ? protectedClassName : '', + fieldLabel: _('description'), + description: MODx.expandHelp ? '' : _('policy_desc_description'), + name: 'description', + disabled: config.record.isProtected, + grow: true, + listeners: { + beforerender: { + fn: function(cmp) { + // const renderValue = this.record.description_trans || cmp.value; + const renderValue = this.record.isProtected && MODx.cultureKey !== 'en' && !this.record.description_trans_missing + ? this.record.description_trans + : this.record.description + ; + // console.log('desc beforerender, cmp', cmp); + // console.log('desc beforerender, this', this); + cmp.setValue(renderValue); + }, + scope: this + } } - ,forceLayout: true - ,deferredRender: false - ,items: [{ - title: _('policy') - ,layout: 'form' - ,items: [{ - html: '

        '+_('policy_desc')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'panel' - ,border: false - ,cls:'main-wrapper' - ,layout: 'form' - ,labelAlign: 'top' - ,labelSeparator: '' - ,defaults: { - msgTarget: 'under' - } - ,items: [{ - xtype: 'hidden' - ,name: 'id' - ,value: config.plugin - },{ - xtype: 'textfield' - ,fieldLabel: _('name') - ,description: MODx.expandHelp ? '' : _('policy_desc_name') - ,name: 'name' - ,maxLength: 255 - ,enableKeyEvents: true - ,allowBlank: false - ,anchor: '100%' - ,listeners: { - 'keyup': {scope:this,fn:function(f,e) { + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('policy_desc_description'), + cls: 'desc-under' + }, { + xtype: 'textfield', + itemCls: config.record.isProtected ? protectedClassName : '', + fieldLabel: _('lexicon'), + description: MODx.expandHelp ? '' : _('policy_desc_lexicon'), + name: 'lexicon', + disabled: config.record.isProtected, + value: 'permissions' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('policy_desc_lexicon'), + cls: 'desc-under' + }], + nameFields = config.record.isProtected + ? [ + /* + For protected, core policies show the translated name but submit the system name. + Need to do this because the field is required. + */ + { + xtype: 'hidden', + name: 'name', + value: config.record.name + }, { + xtype: 'textfield', + itemCls: protectedClassName, + fieldLabel: _('name'), + description: MODx.expandHelp ? '' : _('policy_desc_name'), + disabled: true, + value: MODx.cultureKey !== 'en' && !config.record.name_trans_missing + ? config.record.name_trans + : config.record.name + } + ] + : [ + { + xtype: 'textfield', + fieldLabel: _('name'), + description: MODx.expandHelp ? '' : _('policy_desc_name'), + name: 'name', + maxLength: 255, + enableKeyEvents: true, + allowBlank: false, + listeners: { + keyup: { + fn: function(f, e) { Ext.getCmp('modx-header-breadcrumbs').updateHeader(Ext.util.Format.htmlEncode(f.getValue())); - }} + }, + scope: this } - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-policy-name' - ,html: _('policy_desc_name') - ,cls: 'desc-under' - },{ - xtype: 'textarea' - ,fieldLabel: _('description') - ,description: MODx.expandHelp ? '' : _('policy_desc_description') - ,name: 'description' - ,anchor: '100%' - ,grow: true - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-policy-description' - ,html: _('policy_desc_description') - ,cls: 'desc-under' - },{ - xtype: 'textfield' - ,fieldLabel: _('lexicon') - ,description: MODx.expandHelp ? '' : _('policy_desc_lexicon') - ,name: 'lexicon' - ,allowBlank: true - ,anchor: '100%' - ,value: 'permissions' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-policy-lexicon' - ,html: _('policy_desc_lexicon') - ,cls: 'desc-under' - }] - },{ - html: '

        '+_('permissions_desc')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-policy-permissions' - ,cls:'main-wrapper' - ,policy: MODx.request.id - ,autoHeight: true - ,preventRender: true + } + } + ] + ; + formItems.unshift({ + xtype: 'hidden', + name: 'id', + value: config.plugin + }, ...nameFields); + + Ext.applyIf(config, { + url: MODx.config.connector_url, + baseParams: { + action: 'Security/Access/Policy/Get' + }, + id: 'modx-panel-access-policy', + cls: 'container form-with-labels', + class_key: 'modAccessPolicy', + plugin: '', + bodyStyle: '', + defaults: { + collapsible: false, + autoHeight: true + }, + items: [this.getPageHeader(config), { + xtype: 'modx-tabs', + defaults: { + autoHeight: true, + border: true, + bodyCssClass: 'tab-panel-wrapper' + }, + forceLayout: true, + deferredRender: false, + items: [{ + title: _('policy'), + layout: 'form', + items: [{ + html: `

        ${_('policy_desc')}

        `, + xtype: 'modx-description' + }, { + xtype: 'panel', + border: false, + cls: 'main-wrapper', + layout: 'form', + labelAlign: 'top', + labelSeparator: '', + defaults: { + msgTarget: 'under', + anchor: '100%' + }, + items: formItems + }, { + html: `

        ${_('permissions_desc')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-policy-permissions', + cls: 'main-wrapper', + policy: MODx.request.id, + autoHeight: true, + preventRender: true }] }] - }] - ,listeners: { - 'setup': {fn:this.setup,scope:this} - ,'success': {fn:this.success,scope:this} - ,'beforeSubmit': {fn:this.beforeSubmit,scope:this} + }], + listeners: { + setup: { + fn: this.setup, scope: this + }, + success: { + fn: this.success, scope: this + }, + beforeSubmit: { + fn: this.beforeSubmit, scope: this + } } }); - MODx.panel.AccessPolicy.superclass.constructor.call(this,config); + MODx.panel.AccessPolicy.superclass.constructor.call(this, config); }; -Ext.extend(MODx.panel.AccessPolicy,MODx.FormPanel,{ - initialized: false - ,setup: function() { - if (this.config.policy === '' || this.config.policy === 0) { +Ext.extend(MODx.panel.AccessPolicy, MODx.FormPanel, { + initialized: false, + setup: function() { + if (this.initialized || this.config.policy === '' || this.config.policy === 0) { this.fireEvent('ready'); return false; } + // console.log('setup :: this.config', this.config); + // console.log('MODx cfg', MODx.config); if (!this.initialized) { - var r = this.config.record; - this.getForm().setValues(r); - Ext.getCmp('modx-header-breadcrumbs').updateHeader(Ext.util.Format.htmlEncode(r.name)); - - var g = Ext.getCmp('modx-grid-policy-permissions'); - if (g) { g.getStore().loadData(r.permissions); } - + const + { record } = this.config, + renderName = record.isProtected && MODx.cultureKey !== 'en' && !record.name_trans_missing + ? record.name_trans + : record.name, + permissionsGrid = Ext.getCmp('modx-grid-policy-permissions') + ; + this.getForm().setValues(record); + Ext.getCmp('modx-header-breadcrumbs').updateHeader(Ext.util.Format.htmlEncode(renderName)); + if (permissionsGrid) { + permissionsGrid.getStore().loadData(record.permissions); + } this.fireEvent('ready'); MODx.fireEvent('ready'); this.initialized = true; } - } + }, - ,beforeSubmit: function(o) { + beforeSubmit: function(o) { const policyGrid = Ext.getCmp('modx-grid-policy-permissions'); policyGrid.clearFilter(); - Ext.apply(o.form.baseParams,{ + Ext.apply(o.form.baseParams, { permissions: policyGrid ? policyGrid.encode() : {} }); - } + }, - ,success: function(o) { + success: function(o) { Ext.getCmp('modx-grid-policy-permissions').getStore().commitChanges(); - } + }, - ,getPageHeader: function(config) { + getPageHeader: function(config) { return MODx.util.getHeaderBreadCrumbs('modx-policy-header', [{ text: _('user_group_management'), href: MODx.getPage('security/permission') }]); } }); -Ext.reg('modx-panel-access-policy',MODx.panel.AccessPolicy); +Ext.reg('modx-panel-access-policy', MODx.panel.AccessPolicy); /** * @class MODx.grid.PolicyPermissions @@ -162,40 +226,42 @@ Ext.reg('modx-panel-access-policy',MODx.panel.AccessPolicy); * @param {Object} config An object of options. * @xtype modx-grid-policy-permissions */ -MODx.grid.PolicyPermissions = function(config) { - config = config || {}; +MODx.grid.PolicyPermissions = function(config = {}) { const enabledCheckCol = new Ext.ux.grid.CheckColumn({ - header: _('enabled') - ,dataIndex: 'enabled' - ,width: 40 - ,sortable: true + header: _('enabled'), + dataIndex: 'enabled', + width: 40, + sortable: true }); - Ext.applyIf(config,{ - id: 'modx-grid-policy-permissions' - ,showActionsColumn: false - ,cls: 'modx-grid modx-policy-permissions-grid' - ,fields: [ + Ext.applyIf(config, { + id: 'modx-grid-policy-permissions', + showActionsColumn: false, + cls: 'modx-grid modx-policy-permissions-grid', + fields: [ 'name', 'description', 'description_trans', 'value', 'enabled' - ] - ,plugins: enabledCheckCol - ,columns: [{ - header: _('name') - ,dataIndex: 'name' - ,width: 100 - ,editor: { xtype: 'textfield', renderer: true } - },{ - header: _('description') - ,dataIndex: 'description_trans' - ,width: 250 - ,editable: false + ], + plugins: enabledCheckCol, + columns: [{ + header: _('name'), + dataIndex: 'name', + width: 100, + editor: { + xtype: 'textfield', + renderer: true + } + }, { + header: _('description'), + dataIndex: 'description_trans', + width: 250, + editable: false }, - enabledCheckCol - ] - ,tbar: [ + enabledCheckCol + ], + tbar: [ '->', { xtype: 'checkbox', @@ -244,31 +310,33 @@ MODx.grid.PolicyPermissions = function(config) { scope: this }, mouseout: { - fn: function(evt){ + fn: function(evt) { this.removeClass('x-btn-focus'); } } } } - ] - ,data: [] - ,width: '90%' - ,height: 300 - ,maxHeight: 300 - ,autosave: false - ,autoExpandColumn: 'name' + ], + data: [], + width: '90%', + height: 300, + maxHeight: 300, + autosave: false, + autoExpandColumn: 'name' }); - MODx.grid.PolicyPermissions.superclass.constructor.call(this,config); - this.propRecord = new Ext.data.Record.create(['name','description','access','value']); - this.on('rowclick',this.onPermRowClick,this); + MODx.grid.PolicyPermissions.superclass.constructor.call(this, config); + // eslint-disable-next-line new-cap + this.propRecord = new Ext.data.Record.create(['name', 'description', 'access', 'value']); + this.on('rowclick', this.onPermRowClick, this); + // console.log('Policy > Policies grid...'); }; -Ext.extend(MODx.grid.PolicyPermissions,MODx.grid.LocalGrid,{ - onPermRowClick: function(g,ri,e) { +Ext.extend(MODx.grid.PolicyPermissions, MODx.grid.LocalGrid, { + onPermRowClick: function(g, ri, e) { var s = this.getStore(); if (!s || typeof ri == 'undefined') { return; } var r = s.getAt(ri); - r.set('enabled',r.get('enabled') ? false : true); + r.set('enabled', r.get('enabled') ? false : true); r.commit(); }, applyQueryFilter: function(cmp, newValue) { @@ -295,4 +363,4 @@ Ext.extend(MODx.grid.PolicyPermissions,MODx.grid.LocalGrid,{ this.getStore().clearFilter(); } }); -Ext.reg('modx-grid-policy-permissions',MODx.grid.PolicyPermissions); +Ext.reg('modx-grid-policy-permissions', MODx.grid.PolicyPermissions); diff --git a/manager/assets/modext/widgets/security/modx.panel.groups.roles.js b/manager/assets/modext/widgets/security/modx.panel.groups.roles.js index 3d09269c6dd..ddcc2dc1747 100644 --- a/manager/assets/modext/widgets/security/modx.panel.groups.roles.js +++ b/manager/assets/modext/widgets/security/modx.panel.groups.roles.js @@ -7,14 +7,14 @@ MODx.panel.GroupsRoles = function(config = {}) { this.currentGroupId = 0; Ext.applyIf(config, { - id: 'modx-panel-groups-roles' - ,cls: 'container' - ,defaults: { + id: 'modx-panel-groups-roles', + cls: 'container', + defaults: { collapsible: false, autoHeight: true - } - ,forceLayout: true - ,items: [ + }, + forceLayout: true, + items: [ { html: _('user_group_management'), id: 'modx-access-permissions-header', @@ -25,41 +25,45 @@ MODx.panel.GroupsRoles = function(config = {}) { ) ] }); - MODx.panel.GroupsRoles.superclass.constructor.call(this,config); - - const userGrid = Ext.getCmp('modx-usergroup-users'), - usergroupTree = Ext.getCmp('modx-tree-usergroup') - ; + MODx.panel.GroupsRoles.superclass.constructor.call(this, config); - usergroupTree.on({ - resize: { - fn: function(cmp) { - if (userGrid.hidden) { - Ext.getCmp('modx-tree-panel-usergroup').layout.west.getSplitBar().el.hide(); + if (MODx.perm.usergroup_user_list) { + const + userGrid = Ext.getCmp('modx-usergroup-users'), + usergroupTree = Ext.getCmp('modx-tree-usergroup') + ; + if (usergroupTree) { + usergroupTree.on({ + resize: { + fn: function(cmp) { + if (userGrid.hidden) { + Ext.getCmp('modx-tree-panel-usergroup').layout.west.getSplitBar().el.hide(); + } + }, + scope: this + }, + refresh: { + fn: function() { + this.setActiveGroupNodeFromParam(); + }, + scope: this + }, + click: { + fn: function(node, e) { + this.currentGroupId = MODx.util.tree.getGroupIdFromNode(node); + Ext.getCmp('modx-usergroup-users').clearGridFilters('filter-query-users'); + if (this.currentGroupId > 0) { + MODx.util.url.setParams({ + group: this.currentGroupId, + tab: 0 + }); + } + this.getUsers(node); + }, + scope: this } - }, - scope: this - }, - refresh: { - fn: function() { - this.setActiveGroupNodeFromParam(); - }, - scope: this + }); } - }); - - if (MODx.perm.usergroup_user_list) { - usergroupTree.on('click', function(node, e) { - this.currentGroupId = MODx.util.tree.getGroupIdFromNode(node); - Ext.getCmp('modx-usergroup-users').clearGridFilters('filter-query-users'); - if (this.currentGroupId > 0) { - MODx.util.url.setParams({ - group: this.currentGroupId, - tab: 0 - }); - } - this.getUsers(node); - }, this); usergroupTree.getLoader().on({ load: { @@ -72,109 +76,109 @@ MODx.panel.GroupsRoles = function(config = {}) { }); } }; -Ext.extend(MODx.panel.GroupsRoles,MODx.FormPanel,{ +Ext.extend(MODx.panel.GroupsRoles, MODx.FormPanel, { getPageTabs: function(config) { - var tbs = []; - if (MODx.perm.usergroup_view) { - tbs.push({ - title: _('user_groups') + ' & ' + _('users') - ,autoHeight: true - ,layout: 'form' - ,items: [{ - html: '

        '+_('user_group_management_msg')+'

        ' - ,xtype: 'modx-description' - },{ - layout: 'border' - ,id: 'modx-tree-panel-usergroup' - ,height: 500 - ,border:false - ,defaults: { - border:false - ,bodyStyle: 'background-color:transparent;' - } - ,items: [ + const tabs = []; + if (MODx.perm.usergroup_view && MODx.perm.usergroup_user_list) { + tabs.push({ + title: `${_('user_groups')} & ${_('users')}`, + autoHeight: true, + layout: 'form', + items: [{ + html: `

        ${_('user_group_management_msg')}

        `, + xtype: 'modx-description' + }, { + layout: 'border', + id: 'modx-tree-panel-usergroup', + height: 500, + border: false, + defaults: { + border: false, + bodyStyle: 'background-color:transparent;' + }, + items: [ { - region:'west' - ,cls:'main-wrapper' - ,collapseMode: 'mini' - ,split: true - ,useSplitTips: true - ,monitorResize: true - ,width: 280 - ,minWidth: 280 - ,minSize: 280 - ,maxSize: 400 - ,layout: 'fit' - ,items: [{ + region: 'west', + cls: 'main-wrapper', + collapseMode: 'mini', + split: true, + useSplitTips: true, + monitorResize: true, + width: 280, + minWidth: 280, + minSize: 280, + maxSize: 400, + layout: 'fit', + items: [{ xtype: 'modx-tree-usergroup' }] }, { - region: 'center' - ,id: 'modx-usergroup-users' - ,xtype: 'modx-grid-user-group-users' - ,hidden: MODx.perm.usergroup_user_list && this.currentGroupId > 0 ? false : true - ,usergroup: this.currentGroupId - ,layout: 'fit' - ,cls:'main-wrapper' + region: 'center', + id: 'modx-usergroup-users', + xtype: 'modx-grid-user-group-users', + hidden: !(this.currentGroupId > 0), + usergroup: this.currentGroupId, + layout: 'fit', + cls: 'main-wrapper' } ] }] }); } if (MODx.perm.view_role) { - tbs.push({ - title: _('roles') - ,autoHeight: true - ,layout: 'form' - ,items: [{ - html: '

        '+_('roles_msg')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-role' - ,cls:'main-wrapper' - ,title: '' - ,preventRender: true + tabs.push({ + title: _('roles'), + autoHeight: true, + layout: 'form', + items: [{ + html: `

        ${_('roles_msg')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-role', + cls: 'main-wrapper', + title: '', + preventRender: true }] }); } if (MODx.perm.policy_view) { - tbs.push({ - title: _('policies') - ,id: 'modx-panel-access-policies' - ,autoHeight: true - ,layout: 'form' - ,items: [{ - html: '

        '+_('policy_management_msg')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-access-policy' - ,cls:'main-wrapper' + tabs.push({ + title: _('policies'), + id: 'modx-panel-access-policies', + autoHeight: true, + layout: 'form', + items: [{ + html: `

        ${_('policy_management_msg')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-access-policy', + cls: 'main-wrapper' }] }); } if (MODx.perm.policy_template_view) { - tbs.push({ - title: _('policy_templates') - ,id: 'modx-panel-access-policy-templates' - ,autoHeight: true - ,layout: 'form' - ,items: [{ - html: '

        '+_('policy_templates.intro_msg')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-access-policy-templates' - ,cls:'main-wrapper' + tabs.push({ + title: _('policy_templates'), + id: 'modx-panel-access-policy-templates', + autoHeight: true, + layout: 'form', + items: [{ + html: `

        ${_('policy_templates.intro_msg')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-access-policy-templates', + cls: 'main-wrapper' }] }); } - return tbs; - } + return tabs; + }, - ,getUsers: function(node) { + getUsers: function(node) { const userGrid = Ext.getCmp('modx-usergroup-users'), westPanel = Ext.getCmp('modx-tree-panel-usergroup').layout.west ; - if (this.currentGroupId == 0) { + if (this.currentGroupId === 0) { userGrid.hide(); westPanel.getSplitBar().el.hide(); } else { @@ -185,9 +189,9 @@ Ext.extend(MODx.panel.GroupsRoles,MODx.FormPanel,{ userGrid.store.baseParams.usergroup = this.currentGroupId; userGrid.store.load(); } - } + }, - ,setActiveGroupNodeFromParam: function() { + setActiveGroupNodeFromParam: function() { if (this.currentGroupId > 0) { const usergroupTree = Ext.getCmp('modx-tree-usergroup'), groupNodeId = `n_ug_${this.currentGroupId}`, @@ -200,4 +204,4 @@ Ext.extend(MODx.panel.GroupsRoles,MODx.FormPanel,{ } } }); -Ext.reg('modx-panel-groups-roles',MODx.panel.GroupsRoles); +Ext.reg('modx-panel-groups-roles', MODx.panel.GroupsRoles); diff --git a/manager/assets/modext/widgets/security/modx.panel.user.group.js b/manager/assets/modext/widgets/security/modx.panel.user.group.js index d1bb58f287a..07edf4eadd2 100644 --- a/manager/assets/modext/widgets/security/modx.panel.user.group.js +++ b/manager/assets/modext/widgets/security/modx.panel.user.group.js @@ -233,11 +233,12 @@ MODx.panel.UserGroup = function(config) { }] ,listeners: { render: function(vtabPanel) { - var elCatsPanelKey = vtabPanel.items.keys.indexOf('user-group-category-access'), + const + form = Ext.getCmp('modx-panel-user-group').getForm(), + elCatsPanelKey = vtabPanel.items.keys.indexOf('user-group-category-access'), mediaSrcPanelKey = vtabPanel.items.keys.indexOf('user-group-source-access'), namespacePanelKey = vtabPanel.items.keys.indexOf('user-group-namespace-access') - form = Ext.getCmp('modx-panel-user-group').getForm() - ; + ; if (form.record.id === 0) { vtabPanel.hideTabStripItem(elCatsPanelKey); vtabPanel.hideTabStripItem(mediaSrcPanelKey); @@ -281,10 +282,11 @@ MODx.panel.UserGroup = function(config) { }] ,listeners: { render: function(tabPanel) { - var usersPanelKey = tabPanel.items.keys.indexOf('modx-usergroup-users-panel'), - settingsPanelKey = tabPanel.items.keys.indexOf('modx-usergroup-settings-panel'), - form = Ext.getCmp('modx-panel-user-group').getForm() - ; + const + form = Ext.getCmp('modx-panel-user-group').getForm(), + usersPanelKey = tabPanel.items.keys.indexOf('modx-usergroup-users-panel'), + settingsPanelKey = tabPanel.items.keys.indexOf('modx-usergroup-settings-panel') + ; if (form.record.id === 0) { tabPanel.hideTabStripItem(usersPanelKey); tabPanel.hideTabStripItem(settingsPanelKey); diff --git a/manager/assets/modext/widgets/source/modx.panel.source.js b/manager/assets/modext/widgets/source/modx.panel.source.js index cec222890e8..396151653e1 100644 --- a/manager/assets/modext/widgets/source/modx.panel.source.js +++ b/manager/assets/modext/widgets/source/modx.panel.source.js @@ -4,223 +4,252 @@ * @param {Object} config An object of configuration properties * @xtype modx-panel-source */ -MODx.panel.Source = function(config) { - config = config || {}; - Ext.applyIf(config,{ - id: 'modx-panel-source' - ,url: MODx.config.connector_url - ,baseParams: { +MODx.panel.Source = function(config = {}) { + let generalIntro = {}; + if (config.record.reserved) { + generalIntro = { + xtype: 'box', + cls: 'panel-desc', + html: _('source_reserved_general_desc') + }; + } + Ext.applyIf(config, { + id: 'modx-panel-source', + url: MODx.config.connector_url, + baseParams: { action: 'Source/Update' - } - ,defaults: { collapsible: false ,autoHeight: true } - ,cls: 'container form-with-labels' - ,items: [this.getPageHeader(config),{ - xtype: 'modx-tabs' - ,defaults: { - autoHeight: true - ,border: true - ,bodyCssClass: 'tab-panel-wrapper' - } - ,id: 'modx-source-tabs' - ,forceLayout: true - ,deferredRender: false - ,stateful: true - ,stateId: 'modx-source-tabpanel' - ,stateEvents: ['tabchange'] - ,getState:function() { - return {activeTab:this.items.indexOf(this.getActiveTab())}; - } - ,items: [{ - title: _('general_information') - ,defaults: { border: false, msgTarget: 'side' } - ,layout: 'form' - ,id: 'modx-source-form' - ,labelWidth: 150 - ,items: [{ - xtype: 'panel' - ,border: false - ,cls: 'main-wrapper' - ,layout: 'form' - ,labelAlign: 'top' - ,items: [{ - layout: 'column' - ,border: false - ,defaults: { - layout: 'form' - ,labelAlign: 'top' - ,anchor: '100%' - ,border: false - } - ,items: [{ - columnWidth: .65 - ,cls: 'main-content' - ,items: [{ - xtype: 'hidden' - ,name: 'id' - ,id: 'modx-source-id' - ,value: config.record.id - },{ - name: 'name' - ,id: 'modx-source-name' - ,xtype: 'textfield' - ,fieldLabel: _('name') - ,description: MODx.expandHelp ? '' : _('source_name_desc') - ,allowBlank: false - ,enableKeyEvents: true - ,anchor: '100%' - ,listeners: { - 'keyup': {scope:this,fn:function(f,e) { - Ext.getCmp('modx-header-breadcrumbs').updateHeader(Ext.util.Format.htmlEncode(f.getValue())); - }} + }, + defaults: { + collapsible: false, + autoHeight: true + }, + cls: 'container form-with-labels', + items: [this.getPageHeader(config), { + xtype: 'modx-tabs', + defaults: { + autoHeight: true, + border: true, + bodyCssClass: 'tab-panel-wrapper' + }, + id: 'modx-source-tabs', + forceLayout: true, + deferredRender: false, + stateful: true, + stateId: 'modx-source-tabpanel', + stateEvents: ['tabchange'], + getState: function() { + return { + activeTab: this.items.indexOf(this.getActiveTab()) + }; + }, + items: [{ + title: _('general_information'), + layout: 'form', + id: 'modx-source-form', + items: [generalIntro, { + xtype: 'panel', + border: false, + cls: 'main-wrapper', + layout: 'form', + labelAlign: 'top', + items: [{ + layout: 'column', + border: false, + defaults: { + layout: 'form', + labelAlign: 'top', + labelSeparator: '' + }, + items: [{ + columnWidth: 0.65, + defaults: { + anchor: '100%', + msgTarget: 'under' + }, + cls: 'main-content', + items: [{ + xtype: 'hidden', + name: 'id', + id: 'modx-source-id', + value: config.record.id + }, { + xtype: config.record.reserved ? 'statictextfield' : 'textfield', + name: 'name', + id: 'modx-source-name', + fieldLabel: _('name'), + description: MODx.expandHelp ? '' : _('source_name_desc'), + allowBlank: false, + enableKeyEvents: true, + listeners: { + keyup: { + scope: this, + fn: function(field, e) { + Ext.getCmp('modx-header-breadcrumbs').updateHeader(Ext.util.Format.htmlEncode(field.getValue())); + } + } } - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-source-name' - ,html: _('source_name_desc') - ,cls: 'desc-under' - },{ - name: 'description' - ,id: 'modx-source-description' - ,xtype: 'textarea' - ,fieldLabel: _('description') - ,description: MODx.expandHelp ? '' : _('source_description_desc') - ,anchor: '100%' - ,grow: true - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-source-description' - ,html: _('source_description_desc') - ,cls: 'desc-under' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('source_name_desc'), + cls: 'desc-under' + }, { + xtype: config.record.reserved ? 'statictextarea' : 'textarea', + name: 'description', + id: 'modx-source-description', + fieldLabel: _('description'), + description: MODx.expandHelp ? '' : _('source_description_desc'), + grow: true + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('source_description_desc'), + cls: 'desc-under' }] - },{ - columnWidth: .35 - ,cls: 'main-content' - ,items: [{ - name: 'class_key' - ,hiddenName: 'class_key' - ,id: 'modx-source-type' - ,xtype: 'modx-combo-source-type' - ,fieldLabel: _('source_type') - ,description: MODx.expandHelp ? '' : _('source_type_desc') - ,anchor: '100%' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-source-type' - ,html: _('source_type_desc') - ,cls: 'desc-under' + }, { + columnWidth: 0.35, + defaults: { + anchor: '100%', + msgTarget: 'under' + }, + cls: 'main-content', + items: [{ + disabled: config.record.reserved, + xtype: 'modx-combo-source-type', + name: 'class_key', + hiddenName: 'class_key', + id: 'modx-source-type', + fieldLabel: _('source_type'), + description: MODx.expandHelp ? '' : _('source_type_desc') + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('source_type_desc'), + cls: 'desc-under' }] }] }] - },{ - html: '

        '+_('source_properties.intro_msg')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-source-properties' - ,preventRender: true - ,source: config.record.id - ,defaultProperties: config.defaultProperties - ,autoHeight: true - ,cls: 'main-wrapper' - ,listeners: { - 'afterRemoveRow': {fn:this.markDirty,scope:this} + }, { + html: `

        ${_('source_properties.intro_msg')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-source-properties', + preventRender: true, + source: config.record.id, + defaultProperties: config.defaultProperties, + autoHeight: true, + cls: 'main-wrapper', + listeners: { + afterRemoveRow: { fn: this.markDirty, scope: this } } }] - },{ - title: _('access') - ,hideMode: 'offsets' - ,items: [{ - html: '

        '+_('source.access.intro_msg')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-source-access' - ,preventRender: true - ,source: config.record.id - ,autoHeight: true - ,cls: 'main-wrapper' - ,listeners: { - 'afterRemoveRow': {fn:this.markDirty,scope:this} - ,'updateRole': {fn:this.markDirty,scope:this} - ,'addMember': {fn:this.markDirty,scope:this} + }, { + title: _('access'), + hideMode: 'offsets', + items: [{ + html: `

        ${_('source.access.intro_msg')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-source-access', + preventRender: true, + source: config.record.id, + autoHeight: true, + cls: 'main-wrapper', + listeners: { + afterRemoveRow: { fn: this.markDirty, scope: this }, + updateRole: { fn: this.markDirty, scope: this }, + addMember: { fn: this.markDirty, scope: this } } }] }] - }] - ,listeners: { - 'setup': {fn:this.setup,scope:this} - ,'success': {fn:this.success,scope:this} - ,'beforeSubmit': {fn:this.beforeSubmit,scope:this} + }], + listeners: { + setup: { fn: this.setup, scope: this }, + success: { fn: this.success, scope: this }, + beforeSubmit: { fn: this.beforeSubmit, scope: this } } }); - MODx.panel.Source.superclass.constructor.call(this,config); + MODx.panel.Source.superclass.constructor.call(this, config); }; -Ext.extend(MODx.panel.Source,MODx.FormPanel,{ - initialized: false +Ext.extend(MODx.panel.Source, MODx.FormPanel, { + initialized: false, - ,setup: function() { - if (this.initialized) { return false; } + setup: function() { + if (this.initialized) { + return false; + } if (Ext.isEmpty(this.config.record.id)) { this.fireEvent('ready'); return false; } + this.getForm().setValues(this.config.record); + /* The component rendering is deferred since we are not using renderTo */ Ext.getCmp('modx-header-breadcrumbs').updateHeader(Ext.util.Format.htmlEncode(this.config.record.name)); - var g,d; if (!Ext.isEmpty(this.config.record.properties)) { - g = Ext.getCmp('modx-grid-source-properties'); - if (g) { - g.defaultProperties = this.config.defaultProperties; - g.getStore().loadData(this.config.record.properties); + const propsGrid = Ext.getCmp('modx-grid-source-properties'); + if (propsGrid) { + propsGrid.defaultProperties = this.config.defaultProperties; + propsGrid.getStore().loadData(this.config.record.properties); } } if (!Ext.isEmpty(this.config.record.access)) { - d = this.config.record.access; - g = Ext.getCmp('modx-grid-source-access'); - if (g) { - d = Ext.decode(d); - if (!Ext.isEmpty(d)) { - g.defaultProperties = d; - g.getStore().loadData(d); + let { access } = this.config.record; + const accessGrid = Ext.getCmp('modx-grid-source-access'); + if (accessGrid) { + access = Ext.decode(access); + if (!Ext.isEmpty(access)) { + accessGrid.defaultProperties = access; + accessGrid.getStore().loadData(access); } } } - this.fireEvent('ready',this.config.record); + this.fireEvent('ready', this.config.record); MODx.fireEvent('ready'); this.initialized = true; - } + }, - ,beforeSubmit: function(o) { - var bp = {}; - var sp = Ext.getCmp('modx-grid-source-properties'); - if (sp) { - bp.properties = sp.encode(); + beforeSubmit: function(o) { + const + sourceData = {}, + propsGrid = Ext.getCmp('modx-grid-source-properties'), + accessGrid = Ext.getCmp('modx-grid-source-access') + ; + if (propsGrid) { + sourceData.properties = propsGrid.encode(); } - var ap = Ext.getCmp('modx-grid-source-access'); - if (ap) { - bp.access = ap.encode(); + if (accessGrid) { + sourceData.access = accessGrid.encode(); } - Ext.apply(o.form.baseParams,bp); - } + Ext.apply(o.form.baseParams, sourceData); + }, - ,success: function(o) { + success: function(o) { if (Ext.isEmpty(this.config.record) || Ext.isEmpty(this.config.record.id)) { - MODx.loadPage('source/update', 'id='+o.result.object.id); + MODx.loadPage('source/update', `id=${o.result.object.id}`); } else { + const + propsGrid = Ext.getCmp('modx-grid-source-properties'), + accessGrid = Ext.getCmp('modx-grid-source-access') + ; Ext.getCmp('modx-abtn-save').setDisabled(false); - var wg = Ext.getCmp('modx-grid-source-properties'); - if (wg) { wg.getStore().commitChanges(); } - var ag = Ext.getCmp('modx-grid-source-access'); - if (ag) { ag.getStore().commitChanges(); } + if (propsGrid) { + propsGrid.getStore().commitChanges(); + } + if (accessGrid) { + accessGrid.getStore().commitChanges(); + } } - } + }, - ,getPageHeader: function(config) { + getPageHeader: function(config) { return MODx.util.getHeaderBreadCrumbs('modx-source-header', [{ text: _('sources'), href: MODx.getPage('source') }]); } }); -Ext.reg('modx-panel-source',MODx.panel.Source); +Ext.reg('modx-panel-source', MODx.panel.Source); diff --git a/manager/assets/modext/widgets/source/modx.panel.sources.js b/manager/assets/modext/widgets/source/modx.panel.sources.js index 7cce9d4e9a3..48e8a59865f 100644 --- a/manager/assets/modext/widgets/source/modx.panel.sources.js +++ b/manager/assets/modext/widgets/source/modx.panel.sources.js @@ -6,45 +6,47 @@ * @param {Object} config An object of configuration options * @xtype modx-panel-sources */ -MODx.panel.Sources = function(config) { - config = config || {}; - Ext.applyIf(config,{ - id: 'modx-panel-sources' - ,cls: 'container' - ,bodyStyle: '' - ,defaults: { collapsible: false ,autoHeight: true } - ,items: [{ - html: _('sources') - ,id: 'modx-sources-header' - ,xtype: 'modx-header' - },MODx.getPageStructure([{ - title: _('sources') - ,layout: 'form' - ,items: [{ - html: '

        '+_('sources.intro_msg')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-sources' - ,cls: 'main-wrapper' - ,preventRender: true +MODx.panel.Sources = function(config = {}) { + Ext.applyIf(config, { + id: 'modx-panel-sources', + cls: 'container', + bodyStyle: '', + defaults: { + collapsible: false, + autoHeight: true + }, + items: [{ + html: _('sources'), + id: 'modx-sources-header', + xtype: 'modx-header' + }, MODx.getPageStructure([{ + title: _('sources'), + layout: 'form', + items: [{ + html: `

        ${_('sources.intro_msg')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-sources', + cls: 'main-wrapper', + preventRender: true }] - },{ - layout: 'form' - ,title: _('source_types') - ,items: [{ - html: '

        '+_('source_types.intro_msg')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-source-types' - ,cls: 'main-wrapper' - ,preventRender: true + }, { + layout: 'form', + title: _('source_types'), + items: [{ + html: `

        ${_('source_types.intro_msg')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-source-types', + cls: 'main-wrapper', + preventRender: true }] }])] }); - MODx.panel.Sources.superclass.constructor.call(this,config); + MODx.panel.Sources.superclass.constructor.call(this, config); }; -Ext.extend(MODx.panel.Sources,MODx.FormPanel); -Ext.reg('modx-panel-sources',MODx.panel.Sources); +Ext.extend(MODx.panel.Sources, MODx.FormPanel); +Ext.reg('modx-panel-sources', MODx.panel.Sources); /** * Loads a grid of Sources. @@ -56,168 +58,191 @@ Ext.reg('modx-panel-sources',MODx.panel.Sources); */ MODx.grid.Sources = function(config = {}) { this.sm = new Ext.grid.CheckboxSelectionModel(); - Ext.applyIf(config,{ - url: MODx.config.connector_url - ,baseParams: { + + Ext.applyIf(config, { + id: 'modx-grid-sources', + url: MODx.config.connector_url, + baseParams: { action: 'Source/GetList' - } - ,fields: [ + }, + fields: [ 'id', 'name', 'description', 'class_key', - 'cls' - ] - ,paging: true - ,autosave: true - ,save_action: 'Source/UpdateFromGrid' - ,remoteSort: true - ,sm: this.sm - ,columns: [this.sm,{ - header: _('id') - ,dataIndex: 'id' - ,width: 50 - ,sortable: true - },{ - header: _('name') - ,dataIndex: 'name' - ,width: 150 - ,sortable: true - ,editor: { xtype: 'textfield' ,allowBlank: false } - ,renderer: { fn: function(v,md,record) { - return this.renderLink(v, { - href: '?a=source/update&id=' + record.data.id - }); - }, scope: this } - },{ - header: _('description') - ,dataIndex: 'description' - ,width: 300 - ,sortable: false - ,editor: { xtype: 'textarea' } - ,renderer: Ext.util.Format.htmlEncode - }] - ,tbar: [ - { - text: _('create') - ,handler: { - xtype: 'modx-window-source-create', - blankValues: true + 'creator' + ], + paging: true, + autosave: true, + save_action: 'Source/UpdateFromGrid', + remoteSort: true, + sm: this.sm, + stateful: true, + stateId: 'modx-grid-sources-state', + columns: [this.sm, { + header: _('id'), + dataIndex: 'id', + width: 50, + sortable: true + }, { + header: _('name'), + dataIndex: 'name', + id: 'modx-source--name', + width: 150, + sortable: true, + editor: { + xtype: 'textfield', + allowBlank: false, + blankText: _('source_err_ns_name'), + validationEvent: 'change', + validator: function(value) { + const grid = Ext.getCmp('modx-grid-sources'), + reserved = this.gridEditor.record.json.reserved.name + ; + if (grid.valueIsReserved(reserved, value)) { + const msg = _('source_err_name_reserved', { reservedName: value }); + Ext.Msg.alert(_('error'), msg); + return false; + } + return true; } - ,cls:'primary-button' - },{ - text: _('bulk_actions') - ,menu: [{ - text: _('selected_remove') - ,handler: this.removeSelected - ,scope: this - }] }, - '->', - this.getQueryFilterField(), - this.getClearFiltersButton() - ] - }); - MODx.grid.Sources.superclass.constructor.call(this,config); -}; -Ext.extend(MODx.grid.Sources,MODx.grid.Grid,{ - getMenu: function() { - var r = this.getSelectionModel().getSelected(); - var p = r.data.cls; - - var m = []; - if (this.getSelectionModel().getCount() > 1) { - m.push({ - text: _('selected_remove') - ,handler: this.removeSelected - ,scope: this - }); - } else { - if (p.indexOf('pupdate') != -1) { - m.push({ - text: _('edit') - ,handler: this.updateSource - }); + renderer: { + fn: function(value, metaData, record) { + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return this.userCanEditRecord(record) + ? this.renderLink(value, { + href: `?a=source/update&id=${record.data.id}`, + title: _('source_edit') + }) + : value + ; + }, + scope: this } - if (p.indexOf('pduplicate') != -1) { - m.push({ - text: _('duplicate') - ,handler: this.duplicateSource - }); + }, { + header: _('description'), + dataIndex: 'description', + id: 'modx-source--description', + width: 300, + editor: { + xtype: 'textarea' + }, + renderer: { + fn: function(value, metaData, record) { + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return value; + }, + scope: this } - if (p.indexOf('premove') != -1 && r.data.id != 1 && r.data.name != 'Filesystem') { - if (m.length > 0) m.push('-'); - m.push({ - text: _('delete') - ,handler: this.removeSource - }); + }, + this.getCreatorColumnConfig('source') + ], + tbar: [{ + text: _('create'), + cls: 'primary-button', + handler: { + xtype: 'modx-window-source-create', + blankValues: true + }, + listeners: { + render: { + fn: function(btn) { + if (!this.userCanCreate) { + btn.hide(); + } + }, + scope: this + } } - } - if (m.length > 0) { - this.addContextMenuItem(m); - } - } + }, + this.getBulkActionsButton('source', 'Source/RemoveMultiple'), + '->', + this.getQueryFilterField(), + this.getClearFiltersButton() + ], + viewConfig: this.getViewConfig() + }); + MODx.grid.Sources.superclass.constructor.call(this, config); - ,createSource: function() { - MODx.loadPage('system/source/create'); - } + this.gridMenuActions = ['edit', 'delete', 'duplicate']; - ,updateSource: function() { - MODx.loadPage('source/update', 'id='+this.menu.record.id); - } + this.setUserCanEdit(['source_save', 'source_edit']); + this.setUserCanCreate(['source_save']); + this.setUserCanDelete(['source_delete']); + this.setShowActionsMenu(); - ,duplicateSource: function(btn,e) { - MODx.Ajax.request({ - url: this.config.url - ,params: { - action: 'Source/Duplicate' - ,id: this.menu.record.id - } - ,listeners: { - 'success': {fn:this.refresh,scope:this} + this.on({ + render: function(grid) { + this.setEditableColumnAccess( + ['modx-source--name', 'modx-source--description'] + ); + }, + beforeedit: function(e) { + if (e.record.json.isProtected || !this.userCanEditRecord(e.record)) { + return false; } - }); - } + } + }); +}; +Ext.extend(MODx.grid.Sources, MODx.grid.Grid, { - ,removeSource: function() { - MODx.msg.confirm({ - title: _('delete') - ,text: _('source_remove_confirm') - ,url: this.config.url - ,params: { - action: 'Source/Remove' - ,id: this.menu.record.id - } - ,listeners: { - 'success': {fn:this.refresh,scope:this} + getMenu: function() { + const + record = this.getSelectionModel().getSelected(), + menu = [] + ; + if (this.userCanEdit && this.userCanEditRecord(record)) { + menu.push({ + text: _('edit'), + handler: this.updateSource + }); + } + if (this.userCanCreate && this.userCanDuplicateRecord(record)) { + menu.push({ + text: _('duplicate'), + handler: this.duplicateSource + }); + } + if (this.userCanDelete && this.userCanDeleteRecord(record)) { + if (menu.length > 0) { + menu.push('-'); } - }); - } + menu.push({ + text: _('delete'), + handler: this.confirm.createDelegate(this, ['Source/Remove', 'source_remove_confirm']) + }); + } + return menu; + }, + + createSource: function() { + MODx.loadPage('system/source/create'); + }, - ,removeSelected: function() { - var cs = this.getSelectedAsList(); - if (cs === false) return false; + updateSource: function() { + MODx.loadPage('source/update', `id=${this.menu.record.id}`); + }, - MODx.msg.confirm({ - title: _('selected_remove') - ,text: _('source_remove_multiple_confirm') - ,url: this.config.url - ,params: { - action: 'Source/RemoveMultiple' - ,sources: cs - } - ,listeners: { - 'success': {fn:function(r) { - this.getSelectionModel().clearSelections(true); - this.refresh(); - },scope:this} + duplicateSource: function(btn, e) { + MODx.Ajax.request({ + url: this.config.url, + params: { + action: 'Source/Duplicate', + id: this.menu.record.id + }, + listeners: { + success: { + fn: this.refresh, + scope: this + } } }); - return true; } - }); -Ext.reg('modx-grid-sources',MODx.grid.Sources); +Ext.reg('modx-grid-sources', MODx.grid.Sources); /** * Generates the create Source window. @@ -228,68 +253,69 @@ Ext.reg('modx-grid-sources',MODx.grid.Sources); * @xtype modx-window-source-create */ MODx.window.CreateSource = function(config = {}) { - Ext.applyIf(config,{ - title: _('create') - ,url: MODx.config.connector_url - ,autoHeight: true - ,action: 'Source/Create' - ,fields: [{ - xtype: 'textfield' - ,fieldLabel: _('name') - ,name: 'name' - ,anchor: '100%' - ,allowBlank: false - },{ - xtype: 'textarea' - ,fieldLabel: _('description') - ,name: 'description' - ,anchor: '100%' - ,grow: true - },{ - name: 'class_key' - ,hiddenName: 'class_key' - ,xtype: 'modx-combo-source-type' - ,fieldLabel: _('source_type') - ,anchor: '100%' - ,allowBlank: false - ,value: MODx.config.default_media_source_type - }] - ,keys: [] + Ext.applyIf(config, { + title: _('create'), + url: MODx.config.connector_url, + autoHeight: true, + action: 'Source/Create', + formDefaults: { + anchor: '100%', + validationEvent: 'change', + validateOnBlur: false + }, + fields: [{ + xtype: 'textfield', + fieldLabel: _('name'), + name: 'name', + allowBlank: false + }, { + xtype: 'textarea', + fieldLabel: _('description'), + name: 'description', + grow: true + }, { + name: 'class_key', + xtype: 'modx-combo-source-type', + fieldLabel: _('source_type'), + allowBlank: false, + value: MODx.config.default_media_source_type + }], + keys: [] }); - MODx.window.CreateSource.superclass.constructor.call(this,config); + MODx.window.CreateSource.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.CreateSource,MODx.Window); -Ext.reg('modx-window-source-create',MODx.window.CreateSource); +Ext.extend(MODx.window.CreateSource, MODx.Window); +Ext.reg('modx-window-source-create', MODx.window.CreateSource); MODx.grid.SourceTypes = function(config = {}) { - Ext.applyIf(config,{ - url: MODx.config.connector_url - ,baseParams: { + Ext.applyIf(config, { + url: MODx.config.connector_url, + baseParams: { action: 'Source/Type/GetList' - } - ,fields: [ + }, + fields: [ 'class', 'name', 'description' - ] - ,showActionsColumn: false - ,paging: true - ,remoteSort: true - ,columns: [{ - header: _('name') - ,dataIndex: 'name' - ,width: 150 - ,sortable: true - ,renderer: Ext.util.Format.htmlEncode - },{ - header: _('description') - ,dataIndex: 'description' - ,width: 300 - ,sortable: false - ,renderer: Ext.util.Format.htmlEncode + ], + showActionsColumn: false, + paging: true, + remoteSort: true, + columns: [{ + header: _('name'), + dataIndex: 'name', + width: 150, + sortable: true, + renderer: Ext.util.Format.htmlEncode + }, { + header: _('description'), + dataIndex: 'description', + width: 300, + sortable: false, + renderer: Ext.util.Format.htmlEncode }] }); - MODx.grid.SourceTypes.superclass.constructor.call(this,config); + MODx.grid.SourceTypes.superclass.constructor.call(this, config); }; -Ext.extend(MODx.grid.SourceTypes,MODx.grid.Grid); -Ext.reg('modx-grid-source-types',MODx.grid.SourceTypes); +Ext.extend(MODx.grid.SourceTypes, MODx.grid.Grid); +Ext.reg('modx-grid-source-types', MODx.grid.SourceTypes); diff --git a/manager/assets/modext/widgets/system/modx.grid.context.js b/manager/assets/modext/widgets/system/modx.grid.context.js index 2532e5bfd78..5aa3eb7d285 100644 --- a/manager/assets/modext/widgets/system/modx.grid.context.js +++ b/manager/assets/modext/widgets/system/modx.grid.context.js @@ -6,34 +6,37 @@ * @param {Object} config An object of configuration options * @xtype modx-panel-contexts */ -MODx.panel.Contexts = function(config) { - config = config || {}; - Ext.applyIf(config,{ - id: 'modx-panel-contexts' - ,cls: 'container' - ,bodyStyle: '' - ,defaults: { collapsible: false ,autoHeight: true } - ,items: [{ - html: _('contexts') - ,id: 'modx-contexts-header' - ,xtype: 'modx-header' - },MODx.getPageStructure([{ - title: _('contexts') - ,layout: 'form' - ,items: [{ - html: '

        '+_('context_management_message')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-contexts' - ,cls:'main-wrapper' - ,preventRender: true +MODx.panel.Contexts = function(config = {}) { + Ext.applyIf(config, { + id: 'modx-panel-contexts', + cls: 'container', + bodyStyle: '', + defaults: { + collapsible: false, + autoHeight: true + }, + items: [{ + html: _('contexts'), + id: 'modx-contexts-header', + xtype: 'modx-header' + }, MODx.getPageStructure([{ + title: _('contexts'), + layout: 'form', + items: [{ + html: `

        ${_('context_management_message')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-contexts', + urlFilters: ['search'], + cls: 'main-wrapper', + preventRender: true }] }])] }); - MODx.panel.Contexts.superclass.constructor.call(this,config); + MODx.panel.Contexts.superclass.constructor.call(this, config); }; -Ext.extend(MODx.panel.Contexts,MODx.FormPanel); -Ext.reg('modx-panel-contexts',MODx.panel.Contexts); +Ext.extend(MODx.panel.Contexts, MODx.FormPanel); +Ext.reg('modx-panel-contexts', MODx.panel.Contexts); /** * Loads a grid of modContexts. @@ -43,110 +46,221 @@ Ext.reg('modx-panel-contexts',MODx.panel.Contexts); * @param {Object} config An object of configuration properties * @xtype modx-grid-contexts */ -MODx.grid.Context = function(config) { - config = config || {}; - Ext.applyIf(config,{ - title: _('contexts') - ,id: 'modx-grid-context' - ,url: MODx.config.connector_url - ,baseParams: { +MODx.grid.Context = function(config = {}) { + Ext.applyIf(config, { + title: _('contexts'), + id: 'modx-grid-context', + url: MODx.config.connector_url, + baseParams: { action: 'Context/GetList' - } - ,fields: [ + }, + fields: [ 'key', 'name', 'description', - 'perm', - 'rank' - ] - ,paging: true - ,autosave: true - ,save_action: 'Context/UpdateFromGrid' - ,remoteSort: true - ,primaryKey: 'key' - ,columns: [{ - header: _('key') - ,dataIndex: 'key' - ,width: 100 - ,sortable: true - },{ - header: _('name') - ,dataIndex: 'name' - ,width: 150 - ,sortable: true - ,editor: { xtype: 'textfield' } - ,renderer: { fn: function(v,md,record) { - return this.renderLink(v, { - href: '?a=context/update&key=' + record.data.key - }); - }, scope: this } - },{ - header: _('description') - ,dataIndex: 'description' - ,width: 575 - ,sortable: false - ,editor: { xtype: 'textarea' } - },{ - header: _('rank') - ,dataIndex: 'rank' - ,width: 100 - ,sortable: true - ,editor: { xtype: 'numberfield' } - }] - ,tbar: [ + 'rank', + 'creator' + ], + paging: true, + autosave: true, + save_action: 'Context/UpdateFromGrid', + remoteSort: true, + primaryKey: 'key', + stateful: true, + stateId: 'modx-grid-context-state', + columns: [{ + header: _('key'), + dataIndex: 'key', + width: 100, + sortable: true + }, { + header: _('name'), + dataIndex: 'name', + id: 'modx-context--name', + width: 150, + sortable: true, + editor: { + xtype: 'textfield', + allowBlank: false, + blankText: _('context_err_ns_name'), + validationEvent: 'change', + validator: function(value) { + const + grid = Ext.getCmp('modx-grid-context'), + reserved = this.gridEditor.record.json.reserved.name + ; + if (grid.valueIsReserved(reserved, value)) { + const msg = _('context_err_name_reserved', { + reservedName: value + }); + Ext.Msg.alert(_('error'), msg); + return false; + } + return true; + } + }, + renderer: { + fn: function(value, metaData, record) { + // const renderValue = MODx.util.Render.translatedValue('name', record); + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected, !(record.json.key === 'web')]); + return this.userCanEditRecord(record) + ? this.renderLink(value, { + href: `?a=context/update&key=${record.data.key}`, + title: _('context_edit') + }) + : value + ; + }, + scope: this + } + }, { + header: _('description'), + dataIndex: 'description', + id: 'modx-context--description', + width: 575, + sortable: false, + editor: { + xtype: 'textarea' + }, + renderer: { + fn: function(value, metaData, record) { + // const renderValue = MODx.util.Render.translatedValue('description', record); + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected, !(record.json.key === 'web')]); + // return Ext.util.Format.htmlEncode(renderValue); + return value; + }, + scope: this + } + }, + this.getCreatorColumnConfig('context'), + { + header: _('rank'), + dataIndex: 'rank', + id: 'modx-context--rank', + width: 100, + align: 'center', + sortable: true, + editor: { + xtype: 'numberfield' + }, + renderer: { + fn: function(value, metaData, record) { + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected, !(record.json.key === 'web')]); + return value; + }, + scope: this + } + }], + tbar: [ { - text: _('create') - ,cls:'primary-button' - ,handler: this.create - ,scope: this + text: _('create'), + cls: 'primary-button', + handler: this.create, + scope: this }, '->', this.getQueryFilterField(), this.getClearFiltersButton() - ] + ], + viewConfig: { + forceFit: true, + scrollOffset: 0, + getRowClass: function(record, index, rowParams, store) { + // Adds the returned class to the row container's css classes + return record.json.isProtected ? 'modx-protected-row' : ''; + } + } + }); + MODx.grid.Context.superclass.constructor.call(this, config); + + this.gridMenuActions = ['edit', 'delete', 'duplicate']; + + this.setUserCanEdit(['save_context', 'edit_context']); + this.setUserCanCreate(['save_context', 'new_context']); + this.setUserCanDelete(['delete_context']); + this.setShowActionsMenu(); + + this.on({ + render: function() { + this.setEditableColumnAccess( + ['modx-context--name', 'modx-context--description', 'modx-context--rank'] + ); + }, + beforeedit: function(e) { + if (e.record.json.key === 'mgr' || !this.userCanEditRecord(e.record)) { + return false; + } + }, + afterAutoSave: function(response) { + if (response.eventData.value !== response.eventData.originalValue) { + const + resourceTree = Ext.getCmp('modx-resource-tree'), + contextNodeId = `${response.object.key}_0`, + contextRootNode = resourceTree.root.findChild('id', contextNodeId) + ; + if (resourceTree && resourceTree.rendered) { + switch (response.eventData.field) { + case 'name': + contextRootNode.setText(response.eventData.value); + break; + case 'description': + contextRootNode.setTooltip(response.eventData.value); + break; + case 'rank': + resourceTree.refresh(); + break; + // no default + } + } + } + } }); - MODx.grid.Context.superclass.constructor.call(this,config); }; -Ext.extend(MODx.grid.Context,MODx.grid.Grid,{ +Ext.extend(MODx.grid.Context, MODx.grid.Grid, { + getMenu: function() { - var r = this.getSelectionModel().getSelected(); - var p = r.data.perm; - var m = []; - if (p.indexOf('pnew') != -1) { - m.push({ - text: _('duplicate') - ,handler: this.duplicateContext - ,scope: this + const + record = this.getSelectionModel().getSelected(), + menu = [] + ; + if (this.userCanCreate && this.userCanDuplicateRecord(record)) { + menu.push({ + text: _('duplicate'), + handler: this.duplicateContext, + scope: this }); } - - if (p.indexOf('pedit') != -1) { - m.push({ - text: _('edit') - ,handler: this.updateContext + if (this.userCanEdit && this.userCanEditRecord(record)) { + menu.push({ + text: _('edit'), + handler: this.updateContext }); } - - if (p.indexOf('premove') != -1) { - m.push('-'); - m.push({ - text: _('delete') - ,handler: this.remove - ,scope: this + if (this.userCanDelete && this.userCanDeleteRecord(record)) { + if (menu.length > 0) { + menu.push('-'); + } + menu.push({ + text: _('delete'), + handler: this.remove, + scope: this }); } - return m; - } + return menu; + }, - ,create: function(btn, e) { + create: function(btn, e) { if (this.createWindow) { this.createWindow.destroy(); } this.createWindow = MODx.load({ xtype: 'modx-window-context-create', - closeAction:'close', + closeAction: 'close', listeners: { - 'success': { + success: { fn: function() { this.afterAction(); }, @@ -155,34 +269,39 @@ Ext.extend(MODx.grid.Context,MODx.grid.Grid,{ } }); this.createWindow.show(e.target); - } + }, - ,updateContext: function(itm,e) { - MODx.loadPage('context/update', 'key='+this.menu.record.key); - } + updateContext: function(itm, e) { + MODx.loadPage('context/update', `key=${this.menu.record.key}`); + }, - ,duplicateContext: function() { - var r = { - key: this.menu.record.key - ,newkey: '' - }; - var w = MODx.load({ - xtype: 'modx-window-context-duplicate' - ,record: r - ,listeners: { - 'success': {fn:function() { - this.refresh(); - var tree = Ext.getCmp('modx-resource-tree'); - if (tree) { - tree.refresh(); + duplicateContext: function() { + const + record = { + key: this.menu.record.key, + newkey: '' + }, + window = MODx.load({ + xtype: 'modx-window-context-duplicate', + record: record, + listeners: { + success: { + fn: function() { + this.refresh(); + const tree = Ext.getCmp('modx-resource-tree'); + if (tree) { + tree.refresh(); + } + }, + scope: this } - },scope:this} - } - }); - w.show(); - } + } + }) + ; + window.show(); + }, - ,remove: function(btn, e) { + remove: function(btn, e) { MODx.msg.confirm({ title: _('warning'), text: _('context_remove_confirm'), @@ -192,7 +311,7 @@ Ext.extend(MODx.grid.Context,MODx.grid.Grid,{ key: this.menu.record.key }, listeners: { - 'success': { + success: { fn: function() { this.afterAction(); }, @@ -200,10 +319,10 @@ Ext.extend(MODx.grid.Context,MODx.grid.Grid,{ } } }); - } + }, - ,afterAction: function() { - var cmp = Ext.getCmp('modx-resource-tree'); + afterAction: function() { + const cmp = Ext.getCmp('modx-resource-tree'); if (cmp) { cmp.refresh(); } @@ -211,7 +330,7 @@ Ext.extend(MODx.grid.Context,MODx.grid.Grid,{ this.refresh(); } }); -Ext.reg('modx-grid-contexts',MODx.grid.Context); +Ext.reg('modx-grid-contexts', MODx.grid.Context); /** * Generates the create context window. @@ -221,40 +340,41 @@ Ext.reg('modx-grid-contexts',MODx.grid.Context); * @param {Object} config An object of options. * @xtype modx-window-context-create */ -MODx.window.CreateContext = function(config) { - config = config || {}; - Ext.applyIf(config,{ - title: _('create') - ,url: MODx.config.connector_url - ,action: 'Context/Create' - ,fields: [{ - xtype: 'textfield' - ,fieldLabel: _('context_key') - ,name: 'key' - ,anchor: '100%' - ,maxLength: 100 - },{ - xtype: 'textfield' - ,fieldLabel: _('name') - ,name: 'name' - ,anchor: '100%' - ,maxLength: 100 - },{ - xtype: 'textarea' - ,fieldLabel: _('description') - ,name: 'description' - ,anchor: '100%' - ,grow: true - },{ - xtype: 'numberfield' - ,fieldLabel: _('rank') - ,name: 'rank' - ,allowBlank: true - ,anchor: '100%' - }] - ,keys: [] +MODx.window.CreateContext = function(config = {}) { + Ext.applyIf(config, { + title: _('create'), + url: MODx.config.connector_url, + action: 'Context/Create', + formDefaults: { + anchor: '100%' + }, + fields: [{ + xtype: 'textfield', + fieldLabel: _('context_key'), + name: 'key', + maxLength: 100, + allowBlank: false, + blankText: _('context_err_ns_key') + }, { + xtype: 'textfield', + fieldLabel: _('name'), + name: 'name', + maxLength: 100, + allowBlank: false, + blankText: _('context_err_ns_name') + }, { + xtype: 'textarea', + fieldLabel: _('description'), + name: 'description', + grow: true + }, { + xtype: 'numberfield', + fieldLabel: _('rank'), + name: 'rank' + }], + keys: [] }); - MODx.window.CreateContext.superclass.constructor.call(this,config); + MODx.window.CreateContext.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.CreateContext,MODx.Window); -Ext.reg('modx-window-context-create',MODx.window.CreateContext); +Ext.extend(MODx.window.CreateContext, MODx.Window); +Ext.reg('modx-window-context-create', MODx.window.CreateContext); diff --git a/manager/assets/modext/widgets/system/modx.panel.context.js b/manager/assets/modext/widgets/system/modx.panel.context.js index 9a4270cab05..fec00abc7e0 100644 --- a/manager/assets/modext/widgets/system/modx.panel.context.js +++ b/manager/assets/modext/widgets/system/modx.panel.context.js @@ -4,152 +4,176 @@ * @param {Object} config An object of config properties * @xtype modx-panel-context */ -MODx.panel.Context = function(config) { - config = config || {}; - Ext.applyIf(config,{ - url: MODx.config.connector_url - ,baseParams: { +MODx.panel.Context = function(config = {}) { + Ext.applyIf(config, { + url: MODx.config.connector_url, + baseParams: { action: 'Context/Get' - } - ,id: 'modx-panel-context' - ,cls: 'container' - ,class_key: 'modContext' - ,plugin: '' - ,bodyStyle: '' - ,items: [this.getPageHeader(config), MODx.getPageStructure([{ - title: _('general_information') - ,autoHeight: true - ,layout: 'form' - ,defaults: { border: false ,msgTarget: 'side' } - ,items:[{ - xtype: 'panel' - ,border: false - ,cls:'main-wrapper' - ,layout: 'form' - ,items: [{ - xtype: 'statictextfield' - ,fieldLabel: _('key') - ,name: 'key' - ,width: 300 - ,maxLength: 100 - ,enableKeyEvents: true - ,allowBlank: true - ,value: config.context - ,submitValue: true - },{ - xtype: 'textfield' - ,fieldLabel: _('name') - ,name: 'name' - ,width: 300 - ,maxLength: 191 - },{ - xtype: 'textarea' - ,fieldLabel: _('description') - ,name: 'description' - ,width: 300 - ,grow: true - },{ - xtype: 'numberfield' - ,fieldLabel: _('rank') - ,name: 'rank' - ,allowBlank: true - ,width: 300 - },{ - html: MODx.onContextFormRender - ,border: false + }, + id: 'modx-panel-context', + cls: 'container', + class_key: 'modContext', + plugin: '', + bodyStyle: '', + items: [this.getPageHeader(config), MODx.getPageStructure([{ + title: _('general_information'), + autoHeight: true, + layout: 'form', + defaults: { border: false, msgTarget: 'side' }, + items: [{ + xtype: 'modx-description', + id: 'modx-context-general-desc', + hidden: true, + html: '' + }, { + xtype: 'panel', + border: false, + cls: 'main-wrapper', + layout: 'form', + items: [{ + xtype: 'statictextfield', + fieldLabel: _('key'), + name: 'key', + width: 300, + maxLength: 100, + enableKeyEvents: true, + value: config.context, + submitValue: true + }, { + xtype: config.context === 'mgr' ? 'statictextfield' : 'textfield', + fieldLabel: _('name'), + name: 'name', + width: 300, + maxLength: 191 + }, { + xtype: config.context === 'mgr' ? 'statictextarea' : 'textarea', + fieldLabel: _('description'), + name: 'description', + width: 300, + grow: true + }, { + xtype: config.context === 'mgr' ? 'statictextfield' : 'numberfield', + fieldLabel: _('rank'), + name: 'rank', + width: 300 + }, { + html: MODx.onContextFormRender, + border: false }] }] - },{ - title: _('context_settings') - ,autoHeight: true - ,layout: 'form' - ,items: [{ - html: '

        '+_('context_settings_desc')+'

        ' - ,id: 'modx-context-settings-desc' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-context-settings' - ,cls:'main-wrapper' - ,title: '' - ,preventRender: true - ,context_key: config.context - ,listeners: { - 'afteredit': {fn:function() { this.markDirty(); },scope:this} + }, { + title: _('context_settings'), + autoHeight: true, + layout: 'form', + items: [{ + html: `

        ${_('context_settings_desc')}

        `, + id: 'modx-context-settings-desc', + xtype: 'modx-description' + }, { + xtype: 'modx-grid-context-settings', + cls: 'main-wrapper', + title: '', + preventRender: true, + context_key: config.context, + listeners: { + afteredit: { + fn: function() { + this.markDirty(); + }, + scope: this + } } }] - },{ - title: _('access_permissions') - ,autoHeight: true - ,items:[{ - xtype: 'modx-grid-access-context' - ,cls:'main-wrapper' - ,title: '' - ,preventRender: true - ,context_key: config.context - ,listeners: { - 'afteredit': {fn:function() { this.markDirty(); },scope:this} + }, { + title: _('access_permissions'), + autoHeight: true, + items: [{ + xtype: 'modx-grid-access-context', + cls: 'main-wrapper', + title: '', + preventRender: true, + context_key: config.context, + listeners: { + afteredit: { fn: function() { this.markDirty(); }, scope: this } } }] - }],{ + }], { id: 'modx-context-tabs' - })] - ,useLoadingMask: true - ,listeners: { - 'setup': {fn:this.setup,scope:this} - ,'success': {fn:this.success,scope:this} - ,'beforeSubmit': {fn:this.beforeSubmit,scope:this} + })], + useLoadingMask: true, + listeners: { + setup: { fn: this.setup, scope: this }, + success: { fn: this.success, scope: this }, + beforeSubmit: { fn: this.beforeSubmit, scope: this } } }); - MODx.panel.Context.superclass.constructor.call(this,config); + MODx.panel.Context.superclass.constructor.call(this, config); }; -Ext.extend(MODx.panel.Context,MODx.FormPanel,{ - initialized: false +Ext.extend(MODx.panel.Context, MODx.FormPanel, { + initialized: false, - ,setup: function() { + setup: function() { if (this.initialized || (this.config.context === '' || this.config.context === 0)) { this.fireEvent('ready'); return false; } MODx.Ajax.request({ - url: this.config.url - ,params: { - action: 'Context/Get' - ,key: this.config.context - } - ,listeners: { - 'success': {fn:function(r) { - this.getForm().setValues(r.object); - Ext.getCmp('modx-header-breadcrumbs').updateHeader(Ext.util.Format.htmlEncode(r.object.key)); - this.fireEvent('ready'); - MODx.fireEvent('ready'); - this.initialized = true; - },scope:this} + url: this.config.url, + params: { + action: 'Context/Get', + key: this.config.context + }, + listeners: { + success: { + fn: function(response) { + const record = response.object; + this.config.record = record; + if (record.isProtected && record.key !== 'web') { + const descriptionCmp = Ext.getCmp('modx-context-general-desc'); + descriptionCmp.update(_('context_reserved_general_desc')); + descriptionCmp.show(); + } + this.getForm().setValues(record); + Ext.getCmp('modx-header-breadcrumbs').updateHeader(Ext.util.Format.htmlEncode(record.key)); + this.fireEvent('ready'); + MODx.fireEvent('ready'); + this.initialized = true; + }, + scope: this + } } }); - } - - ,beforeSubmit: function(o) { - var r = {}; - - var g = Ext.getCmp('modx-grid-context-settings'); - if (g) { r.settings = g.encodeModified(); } + }, - Ext.apply(o.form.baseParams,r); - } - - ,success: function(o) { - var g = Ext.getCmp('modx-grid-context-settings'); - if (g) { g.getStore().commitChanges(); } + beforeSubmit: function(o) { + const + data = {}, + settingsCmp = Ext.getCmp('modx-grid-context-settings') + ; + if (settingsCmp) { + data.settings = settingsCmp.encodeModified(); + } + Ext.apply(o.form.baseParams, data); + }, - var t = parent.Ext.getCmp('modx-resource-tree'); - if (t) { t.refresh(); } - } + success: function(o) { + const + settingsCmp = Ext.getCmp('modx-grid-context-settings'), + tree = Ext.getCmp('modx-resource-tree') + ; + if (settingsCmp) { + settingsCmp.getStore().commitChanges(); + } + if (tree) { + tree.refresh(); + } + }, - ,getPageHeader: function(config) { + getPageHeader: function(config) { return MODx.util.getHeaderBreadCrumbs('modx-context-name', [{ text: _('contexts'), href: MODx.getPage('context') }]); } }); -Ext.reg('modx-panel-context',MODx.panel.Context); +Ext.reg('modx-panel-context', MODx.panel.Context); diff --git a/manager/assets/modext/widgets/system/modx.panel.dashboards.js b/manager/assets/modext/widgets/system/modx.panel.dashboards.js index 1be48b36c5d..3163c36b482 100644 --- a/manager/assets/modext/widgets/system/modx.panel.dashboards.js +++ b/manager/assets/modext/widgets/system/modx.panel.dashboards.js @@ -4,44 +4,43 @@ * @param {Object} config An object of configuration properties * @xtype modx-panel-dashboards */ -MODx.panel.Dashboards = function(config) { - config = config || {}; - Ext.applyIf(config,{ - id: 'modx-panel-dashboards' - ,cls: 'container' - ,defaults: { collapsible: false ,autoHeight: true } - ,items: [{ - html: _('dashboards') - ,id: 'modx-dashboards-header' - ,xtype: 'modx-header' - },MODx.getPageStructure([{ - layout: 'form' - ,title: _('dashboards') - ,items: [{ - html: '

        '+_('dashboards.intro_msg')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-dashboards' - ,cls: 'main-wrapper' - ,preventRender: true +MODx.panel.Dashboards = function(config = {}) { + Ext.applyIf(config, { + id: 'modx-panel-dashboards', + cls: 'container', + defaults: { collapsible: false, autoHeight: true }, + items: [{ + html: _('dashboards'), + id: 'modx-dashboards-header', + xtype: 'modx-header' + }, MODx.getPageStructure([{ + layout: 'form', + title: _('dashboards'), + items: [{ + html: `

        ${_('dashboards.intro_msg')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-dashboards', + cls: 'main-wrapper', + preventRender: true }] - },{ - layout: 'form' - ,title: _('widgets') - ,items: [{ - html: '

        '+_('widgets.intro_msg')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-dashboard-widgets' - ,cls: 'main-wrapper' - ,preventRender: true + }, { + layout: 'form', + title: _('widgets'), + items: [{ + html: `

        ${_('widgets.intro_msg')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-dashboard-widgets', + cls: 'main-wrapper', + preventRender: true }] }])] }); - MODx.panel.Dashboards.superclass.constructor.call(this,config); + MODx.panel.Dashboards.superclass.constructor.call(this, config); }; -Ext.extend(MODx.panel.Dashboards,MODx.FormPanel); -Ext.reg('modx-panel-dashboards',MODx.panel.Dashboards); +Ext.extend(MODx.panel.Dashboards, MODx.FormPanel); +Ext.reg('modx-panel-dashboards', MODx.panel.Dashboards); /** * @class MODx.grid.Dashboards @@ -52,72 +51,108 @@ Ext.reg('modx-panel-dashboards',MODx.panel.Dashboards); MODx.grid.Dashboards = function(config = {}) { const queryValue = this.applyRequestFilter(0, 'query', 'tab', true); this.sm = new Ext.grid.CheckboxSelectionModel(); - Ext.applyIf(config,{ - url: MODx.config.connector_url - ,baseParams: { + Ext.applyIf(config, { + id: 'modx-grid-dashboards', + url: MODx.config.connector_url, + baseParams: { action: 'System/Dashboard/GetList', usergroup: MODx.request.usergroup || null - } - ,fields: [ + }, + fields: [ 'id', 'name', 'description', - 'cls' - ] - ,paging: true - ,autosave: true - ,save_action: 'System/Dashboard/UpdateFromGrid' - ,remoteSort: true - ,sm: this.sm - ,columns: [this.sm,{ - header: _('id') - ,dataIndex: 'id' - ,width: 50 - ,sortable: true - },{ - header: _('name') - ,dataIndex: 'name' - ,width: 150 - ,sortable: true - ,editor: { xtype: 'textfield' ,allowBlank: false } - ,renderer: { fn: function(v,md,record) { - return this.renderLink(v, { - href: '?a=system/dashboards/update&id=' + record.data.id - }); - }, scope: this } - },{ - header: _('description') - ,dataIndex: 'description' - ,width: 300 - ,sortable: false - ,editor: { xtype: 'textarea' } - }] - ,tbar: [ - { - text: _('create') - ,cls:'primary-button' - ,handler: this.createDashboard - ,scope: this - },{ - text: _('bulk_actions') - ,menu: [{ - text: _('selected_remove') - ,handler: this.removeSelected - ,scope: this - }] - },'->',{ - xtype: 'modx-combo-usergroup' - ,itemId: 'filter-usergroup' - ,emptyText: _('user_group_filter') - ,baseParams: { - action: 'Security/Group/GetList' - ,addAll: true + 'creator' + ], + paging: true, + autosave: true, + save_action: 'System/Dashboard/UpdateFromGrid', + remoteSort: true, + sm: this.sm, + columns: [this.sm, { + header: _('id'), + dataIndex: 'id', + width: 50, + sortable: true + }, { + header: _('name'), + dataIndex: 'name', + id: 'modx-dashboard--name', + width: 150, + sortable: true, + editor: { + xtype: 'textfield', + allowBlank: false, + blankText: _('dashboard_err_ns_name'), + validationEvent: 'change', + validator: function(value) { + const grid = Ext.getCmp('modx-grid-dashboards'), + reserved = this.gridEditor.record.json.reserved.name + ; + if (grid.valueIsReserved(reserved, value)) { + const msg = _('dashboard_err_name_reserved', { reservedName: value }); + Ext.Msg.alert(_('error'), msg); + return false; + } + return true; } - ,value: MODx.request.usergroup || null - ,width: 200 - ,listeners: { + }, + renderer: { + fn: function(value, metaData, record) { + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return this.userCanEditRecord(record) + ? this.renderLink(value, { + href: `?a=system/dashboards/update&id=${record.data.id}`, + title: _('dashboard_edit') + }) + : value + ; + }, + scope: this + } + }, { + header: _('description'), + dataIndex: 'description', + id: 'modx-dashboard--description', + width: 300, + sortable: false, + editor: { + xtype: 'textarea' + }, + renderer: { + fn: function(value, metaData, record) { + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses(record, [record.json.isProtected]); + return value; + }, + scope: this + } + }, + this.getCreatorColumnConfig('dashboard') + ], + tbar: [ + { + text: _('create'), + cls: 'primary-button', + handler: this.createDashboard, + scope: this + }, + this.getBulkActionsButton('dashboard', 'System/Dashboard/RemoveMultiple'), + '->', + { + xtype: 'modx-combo-usergroup', + itemId: 'filter-usergroup', + emptyText: _('user_group_filter'), + baseParams: { + action: 'Security/Group/GetList', + addAll: true + }, + value: MODx.request.usergroup || null, + width: 200, + listeners: { select: { - fn: function (cmp, record, selectedIndex) { + fn: function(cmp, record, selectedIndex) { this.applyGridFilter(cmp, 'usergroup'); }, scope: this @@ -126,105 +161,71 @@ MODx.grid.Dashboards = function(config = {}) { }, this.getQueryFilterField(`filter-query:${queryValue}`), this.getClearFiltersButton('filter-usergroup, filter-query') - ] + ], + viewConfig: this.getViewConfig() }); - MODx.grid.Dashboards.superclass.constructor.call(this,config); + MODx.grid.Dashboards.superclass.constructor.call(this, config); + + this.gridMenuActions = ['edit', 'delete', 'duplicate']; + + // Note there are currently no action-specific permissions for Dashboards + this.setUserCanEdit(['dashboards']); + this.setUserCanCreate(['dashboards']); + this.setUserCanDelete(['dashboards']); + this.setShowActionsMenu(); }; -Ext.extend(MODx.grid.Dashboards,MODx.grid.Grid,{ +Ext.extend(MODx.grid.Dashboards, MODx.grid.Grid, { getMenu: function() { - var r = this.getSelectionModel().getSelected(); - var p = r.data.cls; - - var m = []; - if (this.getSelectionModel().getCount() > 1) { - m.push({ - text: _('selected_remove') - ,handler: this.removeSelected - ,scope: this + const + record = this.getSelectionModel().getSelected(), + menu = [] + ; + if (this.userCanEdit && this.userCanEditRecord(record)) { + menu.push({ + text: _('edit'), + handler: this.updateDashboard }); - } else { - if (p.indexOf('pupdate') != -1) { - m.push({ - text: _('edit') - ,handler: this.updateDashboard - }); - } - if (p.indexOf('pduplicate') != -1) { - m.push({ - text: _('duplicate') - ,handler: this.duplicateDashboard - }); - } - if (p.indexOf('premove') != -1 && r.data.id != 1 && r.data.name != 'Default') { - if (m.length > 0) m.push('-'); - m.push({ - text: _('delete') - ,handler: this.removeDashboard - }); - } } - if (m.length > 0) { - this.addContextMenuItem(m); + if (this.userCanCreate && this.userCanDuplicateRecord(record)) { + menu.push({ + text: _('duplicate'), + handler: this.duplicateDashboard + }); } - } + if (this.userCanDelete && this.userCanDeleteRecord(record)) { + if (menu.length > 0) { + menu.push('-'); + } + menu.push({ + text: _('delete'), + handler: this.confirm.createDelegate(this, ['System/Dashboard/Remove', 'dashboard_remove_confirm']) + }); + } + return menu; + }, - ,createDashboard: function() { + createDashboard: function() { MODx.loadPage('system/dashboards/create'); - } + }, - ,updateDashboard: function() { - MODx.loadPage('system/dashboards/update', 'id='+this.menu.record.id); - } + updateDashboard: function() { + MODx.loadPage('system/dashboards/update', `id=${this.menu.record.id}`); + }, - ,duplicateDashboard: function(btn,e) { + duplicateDashboard: function(btn, e) { MODx.Ajax.request({ - url: this.config.url - ,params: { - action: 'System/Dashboard/Duplicate' - ,id: this.menu.record.id - } - ,listeners: { - 'success': {fn:this.refresh,scope:this} - } - }); - } - - ,removeDashboard: function() { - MODx.msg.confirm({ - title: _('delete') - ,text: _('dashboard_remove_confirm') - ,url: this.config.url - ,params: { - action: 'System/Dashboard/Remove' - ,id: this.menu.record.id - } - ,listeners: { - 'success': {fn:this.refresh,scope:this} - } - }); - } - - ,removeSelected: function() { - var cs = this.getSelectedAsList(); - if (cs === false) return false; - - MODx.msg.confirm({ - title: _('selected_remove') - ,text: _('dashboard_remove_multiple_confirm') - ,url: this.config.url - ,params: { - action: 'System/Dashboard/RemoveMultiple' - ,dashboards: cs - } - ,listeners: { - 'success': {fn:function(r) { - this.getSelectionModel().clearSelections(true); - this.refresh(); - },scope:this} + url: this.config.url, + params: { + action: 'System/Dashboard/Duplicate', + id: this.menu.record.id + }, + listeners: { + success: { + fn: this.refresh, + scope: this + } } }); - return true; } - }); -Ext.reg('modx-grid-dashboards',MODx.grid.Dashboards); +Ext.reg('modx-grid-dashboards', MODx.grid.Dashboards); diff --git a/manager/assets/modext/widgets/system/modx.panel.filetree.js b/manager/assets/modext/widgets/system/modx.panel.filetree.js index 93f9c3d830e..1053b15f225 100644 --- a/manager/assets/modext/widgets/system/modx.panel.filetree.js +++ b/manager/assets/modext/widgets/system/modx.panel.filetree.js @@ -6,18 +6,24 @@ * @param {Object} config * @xtype modx-panel-filetree */ -MODx.panel.FileTree = function(config) { - config = config || {}; +MODx.panel.FileTree = function(config = {}) { Ext.applyIf(config, { - _treePrefix: 'source-tree-' - ,autoHeight: true - ,defaults: { - autoHeight: true - ,border: false + _treePrefix: 'source-tree-', + autoHeight: true, + defaults: { + autoHeight: true, + border: false } }); MODx.panel.FileTree.superclass.constructor.call(this, config); - this.on('render', this.getSourceList, this); + this.on({ + render: { + fn: function(panel) { + this.getSourceList(); + }, + scope: this + } + }); }; Ext.extend(MODx.panel.FileTree, Ext.Container, { /** @@ -25,51 +31,49 @@ Ext.extend(MODx.panel.FileTree, Ext.Container, { */ getSourceList: function() { MODx.Ajax.request({ - url: MODx.config.connector_url - ,params: { - action: 'Source/GetList' - ,limit: 0 - } - ,listeners: { + url: MODx.config.connector_url, + params: { + action: 'Source/GetList', + limit: 0 + }, + listeners: { success: { fn: function(data) { this.onSourceListReceived(data.results); - } - ,scope:this - } - ,failure: { + }, + scope: this + }, + failure: { fn: function(data) { // Check if this really is an error - if (data.total > 0 && data.results != undefined) { + if (data.total > 0 && data.results !== undefined) { this.onSourceListReceived(data.results); } return false; - } - ,scope: this + }, + scope: this } } - }) - } + }); + }, /** * Iterate over the given media sources list to add their trees * * @param {Array} sources */ - ,onSourceListReceived: function(sources) { - for (var k = 0; k < sources.length; k++) { - var source = sources[k] - ,exists = this.getComponent(this._treePrefix + source.id); - - if (!exists) { - var tree = this.loadTree(source); + onSourceListReceived: function(sources) { + sources.forEach(source => { + const + sourceTreeExists = this.getComponent(this._treePrefix + source.id), + newSourceTree = !sourceTreeExists ? this.loadTree(source) : false + ; + if (newSourceTree) { + this.add(newSourceTree); } - - this.add(tree); - tree = exists = void 0; - } + }); this.doLayout(); - } + }, /** * Load the tree configuration for the given media source @@ -77,42 +81,31 @@ Ext.extend(MODx.panel.FileTree, Ext.Container, { * @param {Object} source * @returns {Object} */ - ,loadTree: function(source) { - var params = {}; - if (location.search) { - var parts = location.search.substring(1).split('&'); - - for (var i = 0; i < parts.length; i++) { - var nv = parts[i].split('='); - if (!nv[0]) continue; - params[nv[0]] = nv[1] || true; + loadTree: function(source) { + let expandSource = false; + if (window.location.search) { + const params = MODx.util.UrlParams.get(); + if (Object.hasOwn(params, 'source')) { + expandSource = source.id === params.source; } } - var activeSource = params.source, - expandSource = false; - - if (source.id == activeSource) { - expandSource = true; - } - return MODx.load({ - xtype: 'modx-tree-directory' - ,autoExpandRoot: expandSource - ,itemId: this._treePrefix + source.id - ,stateId: this._treePrefix + source.id - ,id: this._treePrefix + source.id - ,cls: source.cls || '' - ,rootName: source.name - ,rootQtip: source.description || '' - ,hideSourceCombo: true - ,source: source.id - ,rootIconCls: source.iconCls || '' - ,tbar: false - ,tbarCfg: { + xtype: 'modx-tree-directory', + autoExpandRoot: expandSource, + itemId: this._treePrefix + source.id, + stateId: this._treePrefix + source.id, + id: this._treePrefix + source.id, + cls: source.cls || '', + rootName: source.name, + rootQtip: source.description || '', + hideSourceCombo: true, + source: source.id, + rootIconCls: source.iconCls || '', + tbar: false, + tbarCfg: { hidden: true } }); } }); Ext.reg('modx-panel-filetree', MODx.panel.FileTree); - diff --git a/manager/assets/modext/workspace/namespace/modx.namespace.panel.js b/manager/assets/modext/workspace/namespace/modx.namespace.panel.js index 90aaaba74e7..d92bbcae317 100644 --- a/manager/assets/modext/workspace/namespace/modx.namespace.panel.js +++ b/manager/assets/modext/workspace/namespace/modx.namespace.panel.js @@ -6,34 +6,41 @@ * @param {Object} config An object of configuration properties * @xtype modx-panel-namespaces */ -MODx.panel.Namespaces = function(config) { - config = config || {}; - Ext.applyIf(config,{ - id: 'modx-panel-namespaces' - ,cls: 'container' - ,bodyStyle: '' - ,defaults: { collapsible: false ,autoHeight: true } - ,items: [{ - html: _('namespaces') - ,id: 'modx-namespaces-header' - ,xtype: 'modx-header' - },MODx.getPageStructure([{ - title: _('namespaces') - ,layout: 'form' - ,items: [{ - html: '

        '+_('namespaces_desc')+'

        ' - ,xtype: 'modx-description' - },{ - xtype: 'modx-grid-namespace' - ,cls:'main-wrapper' - ,preventRender: true - }] - }])] +MODx.panel.Namespaces = function(config = {}) { + Ext.applyIf(config, { + id: 'modx-panel-namespaces', + cls: 'container', + bodyStyle: '', + defaults: { + collapsible: false, + autoHeight: true + }, + items: [ + { + html: _('namespaces'), + id: 'modx-namespaces-header', + xtype: 'modx-header' + }, + MODx.getPageStructure([{ + title: _('namespaces'), + layout: 'form', + items: [ + { + html: `

        ${_('namespaces_desc')}

        `, + xtype: 'modx-description' + }, { + xtype: 'modx-grid-namespace', + cls: 'main-wrapper', + preventRender: true + } + ] + }]) + ] }); - MODx.panel.Namespaces.superclass.constructor.call(this,config); + MODx.panel.Namespaces.superclass.constructor.call(this, config); }; -Ext.extend(MODx.panel.Namespaces,MODx.FormPanel); -Ext.reg('modx-panel-namespaces',MODx.panel.Namespaces); +Ext.extend(MODx.panel.Namespaces, MODx.FormPanel); +Ext.reg('modx-panel-namespaces', MODx.panel.Namespaces); /** * Loads a grid for managing namespaces. @@ -43,121 +50,169 @@ Ext.reg('modx-panel-namespaces',MODx.panel.Namespaces); * @param {Object} config An object of configuration properties * @xtype modx-grid-namespace */ -MODx.grid.Namespace = function(config) { - config = config || {}; +MODx.grid.Namespace = function(config = {}) { this.sm = new Ext.grid.CheckboxSelectionModel(); - Ext.applyIf(config,{ - url: MODx.config.connector_url - ,baseParams: { + Ext.applyIf(config, { + id: 'modx-grid-namespaces', + url: MODx.config.connector_url, + baseParams: { action: 'Workspace/PackageNamespace/GetList' - } - ,fields: [ - 'id', + }, + fields: [ 'name', 'path', 'assets_path', - 'perm' - ] - ,anchor: '100%' - ,paging: true - ,autosave: true - ,save_action: 'Workspace/PackageNamespace/UpdateFromGrid' - ,primaryKey: 'name' - ,remoteSort: true - ,sm: this.sm - ,columns: [this.sm,{ - header: _('name') - ,dataIndex: 'name' - ,width: 200 - ,sortable: true - },{ - header: _('namespace_path') - ,dataIndex: 'path' - ,width: 500 - ,sortable: false - ,editor: { xtype: 'textfield' } - },{ - header: _('namespace_assets_path') - ,dataIndex: 'assets_path' - ,width: 500 - ,sortable: false - ,editor: { xtype: 'textfield' } - }] - ,tbar: [ - { - text: _('create') - ,handler: { xtype: 'modx-window-namespace-create' ,blankValues: true } - ,cls:'primary-button' - ,scope: this + 'perm', + 'creator' + ], + anchor: '100%', + paging: true, + autosave: true, + save_action: 'Workspace/PackageNamespace/UpdateFromGrid', + primaryKey: 'name', + remoteSort: true, + sm: this.sm, + columns: [this.sm, { + header: _('name'), + dataIndex: 'name', + id: 'modx-namespace--name', + width: 200, + sortable: true, + // because PK is name, allowing edit is tricky as implemented; leave for now + listeners: { + click: { + fn: function(column, grid, rowIndex, e) { + if (e.target.classList.contains('simulated-link')) { + this.updateNamespace(e); + } + }, + scope: this + } + } + }, { + header: _('namespace_path'), + dataIndex: 'path', + id: 'modx-namespace--path', + width: 500, + sortable: false, + editor: { + xtype: 'textfield' }, - '->', - this.getQueryFilterField(), - this.getClearFiltersButton() - ] + renderer: { + fn: function(value, metaData, record) { + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses( + record, + [record.json.isProtected, record.json.isExtrasNamespace], + '', + false + ); + return value; + }, + scope: this + } + }, { + header: _('namespace_assets_path'), + dataIndex: 'assets_path', + id: 'modx-namespace--assets_path', + width: 500, + sortable: false, + editor: { + xtype: 'textfield' + }, + renderer: { + fn: function(value, metaData, record) { + // eslint-disable-next-line no-param-reassign + metaData.css = this.setEditableCellClasses( + record, + [record.json.isProtected, record.json.isExtrasNamespace], + '', + false + ); + return value; + }, + scope: this + } + }, + this.getCreatorColumnConfig('namespace') + ], + tbar: [{ + text: _('create'), + handler: { + xtype: 'modx-window-namespace-create', + blankValues: true + }, + cls: 'primary-button', + scope: this + }, + this.getBulkActionsButton('namespace', 'Workspace/PackageNamespace/RemoveMultiple', 'string'), + '->', + this.getQueryFilterField(), + this.getClearFiltersButton() + ], + viewConfig: this.getViewConfig() + }); + MODx.grid.Namespace.superclass.constructor.call(this, config); + + this.gridMenuActions = ['edit', 'delete']; + + // Note there are currently no action-specific permissions for Namespaces + this.setUserCanEdit(['namespaces']); + this.setUserCanCreate(['namespaces']); + this.setUserCanDelete(['namespaces']); + this.setShowActionsMenu(); + + this.on({ + render: function() { + this.setEditableColumnAccess( + ['modx-namespace--path', 'modx-namespace--assets_path'] + ); + }, + beforeedit: function(e) { + return !(e.record.json.isProtected || e.record.json.isExtrasNamespace); + } }); - MODx.grid.Namespace.superclass.constructor.call(this,config); }; -Ext.extend(MODx.grid.Namespace,MODx.grid.Grid,{ +Ext.extend(MODx.grid.Namespace, MODx.grid.Grid, { + getMenu: function() { - var r = this.getSelectionModel().getSelected(); - var p = r.data.perm; - var m = []; - if (this.getSelectionModel().getCount() > 1) { - m.push({ - text: _('selected_remove') - ,handler: this.removeSelected - ,scope: this + const record = this.getSelectionModel().getSelected(), + menu = [] + ; + if (this.userCanEdit && this.userCanEditRecord(record)) { + menu.push({ + text: _('edit'), + handler: this.updateNamespace }); - } else { - m.push({ - text: _('edit') - ,handler: this.namespaceUpdate - }); - if (p.indexOf('premove') != -1 && this.menu.record.name != 'core') { - m.push({ - text: _('delete') - ,handler: this.remove.createDelegate(this,['namespace_remove_confirm','Workspace/PackageNamespace/Remove']) - }); + } + if (this.userCanDelete && !record.json.isProtected) { + if (menu.length > 0) { + menu.push('-'); } + menu.push({ + text: _('delete'), + handler: this.remove.createDelegate(this, ['namespace_remove_confirm', 'Workspace/PackageNamespace/Remove']) + }); } - return m; - } + return menu; + }, - ,namespaceUpdate: function(elem, vent) { - var win = MODx.load({ - xtype: 'modx-window-namespace-update' - ,record: this.menu.record - ,listeners: { - success: { - fn: this.refresh - ,scope: this + updateNamespace: function(e) { + const + record = this.getSelectionModel().getSelected().data, + window = MODx.load({ + xtype: 'modx-window-namespace-update', + record: record, + listeners: { + success: { + fn: this.refresh, + scope: this + } } - } - }); - win.setValues(this.menu.record); - win.show(vent.target); - } - - ,removeSelected: function() { - var cs = this.getSelectedAsList(); - if (cs === false) return false; - - MODx.msg.confirm({ - title: _('selected_remove') - ,text: _('namespace_remove_multiple_confirm') - ,url: this.config.url - ,params: { - action: 'Workspace/PackageNamespace/RemoveMultiple' - ,namespaces: cs - } - ,listeners: { - 'success': {fn:function(r) { - this.getSelectionModel().clearSelections(true); - this.refresh(); - },scope:this} - } - }); - return true; + }) + ; + window.setValues(record); + window.show(e.target); } }); -Ext.reg('modx-grid-namespace',MODx.grid.Namespace); +Ext.reg('modx-grid-namespace', MODx.grid.Namespace); diff --git a/manager/controllers/default/security/access/policy/update.class.php b/manager/controllers/default/security/access/policy/update.class.php index 86191ce21e7..06bd849135e 100644 --- a/manager/controllers/default/security/access/policy/update.class.php +++ b/manager/controllers/default/security/access/policy/update.class.php @@ -10,6 +10,7 @@ use MODX\Revolution\modAccessPolicy; use MODX\Revolution\modManagerController; +// use MODX\Revolution\Processors\Security\Access\Policy\Get as GetPolicy; /** * Loads the policy management page @@ -18,6 +19,7 @@ * @subpackage manager.controllers */ class SecurityAccessPolicyUpdateManagerController extends modManagerController { + public $classKey = modAccessPolicy::class; public $policyArray = []; /** @@ -58,10 +60,10 @@ public function loadCustomCssJs() { public function process(array $scriptProperties = []) { $placeholders = []; - if (empty($scriptProperties['id']) || strlen($scriptProperties['id']) !== strlen((integer)$scriptProperties['id'])) { + if (empty($scriptProperties['id']) || strlen($scriptProperties['id']) !== strlen((int)$scriptProperties['id'])) { return $this->failure($this->modx->lexicon('access_policy_err_ns')); } - $policy = $this->modx->getObject(modAccessPolicy::class, ['id' => $scriptProperties['id']]); + $policy = $this->modx->getObject($this->classKey, ['id' => $scriptProperties['id']]); if (empty($policy)) return $this->failure($this->modx->lexicon('access_policy_err_nf')); $placeholders['policy'] = $policy; @@ -73,12 +75,23 @@ public function process(array $scriptProperties = []) { 'lexicon', 'class', 'template', - 'parent', + 'parent' ]); $this->policyArray['permissions'] = $policy->getPermissions(); + $this->policyArray['isProtected'] = $policy->isCorePolicy($this->policyArray['name']); + $this->policyArray['reserved'] = ['name' => $this->classKey::getCorePolicies()]; + if ($this->policyArray['isProtected']) { + $this->modx->lexicon->setTranslatedCoreDescriptors($this->policyArray, 'policy'); + } $placeholders['policy'] = $this->policyArray; - + // $this->modx->log( + // \modX::LOG_LEVEL_ERROR, + // "\r\t process: + // \t\$this->policyArray: " . print_r($this->policyArray, true) . + // "\t\$scriptProperties: " . print_r($scriptProperties, true) + // ); return $placeholders; + return ''; } /** diff --git a/manager/controllers/default/source/update.class.php b/manager/controllers/default/source/update.class.php index 1af8e61cdfd..0270b5d45a1 100644 --- a/manager/controllers/default/source/update.class.php +++ b/manager/controllers/default/source/update.class.php @@ -1,4 +1,5 @@ modx->hasPermission('source_edit'); } @@ -40,18 +43,28 @@ public function checkPermissions() { * Register custom CSS/JS for the page * @return void */ - public function loadCustomCssJs() { - $mgrUrl = $this->modx->getOption('manager_url',null,MODX_MANAGER_URL); - $this->addJavascript($mgrUrl.'assets/modext/widgets/core/modx.grid.local.property.js'); - $this->addJavascript($mgrUrl.'assets/modext/widgets/source/modx.grid.source.properties.js'); - $this->addJavascript($mgrUrl.'assets/modext/widgets/source/modx.grid.source.access.js'); - $this->addJavascript($mgrUrl.'assets/modext/widgets/source/modx.panel.source.js'); - $this->addJavascript($mgrUrl.'assets/modext/sections/source/update.js'); - $this->addHtml(''); + public function loadCustomCssJs() + { + $mgrUrl = $this->modx->getOption('manager_url', null, MODX_MANAGER_URL); + $this->addJavascript($mgrUrl . 'assets/modext/widgets/core/modx.grid.local.property.js'); + $this->addJavascript($mgrUrl . 'assets/modext/widgets/source/modx.grid.source.properties.js'); + $this->addJavascript($mgrUrl . 'assets/modext/widgets/source/modx.grid.source.access.js'); + $this->addJavascript($mgrUrl . 'assets/modext/widgets/source/modx.panel.source.js'); + $this->addJavascript($mgrUrl . 'assets/modext/sections/source/update.js'); + $record = $this->modx->toJSON($this->sourceArray); + $defaultProps = $this->modx->toJSON($this->sourceDefaultProperties); + $pageCmp = << + Ext.onReady(function() { + MODx.load({ + xtype: 'modx-page-source-update', + record: {$record}, + defaultProperties: {$defaultProps} + }); + }); + +CMP; + $this->addHtml($pageCmp); } /** @@ -59,23 +72,34 @@ public function loadCustomCssJs() { * @param array $scriptProperties * @return mixed */ - public function process(array $scriptProperties = []) { - if (empty($this->scriptProperties['id']) || strlen($this->scriptProperties['id']) !== strlen((integer)$this->scriptProperties['id'])) { + public function process(array $scriptProperties = []) + { + if (empty($this->scriptProperties['id']) || strlen($this->scriptProperties['id']) !== strlen((int)$this->scriptProperties['id'])) { return $this->failure($this->modx->lexicon('source_err_ns')); } $this->source = $this->modx->getObject(modMediaSource::class, ['id' => $this->scriptProperties['id']]); - if (empty($this->source)) return $this->failure($this->modx->lexicon('source_err_nf')); + if (empty($this->source)) { + return $this->failure($this->modx->lexicon('source_err_nf')); + } $this->sourceArray = $this->source->toArray(); + + $coreSources = modMediaSource::getCoreSources(); + $sourceKey = $this->sourceArray['name']; + if (in_array($sourceKey, $coreSources)) { + $this->sourceArray['isProtected'] = true; + $this->sourceArray['reserved'] = true; + $this->modx->lexicon->setTranslatedCoreDescriptors($this->sourceArray, 'source'); + } $this->getProperties(); $this->getAccess(); - $this->getDefaultProperties(); return []; } - public function getProperties() { + public function getProperties() + { $properties = $this->source->getProperties(); $data = []; foreach ($properties as $property) { @@ -94,7 +118,8 @@ public function getProperties() { $this->sourceArray['properties'] = $data; } - public function getDefaultProperties() { + public function getDefaultProperties() + { $default = $this->source->getDefaultProperties(); $default = $this->source->prepareProperties($default); $data = []; @@ -115,7 +140,8 @@ public function getDefaultProperties() { return $data; } - public function getAccess() { + public function getAccess() + { $c = $this->modx->newQuery(modAccessMediaSource::class); $c->innerJoin(modMediaSource::class, 'Target'); $c->innerJoin(modAccessPolicy::class, 'Policy'); @@ -131,7 +157,7 @@ public function getAccess() { 'policy_name' => 'Policy.name', 'authority_name' => 'MinimumRole.name', ]); - $acls = $this->modx->getCollection(modAccessMediaSource::class,$c); + $acls = $this->modx->getCollection(modAccessMediaSource::class, $c); $access = []; /** @var modAccessMediaSource $acl */ foreach ($acls as $acl) { @@ -158,15 +184,17 @@ public function getAccess() { * * @return string */ - public function getPageTitle() { - return $this->modx->lexicon('source').': '.$this->sourceArray['name']; + public function getPageTitle() + { + return $this->modx->lexicon('source') . ': ' . $this->sourceArray['name']; } /** * Return the location of the template file * @return string */ - public function getTemplateFile() { + public function getTemplateFile() + { return ''; } @@ -174,7 +202,8 @@ public function getTemplateFile() { * Specify the language topics to load * @return array */ - public function getLanguageTopics() { + public function getLanguageTopics() + { return ['source','namespace','propertyset']; } @@ -182,7 +211,8 @@ public function getLanguageTopics() { * Get the Help URL * @return string */ - public function getHelpUrl() { + public function getHelpUrl() + { return 'Media+Sources'; } } diff --git a/manager/templates/default/css/index.css b/manager/templates/default/css/index.css index b7c48e28431..a74fa20dbf6 100644 --- a/manager/templates/default/css/index.css +++ b/manager/templates/default/css/index.css @@ -21162,6 +21162,12 @@ input::-moz-focus-inner { .desc-under .copy-this:hover:active::after { color: #515151; } +.desc-under .copy-this:active { + color: #515151; +} +.desc-under .copy-this:active::after { + color: #515151; +} .desc-under .feedback { margin-left: 1.4rem; color: #1b5ca8; @@ -22330,15 +22336,15 @@ html[dir=rtl] .display-switch .x-fieldset legend [type=checkbox] { background-color: #FFF; } -/* fix combo on grid editor bug */ +.x-grid-editor { + z-index: 9002 !important; +} .x-grid-editor .x-form-field-wrap { background: #f6f2f7 url("../images/modx-theme/form/combo-bck.png") repeat-x scroll 0 100%; } - .x-grid-editor .x-form-field-wrap input { background-color: transparent !important; } - .x-grid-editor .x-form-field-wrap img { background-color: #FFF; background-image: url("../images/modx-theme/form/trigger.png"); @@ -26901,6 +26907,11 @@ textarea.x-form-field { } /* grids */ +.modx-protected-row .x-grid3-cell-inner { + font-style: italic; + color: #234368; +} + .x-small-editor .x-form-field { font-size: 12px !important; } @@ -26913,14 +26924,17 @@ textarea.x-form-field { color: #999 !important; } -a.x-grid-link { +.x-grid-link { color: #234368; - text-decoration: underline; -} - -a.x-grid-link:hover, -a.x-grid-link:focus { text-decoration: none; + border-bottom: 1px dotted #749fd0; +} +.x-grid-link:hover { + color: black; + border-bottom-color: #336299; +} +.x-grid-link.simulated-link { + cursor: pointer; } .x-editable-column { @@ -27252,6 +27266,31 @@ a.x-grid-link:focus { } /* rowactions */ +.x-grid3-row.disable-selection .x-grid3-row-checker, .x-grid3-row.disable-selection.x-grid3-row-selected .x-grid3-row-checker { + position: relative; +} +.x-grid3-row.disable-selection .x-grid3-row-checker::before, .x-grid3-row.disable-selection .x-grid3-row-checker::after, .x-grid3-row.disable-selection.x-grid3-row-selected .x-grid3-row-checker::before, .x-grid3-row.disable-selection.x-grid3-row-selected .x-grid3-row-checker::after { + color: #aaaaaa; +} +.x-grid3-row.disable-selection .x-grid3-row-checker::before, .x-grid3-row.disable-selection.x-grid3-row-selected .x-grid3-row-checker::before { + content: "\f0c8"; +} +.x-grid3-row.disable-selection .x-grid3-row-checker::after, .x-grid3-row.disable-selection.x-grid3-row-selected .x-grid3-row-checker::after { + content: "\f715"; + font-size: 6px; + position: absolute; + left: 50%; + top: 50%; + margin-left: 2px; + margin-top: 1px; + transform: translate(-50%, -50%) rotate(98deg); + font-weight: 600; + font-family: "Font Awesome 5 Free"; +} +.x-grid3-row.disable-selection .x-grid3-row-checker:hover, .x-grid3-row.disable-selection.x-grid3-row-selected .x-grid3-row-checker:hover { + cursor: default; +} + .ux-row-action-cell .x-grid3-cell-inner { padding: 1px 0 0 0; } @@ -28163,6 +28202,16 @@ html[dir=rtl] .x-hide-offsets { user-select: text !important; } +.x-selectable.simulated-link * { + color: #234368; + text-decoration: none; + border-bottom: 1px dotted #749fd0; +} +.x-selectable.simulated-link *:hover { + color: black; + border-bottom-color: #336299; +} + /* Lightbox */ #ux-lightbox { left: 0; diff --git a/manager/templates/default/css/login.css b/manager/templates/default/css/login.css index 7bc552cbc8c..3aee99ea6fd 100644 --- a/manager/templates/default/css/login.css +++ b/manager/templates/default/css/login.css @@ -119,7 +119,6 @@ html[dir=rtl] .l-background { justify-content: space-between; width: 30rem; } - .l-background { display: block; } @@ -444,19 +443,15 @@ label > input { margin: 1rem 0 0.5rem; font-size: 1.3rem; } - .l-main { margin: 0.5rem 0 0.5rem; } - .c-logo { width: 10rem; } - label { font-size: 1em; } - .is-error, .is-success { font-size: 1em; diff --git a/setup/assets/css/installer-min.css b/setup/assets/css/installer-min.css index ba3869aa5d2..93ffd09bcde 100644 --- a/setup/assets/css/installer-min.css +++ b/setup/assets/css/installer-min.css @@ -20,7 +20,7 @@ /*! * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - */.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:auto;display:inline-block;font-style:normal;font-variant:normal;line-height:1}.fa-large,.fa-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;line-height:inherit;position:absolute;text-align:center;width:2em}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-bacteria:before{content:"\e059"}.fa-bacterium:before{content:"\e05a"}.fa-bahai:before{content:"\f666"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\e05b"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudflare:before{content:"\e07d"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-alt:before{content:"\f422"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cotton-bureau:before{content:"\f89e"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\e052"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-deezer:before{content:"\e077"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-disease:before{content:"\f7fa"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edge-legacy:before{content:"\e078"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-alt:before{content:"\f424"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-faucet:before{content:"\e005"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\e007"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-pay:before{content:"\e079"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guilded:before{content:"\e07e"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\e05d"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-hands-wash:before{content:"\e05e"}.fa-handshake:before{content:"\f2b5"}.fa-handshake-alt-slash:before{content:"\e05f"}.fa-handshake-slash:before{content:"\e060"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-hdd:before{content:"\f0a0"}.fa-head-side-cough:before{content:"\e061"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-head-side-mask:before{content:"\e063"}.fa-head-side-virus:before{content:"\e064"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hive:before{content:"\e07f"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-house-user:before{content:"\e065"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-ideal:before{content:"\e013"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-innosoft:before{content:"\e080"}.fa-instagram:before{content:"\f16d"}.fa-instagram-square:before{content:"\e055"}.fa-instalod:before{content:"\e081"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-house:before{content:"\e066"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\e067"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microblog:before{content:"\e01a"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\e056"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse:before{content:"\f8cc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-octopus-deploy:before{content:"\e082"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-arrows:before{content:"\e068"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-perbyte:before{content:"\e083"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-square:before{content:"\e01e"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-slash:before{content:"\e069"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pump-medical:before{content:"\e06a"}.fa-pump-soap:before{content:"\e06b"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-rust:before{content:"\e07a"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-shield-virus:before{content:"\e06c"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopify:before{content:"\e057"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sink:before{content:"\e06d"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\e06e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\e06f"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-store-alt-slash:before{content:"\e070"}.fa-store-slash:before{content:"\e071"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swift:before{content:"\f8e1"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-tiktok:before{content:"\e07b"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\e041"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-uncharted:before{content:"\e084"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\e049"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-unsplash:before{content:"\e07c"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-users-slash:before{content:"\e073"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-vest:before{content:"\e085"}.fa-vest-patches:before{content:"\e086"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-virus:before{content:"\e074"}.fa-virus-slash:before{content:"\e075"}.fa-viruses:before{content:"\e076"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-watchman-monitoring:before{content:"\e087"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wodu:before{content:"\e088"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"} + */.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:auto;display:inline-block;font-style:normal;font-variant:normal;line-height:1}.fa-large,.fa-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;line-height:inherit;position:absolute;text-align:center;width:2em}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-bacteria:before{content:"\e059"}.fa-bacterium:before{content:"\e05a"}.fa-bahai:before{content:"\f666"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\e05b"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudflare:before{content:"\e07d"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-alt:before{content:"\f422"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cotton-bureau:before{content:"\f89e"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\e052"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-deezer:before{content:"\e077"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-disease:before{content:"\f7fa"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edge-legacy:before{content:"\e078"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-alt:before{content:"\f424"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-faucet:before{content:"\e005"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\e007"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-pay:before{content:"\e079"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guilded:before{content:"\e07e"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\e05d"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-hands-wash:before{content:"\e05e"}.fa-handshake:before{content:"\f2b5"}.fa-handshake-alt-slash:before{content:"\e05f"}.fa-handshake-slash:before{content:"\e060"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-hdd:before{content:"\f0a0"}.fa-head-side-cough:before{content:"\e061"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-head-side-mask:before{content:"\e063"}.fa-head-side-virus:before{content:"\e064"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hive:before{content:"\e07f"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-house-user:before{content:"\e065"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-ideal:before{content:"\e013"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-innosoft:before{content:"\e080"}.fa-instagram:before{content:"\f16d"}.fa-instagram-square:before{content:"\e055"}.fa-instalod:before{content:"\e081"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-house:before{content:"\e066"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\e067"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microblog:before{content:"\e01a"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\e056"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse:before{content:"\f8cc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-octopus-deploy:before{content:"\e082"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-arrows:before{content:"\e068"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-perbyte:before{content:"\e083"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-square:before{content:"\e01e"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-slash:before{content:"\e069"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pump-medical:before{content:"\e06a"}.fa-pump-soap:before{content:"\e06b"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-rust:before{content:"\e07a"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-shield-virus:before{content:"\e06c"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopify:before{content:"\e057"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sink:before{content:"\e06d"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\e06e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\e06f"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-store-alt-slash:before{content:"\e070"}.fa-store-slash:before{content:"\e071"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swift:before{content:"\f8e1"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-tiktok:before{content:"\e07b"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\e041"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-uncharted:before{content:"\e084"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\e049"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-unsplash:before{content:"\e07c"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-users-slash:before{content:"\e073"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-vest:before{content:"\e085"}.fa-vest-patches:before{content:"\e086"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-virus:before{content:"\e074"}.fa-virus-slash:before{content:"\e075"}.fa-viruses:before{content:"\e076"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-watchman-monitoring:before{content:"\e087"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wodu:before{content:"\e088"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"} /*! * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com @@ -30,7 +30,7 @@ /*! * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - */.fab,.fad,.fal,.far,.fas,.icon{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:auto;display:inline-block;font-style:normal;font-variant:normal;line-height:1}.icon-large,.icon-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-.0667em}.icon-xs{font-size:.75em}.icon-sm{font-size:.875em}.icon-1x{font-size:1em}.icon-2x{font-size:2em}.icon-3x{font-size:3em}.icon-4x{font-size:4em}.icon-5x{font-size:5em}.icon-6x{font-size:6em}.icon-7x{font-size:7em}.icon-8x{font-size:8em}.icon-9x{font-size:9em}.icon-10x{font-size:10em}.icon-fw{text-align:center;width:1.25em}.icon-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.icon-ul>li{position:relative}.icon-li{left:-2em;line-height:inherit;position:absolute;text-align:center;width:2em}.icon-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.icon-pull-left{float:left}.icon-pull-right{float:right}.fab.icon-pull-left,.fal.icon-pull-left,.far.icon-pull-left,.fas.icon-pull-left,.icon.icon-pull-left{margin-right:.3em}.fab.icon-pull-right,.fal.icon-pull-right,.far.icon-pull-right,.fas.icon-pull-right,.icon.icon-pull-right{margin-left:.3em}.icon-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.icon-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.icon-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.icon-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.icon-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.icon-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.icon-flip-vertical{transform:scaleY(-1)}.icon-flip-both,.icon-flip-horizontal.icon-flip-vertical,.icon-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.icon-flip-both,.icon-flip-horizontal.icon-flip-vertical{transform:scale(-1)}:root .icon-flip-both,:root .icon-flip-horizontal,:root .icon-flip-vertical,:root .icon-rotate-90,:root .icon-rotate-180,:root .icon-rotate-270{filter:none}.icon-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.icon-stack-1x,.icon-stack-2x{left:0;position:absolute;text-align:center;width:100%}.icon-stack-1x{line-height:inherit}.icon-stack-2x{font-size:2em}.icon-inverse{color:#fff}.icon-500px:before{content:"\f26e"}.icon-accessible-icon:before{content:"\f368"}.icon-accusoft:before{content:"\f369"}.icon-acquisitions-incorporated:before{content:"\f6af"}.icon-ad:before{content:"\f641"}.icon-address-book:before{content:"\f2b9"}.icon-address-card:before{content:"\f2bb"}.icon-adjust:before{content:"\f042"}.icon-adn:before{content:"\f170"}.icon-adversal:before{content:"\f36a"}.icon-affiliatetheme:before{content:"\f36b"}.icon-air-freshener:before{content:"\f5d0"}.icon-airbnb:before{content:"\f834"}.icon-algolia:before{content:"\f36c"}.icon-align-center:before{content:"\f037"}.icon-align-justify:before{content:"\f039"}.icon-align-left:before{content:"\f036"}.icon-align-right:before{content:"\f038"}.icon-alipay:before{content:"\f642"}.icon-allergies:before{content:"\f461"}.icon-amazon:before{content:"\f270"}.icon-amazon-pay:before{content:"\f42c"}.icon-ambulance:before{content:"\f0f9"}.icon-american-sign-language-interpreting:before{content:"\f2a3"}.icon-amilia:before{content:"\f36d"}.icon-anchor:before{content:"\f13d"}.icon-android:before{content:"\f17b"}.icon-angellist:before{content:"\f209"}.icon-angle-double-down:before{content:"\f103"}.icon-angle-double-left:before{content:"\f100"}.icon-angle-double-right:before{content:"\f101"}.icon-angle-double-up:before{content:"\f102"}.icon-angle-down:before{content:"\f107"}.icon-angle-left:before{content:"\f104"}.icon-angle-right:before{content:"\f105"}.icon-angle-up:before{content:"\f106"}.icon-angry:before{content:"\f556"}.icon-angrycreative:before{content:"\f36e"}.icon-angular:before{content:"\f420"}.icon-ankh:before{content:"\f644"}.icon-app-store:before{content:"\f36f"}.icon-app-store-ios:before{content:"\f370"}.icon-apper:before{content:"\f371"}.icon-apple:before{content:"\f179"}.icon-apple-alt:before{content:"\f5d1"}.icon-apple-pay:before{content:"\f415"}.icon-archive:before{content:"\f187"}.icon-archway:before{content:"\f557"}.icon-arrow-alt-circle-down:before{content:"\f358"}.icon-arrow-alt-circle-left:before{content:"\f359"}.icon-arrow-alt-circle-right:before{content:"\f35a"}.icon-arrow-alt-circle-up:before{content:"\f35b"}.icon-arrow-circle-down:before{content:"\f0ab"}.icon-arrow-circle-left:before{content:"\f0a8"}.icon-arrow-circle-right:before{content:"\f0a9"}.icon-arrow-circle-up:before{content:"\f0aa"}.icon-arrow-down:before{content:"\f063"}.icon-arrow-left:before{content:"\f060"}.icon-arrow-right:before{content:"\f061"}.icon-arrow-up:before{content:"\f062"}.icon-arrows-alt:before{content:"\f0b2"}.icon-arrows-alt-h:before{content:"\f337"}.icon-arrows-alt-v:before{content:"\f338"}.icon-artstation:before{content:"\f77a"}.icon-assistive-listening-systems:before{content:"\f2a2"}.icon-asterisk:before{content:"\f069"}.icon-asymmetrik:before{content:"\f372"}.icon-at:before{content:"\f1fa"}.icon-atlas:before{content:"\f558"}.icon-atlassian:before{content:"\f77b"}.icon-atom:before{content:"\f5d2"}.icon-audible:before{content:"\f373"}.icon-audio-description:before{content:"\f29e"}.icon-autoprefixer:before{content:"\f41c"}.icon-avianex:before{content:"\f374"}.icon-aviato:before{content:"\f421"}.icon-award:before{content:"\f559"}.icon-aws:before{content:"\f375"}.icon-baby:before{content:"\f77c"}.icon-baby-carriage:before{content:"\f77d"}.icon-backspace:before{content:"\f55a"}.icon-backward:before{content:"\f04a"}.icon-bacon:before{content:"\f7e5"}.icon-bacteria:before{content:"\e059"}.icon-bacterium:before{content:"\e05a"}.icon-bahai:before{content:"\f666"}.icon-balance-scale:before{content:"\f24e"}.icon-balance-scale-left:before{content:"\f515"}.icon-balance-scale-right:before{content:"\f516"}.icon-ban:before{content:"\f05e"}.icon-band-aid:before{content:"\f462"}.icon-bandcamp:before{content:"\f2d5"}.icon-barcode:before{content:"\f02a"}.icon-bars:before{content:"\f0c9"}.icon-baseball-ball:before{content:"\f433"}.icon-basketball-ball:before{content:"\f434"}.icon-bath:before{content:"\f2cd"}.icon-battery-empty:before{content:"\f244"}.icon-battery-full:before{content:"\f240"}.icon-battery-half:before{content:"\f242"}.icon-battery-quarter:before{content:"\f243"}.icon-battery-three-quarters:before{content:"\f241"}.icon-battle-net:before{content:"\f835"}.icon-bed:before{content:"\f236"}.icon-beer:before{content:"\f0fc"}.icon-behance:before{content:"\f1b4"}.icon-behance-square:before{content:"\f1b5"}.icon-bell:before{content:"\f0f3"}.icon-bell-slash:before{content:"\f1f6"}.icon-bezier-curve:before{content:"\f55b"}.icon-bible:before{content:"\f647"}.icon-bicycle:before{content:"\f206"}.icon-biking:before{content:"\f84a"}.icon-bimobject:before{content:"\f378"}.icon-binoculars:before{content:"\f1e5"}.icon-biohazard:before{content:"\f780"}.icon-birthday-cake:before{content:"\f1fd"}.icon-bitbucket:before{content:"\f171"}.icon-bitcoin:before{content:"\f379"}.icon-bity:before{content:"\f37a"}.icon-black-tie:before{content:"\f27e"}.icon-blackberry:before{content:"\f37b"}.icon-blender:before{content:"\f517"}.icon-blender-phone:before{content:"\f6b6"}.icon-blind:before{content:"\f29d"}.icon-blog:before{content:"\f781"}.icon-blogger:before{content:"\f37c"}.icon-blogger-b:before{content:"\f37d"}.icon-bluetooth:before{content:"\f293"}.icon-bluetooth-b:before{content:"\f294"}.icon-bold:before{content:"\f032"}.icon-bolt:before{content:"\f0e7"}.icon-bomb:before{content:"\f1e2"}.icon-bone:before{content:"\f5d7"}.icon-bong:before{content:"\f55c"}.icon-book:before{content:"\f02d"}.icon-book-dead:before{content:"\f6b7"}.icon-book-medical:before{content:"\f7e6"}.icon-book-open:before{content:"\f518"}.icon-book-reader:before{content:"\f5da"}.icon-bookmark:before{content:"\f02e"}.icon-bootstrap:before{content:"\f836"}.icon-border-all:before{content:"\f84c"}.icon-border-none:before{content:"\f850"}.icon-border-style:before{content:"\f853"}.icon-bowling-ball:before{content:"\f436"}.icon-box:before{content:"\f466"}.icon-box-open:before{content:"\f49e"}.icon-box-tissue:before{content:"\e05b"}.icon-boxes:before{content:"\f468"}.icon-braille:before{content:"\f2a1"}.icon-brain:before{content:"\f5dc"}.icon-bread-slice:before{content:"\f7ec"}.icon-briefcase:before{content:"\f0b1"}.icon-briefcase-medical:before{content:"\f469"}.icon-broadcast-tower:before{content:"\f519"}.icon-broom:before{content:"\f51a"}.icon-brush:before{content:"\f55d"}.icon-btc:before{content:"\f15a"}.icon-buffer:before{content:"\f837"}.icon-bug:before{content:"\f188"}.icon-building:before{content:"\f1ad"}.icon-bullhorn:before{content:"\f0a1"}.icon-bullseye:before{content:"\f140"}.icon-burn:before{content:"\f46a"}.icon-buromobelexperte:before{content:"\f37f"}.icon-bus:before{content:"\f207"}.icon-bus-alt:before{content:"\f55e"}.icon-business-time:before{content:"\f64a"}.icon-buy-n-large:before{content:"\f8a6"}.icon-buysellads:before{content:"\f20d"}.icon-calculator:before{content:"\f1ec"}.icon-calendar:before{content:"\f133"}.icon-calendar-alt:before{content:"\f073"}.icon-calendar-check:before{content:"\f274"}.icon-calendar-day:before{content:"\f783"}.icon-calendar-minus:before{content:"\f272"}.icon-calendar-plus:before{content:"\f271"}.icon-calendar-times:before{content:"\f273"}.icon-calendar-week:before{content:"\f784"}.icon-camera:before{content:"\f030"}.icon-camera-retro:before{content:"\f083"}.icon-campground:before{content:"\f6bb"}.icon-canadian-maple-leaf:before{content:"\f785"}.icon-candy-cane:before{content:"\f786"}.icon-cannabis:before{content:"\f55f"}.icon-capsules:before{content:"\f46b"}.icon-car:before{content:"\f1b9"}.icon-car-alt:before{content:"\f5de"}.icon-car-battery:before{content:"\f5df"}.icon-car-crash:before{content:"\f5e1"}.icon-car-side:before{content:"\f5e4"}.icon-caravan:before{content:"\f8ff"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.icon-caret-square-down:before{content:"\f150"}.icon-caret-square-left:before{content:"\f191"}.icon-caret-square-right:before{content:"\f152"}.icon-caret-square-up:before{content:"\f151"}.icon-caret-up:before{content:"\f0d8"}.icon-carrot:before{content:"\f787"}.icon-cart-arrow-down:before{content:"\f218"}.icon-cart-plus:before{content:"\f217"}.icon-cash-register:before{content:"\f788"}.icon-cat:before{content:"\f6be"}.icon-cc-amazon-pay:before{content:"\f42d"}.icon-cc-amex:before{content:"\f1f3"}.icon-cc-apple-pay:before{content:"\f416"}.icon-cc-diners-club:before{content:"\f24c"}.icon-cc-discover:before{content:"\f1f2"}.icon-cc-jcb:before{content:"\f24b"}.icon-cc-mastercard:before{content:"\f1f1"}.icon-cc-paypal:before{content:"\f1f4"}.icon-cc-stripe:before{content:"\f1f5"}.icon-cc-visa:before{content:"\f1f0"}.icon-centercode:before{content:"\f380"}.icon-centos:before{content:"\f789"}.icon-certificate:before{content:"\f0a3"}.icon-chair:before{content:"\f6c0"}.icon-chalkboard:before{content:"\f51b"}.icon-chalkboard-teacher:before{content:"\f51c"}.icon-charging-station:before{content:"\f5e7"}.icon-chart-area:before{content:"\f1fe"}.icon-chart-bar:before{content:"\f080"}.icon-chart-line:before{content:"\f201"}.icon-chart-pie:before{content:"\f200"}.icon-check:before{content:"\f00c"}.icon-check-circle:before{content:"\f058"}.icon-check-double:before{content:"\f560"}.icon-check-square:before{content:"\f14a"}.icon-cheese:before{content:"\f7ef"}.icon-chess:before{content:"\f439"}.icon-chess-bishop:before{content:"\f43a"}.icon-chess-board:before{content:"\f43c"}.icon-chess-king:before{content:"\f43f"}.icon-chess-knight:before{content:"\f441"}.icon-chess-pawn:before{content:"\f443"}.icon-chess-queen:before{content:"\f445"}.icon-chess-rook:before{content:"\f447"}.icon-chevron-circle-down:before{content:"\f13a"}.icon-chevron-circle-left:before{content:"\f137"}.icon-chevron-circle-right:before{content:"\f138"}.icon-chevron-circle-up:before{content:"\f139"}.icon-chevron-down:before{content:"\f078"}.icon-chevron-left:before{content:"\f053"}.icon-chevron-right:before{content:"\f054"}.icon-chevron-up:before{content:"\f077"}.icon-child:before{content:"\f1ae"}.icon-chrome:before{content:"\f268"}.icon-chromecast:before{content:"\f838"}.icon-church:before{content:"\f51d"}.icon-circle:before{content:"\f111"}.icon-circle-notch:before{content:"\f1ce"}.icon-city:before{content:"\f64f"}.icon-clinic-medical:before{content:"\f7f2"}.icon-clipboard:before{content:"\f328"}.icon-clipboard-check:before{content:"\f46c"}.icon-clipboard-list:before{content:"\f46d"}.icon-clock:before{content:"\f017"}.icon-clone:before{content:"\f24d"}.icon-closed-captioning:before{content:"\f20a"}.icon-cloud:before{content:"\f0c2"}.icon-cloud-download-alt:before{content:"\f381"}.icon-cloud-meatball:before{content:"\f73b"}.icon-cloud-moon:before{content:"\f6c3"}.icon-cloud-moon-rain:before{content:"\f73c"}.icon-cloud-rain:before{content:"\f73d"}.icon-cloud-showers-heavy:before{content:"\f740"}.icon-cloud-sun:before{content:"\f6c4"}.icon-cloud-sun-rain:before{content:"\f743"}.icon-cloud-upload-alt:before{content:"\f382"}.icon-cloudflare:before{content:"\e07d"}.icon-cloudscale:before{content:"\f383"}.icon-cloudsmith:before{content:"\f384"}.icon-cloudversify:before{content:"\f385"}.icon-cocktail:before{content:"\f561"}.icon-code:before{content:"\f121"}.icon-code-branch:before{content:"\f126"}.icon-codepen:before{content:"\f1cb"}.icon-codiepie:before{content:"\f284"}.icon-coffee:before{content:"\f0f4"}.icon-cog:before{content:"\f013"}.icon-cogs:before{content:"\f085"}.icon-coins:before{content:"\f51e"}.icon-columns:before{content:"\f0db"}.icon-comment:before{content:"\f075"}.icon-comment-alt:before{content:"\f27a"}.icon-comment-dollar:before{content:"\f651"}.icon-comment-dots:before{content:"\f4ad"}.icon-comment-medical:before{content:"\f7f5"}.icon-comment-slash:before{content:"\f4b3"}.icon-comments:before{content:"\f086"}.icon-comments-dollar:before{content:"\f653"}.icon-compact-disc:before{content:"\f51f"}.icon-compass:before{content:"\f14e"}.icon-compress:before{content:"\f066"}.icon-compress-alt:before{content:"\f422"}.icon-compress-arrows-alt:before{content:"\f78c"}.icon-concierge-bell:before{content:"\f562"}.icon-confluence:before{content:"\f78d"}.icon-connectdevelop:before{content:"\f20e"}.icon-contao:before{content:"\f26d"}.icon-cookie:before{content:"\f563"}.icon-cookie-bite:before{content:"\f564"}.icon-copy:before{content:"\f0c5"}.icon-copyright:before{content:"\f1f9"}.icon-cotton-bureau:before{content:"\f89e"}.icon-couch:before{content:"\f4b8"}.icon-cpanel:before{content:"\f388"}.icon-creative-commons:before{content:"\f25e"}.icon-creative-commons-by:before{content:"\f4e7"}.icon-creative-commons-nc:before{content:"\f4e8"}.icon-creative-commons-nc-eu:before{content:"\f4e9"}.icon-creative-commons-nc-jp:before{content:"\f4ea"}.icon-creative-commons-nd:before{content:"\f4eb"}.icon-creative-commons-pd:before{content:"\f4ec"}.icon-creative-commons-pd-alt:before{content:"\f4ed"}.icon-creative-commons-remix:before{content:"\f4ee"}.icon-creative-commons-sa:before{content:"\f4ef"}.icon-creative-commons-sampling:before{content:"\f4f0"}.icon-creative-commons-sampling-plus:before{content:"\f4f1"}.icon-creative-commons-share:before{content:"\f4f2"}.icon-creative-commons-zero:before{content:"\f4f3"}.icon-credit-card:before{content:"\f09d"}.icon-critical-role:before{content:"\f6c9"}.icon-crop:before{content:"\f125"}.icon-crop-alt:before{content:"\f565"}.icon-cross:before{content:"\f654"}.icon-crosshairs:before{content:"\f05b"}.icon-crow:before{content:"\f520"}.icon-crown:before{content:"\f521"}.icon-crutch:before{content:"\f7f7"}.icon-css3:before{content:"\f13c"}.icon-css3-alt:before{content:"\f38b"}.icon-cube:before{content:"\f1b2"}.icon-cubes:before{content:"\f1b3"}.icon-cut:before{content:"\f0c4"}.icon-cuttlefish:before{content:"\f38c"}.icon-d-and-d:before{content:"\f38d"}.icon-d-and-d-beyond:before{content:"\f6ca"}.icon-dailymotion:before{content:"\e052"}.icon-dashcube:before{content:"\f210"}.icon-database:before{content:"\f1c0"}.icon-deaf:before{content:"\f2a4"}.icon-deezer:before{content:"\e077"}.icon-delicious:before{content:"\f1a5"}.icon-democrat:before{content:"\f747"}.icon-deploydog:before{content:"\f38e"}.icon-deskpro:before{content:"\f38f"}.icon-desktop:before{content:"\f108"}.icon-dev:before{content:"\f6cc"}.icon-deviantart:before{content:"\f1bd"}.icon-dharmachakra:before{content:"\f655"}.icon-dhl:before{content:"\f790"}.icon-diagnoses:before{content:"\f470"}.icon-diaspora:before{content:"\f791"}.icon-dice:before{content:"\f522"}.icon-dice-d20:before{content:"\f6cf"}.icon-dice-d6:before{content:"\f6d1"}.icon-dice-five:before{content:"\f523"}.icon-dice-four:before{content:"\f524"}.icon-dice-one:before{content:"\f525"}.icon-dice-six:before{content:"\f526"}.icon-dice-three:before{content:"\f527"}.icon-dice-two:before{content:"\f528"}.icon-digg:before{content:"\f1a6"}.icon-digital-ocean:before{content:"\f391"}.icon-digital-tachograph:before{content:"\f566"}.icon-directions:before{content:"\f5eb"}.icon-discord:before{content:"\f392"}.icon-discourse:before{content:"\f393"}.icon-disease:before{content:"\f7fa"}.icon-divide:before{content:"\f529"}.icon-dizzy:before{content:"\f567"}.icon-dna:before{content:"\f471"}.icon-dochub:before{content:"\f394"}.icon-docker:before{content:"\f395"}.icon-dog:before{content:"\f6d3"}.icon-dollar-sign:before{content:"\f155"}.icon-dolly:before{content:"\f472"}.icon-dolly-flatbed:before{content:"\f474"}.icon-donate:before{content:"\f4b9"}.icon-door-closed:before{content:"\f52a"}.icon-door-open:before{content:"\f52b"}.icon-dot-circle:before{content:"\f192"}.icon-dove:before{content:"\f4ba"}.icon-download:before{content:"\f019"}.icon-draft2digital:before{content:"\f396"}.icon-drafting-compass:before{content:"\f568"}.icon-dragon:before{content:"\f6d5"}.icon-draw-polygon:before{content:"\f5ee"}.icon-dribbble:before{content:"\f17d"}.icon-dribbble-square:before{content:"\f397"}.icon-dropbox:before{content:"\f16b"}.icon-drum:before{content:"\f569"}.icon-drum-steelpan:before{content:"\f56a"}.icon-drumstick-bite:before{content:"\f6d7"}.icon-drupal:before{content:"\f1a9"}.icon-dumbbell:before{content:"\f44b"}.icon-dumpster:before{content:"\f793"}.icon-dumpster-fire:before{content:"\f794"}.icon-dungeon:before{content:"\f6d9"}.icon-dyalog:before{content:"\f399"}.icon-earlybirds:before{content:"\f39a"}.icon-ebay:before{content:"\f4f4"}.icon-edge:before{content:"\f282"}.icon-edge-legacy:before{content:"\e078"}.icon-edit:before{content:"\f044"}.icon-egg:before{content:"\f7fb"}.icon-eject:before{content:"\f052"}.icon-elementor:before{content:"\f430"}.icon-ellipsis-h:before{content:"\f141"}.icon-ellipsis-v:before{content:"\f142"}.icon-ello:before{content:"\f5f1"}.icon-ember:before{content:"\f423"}.icon-empire:before{content:"\f1d1"}.icon-envelope:before{content:"\f0e0"}.icon-envelope-open:before{content:"\f2b6"}.icon-envelope-open-text:before{content:"\f658"}.icon-envelope-square:before{content:"\f199"}.icon-envira:before{content:"\f299"}.icon-equals:before{content:"\f52c"}.icon-eraser:before{content:"\f12d"}.icon-erlang:before{content:"\f39d"}.icon-ethereum:before{content:"\f42e"}.icon-ethernet:before{content:"\f796"}.icon-etsy:before{content:"\f2d7"}.icon-euro-sign:before{content:"\f153"}.icon-evernote:before{content:"\f839"}.icon-exchange-alt:before{content:"\f362"}.icon-exclamation:before{content:"\f12a"}.icon-exclamation-circle:before{content:"\f06a"}.icon-exclamation-triangle:before{content:"\f071"}.icon-expand:before{content:"\f065"}.icon-expand-alt:before{content:"\f424"}.icon-expand-arrows-alt:before{content:"\f31e"}.icon-expeditedssl:before{content:"\f23e"}.icon-external-link-alt:before{content:"\f35d"}.icon-external-link-square-alt:before{content:"\f360"}.icon-eye:before{content:"\f06e"}.icon-eye-dropper:before{content:"\f1fb"}.icon-eye-slash:before{content:"\f070"}.icon-facebook:before{content:"\f09a"}.icon-facebook-f:before{content:"\f39e"}.icon-facebook-messenger:before{content:"\f39f"}.icon-facebook-square:before{content:"\f082"}.icon-fan:before{content:"\f863"}.icon-fantasy-flight-games:before{content:"\f6dc"}.icon-fast-backward:before{content:"\f049"}.icon-fast-forward:before{content:"\f050"}.icon-faucet:before{content:"\e005"}.icon-fax:before{content:"\f1ac"}.icon-feather:before{content:"\f52d"}.icon-feather-alt:before{content:"\f56b"}.icon-fedex:before{content:"\f797"}.icon-fedora:before{content:"\f798"}.icon-female:before{content:"\f182"}.icon-fighter-jet:before{content:"\f0fb"}.icon-figma:before{content:"\f799"}.icon-file:before{content:"\f15b"}.icon-file-alt:before{content:"\f15c"}.icon-file-archive:before{content:"\f1c6"}.icon-file-audio:before{content:"\f1c7"}.icon-file-code:before{content:"\f1c9"}.icon-file-contract:before{content:"\f56c"}.icon-file-csv:before{content:"\f6dd"}.icon-file-download:before{content:"\f56d"}.icon-file-excel:before{content:"\f1c3"}.icon-file-export:before{content:"\f56e"}.icon-file-image:before{content:"\f1c5"}.icon-file-import:before{content:"\f56f"}.icon-file-invoice:before{content:"\f570"}.icon-file-invoice-dollar:before{content:"\f571"}.icon-file-medical:before{content:"\f477"}.icon-file-medical-alt:before{content:"\f478"}.icon-file-pdf:before{content:"\f1c1"}.icon-file-powerpoint:before{content:"\f1c4"}.icon-file-prescription:before{content:"\f572"}.icon-file-signature:before{content:"\f573"}.icon-file-upload:before{content:"\f574"}.icon-file-video:before{content:"\f1c8"}.icon-file-word:before{content:"\f1c2"}.icon-fill:before{content:"\f575"}.icon-fill-drip:before{content:"\f576"}.icon-film:before{content:"\f008"}.icon-filter:before{content:"\f0b0"}.icon-fingerprint:before{content:"\f577"}.icon-fire:before{content:"\f06d"}.icon-fire-alt:before{content:"\f7e4"}.icon-fire-extinguisher:before{content:"\f134"}.icon-firefox:before{content:"\f269"}.icon-firefox-browser:before{content:"\e007"}.icon-first-aid:before{content:"\f479"}.icon-first-order:before{content:"\f2b0"}.icon-first-order-alt:before{content:"\f50a"}.icon-firstdraft:before{content:"\f3a1"}.icon-fish:before{content:"\f578"}.icon-fist-raised:before{content:"\f6de"}.icon-flag:before{content:"\f024"}.icon-flag-checkered:before{content:"\f11e"}.icon-flag-usa:before{content:"\f74d"}.icon-flask:before{content:"\f0c3"}.icon-flickr:before{content:"\f16e"}.icon-flipboard:before{content:"\f44d"}.icon-flushed:before{content:"\f579"}.icon-fly:before{content:"\f417"}.icon-folder:before{content:"\f07b"}.icon-folder-minus:before{content:"\f65d"}.icon-folder-open:before{content:"\f07c"}.icon-folder-plus:before{content:"\f65e"}.icon-font:before{content:"\f031"}.icon-font-awesome:before{content:"\f2b4"}.icon-font-awesome-alt:before{content:"\f35c"}.icon-font-awesome-flag:before{content:"\f425"}.icon-font-awesome-logo-full:before{content:"\f4e6"}.icon-fonticons:before{content:"\f280"}.icon-fonticons-fi:before{content:"\f3a2"}.icon-football-ball:before{content:"\f44e"}.icon-fort-awesome:before{content:"\f286"}.icon-fort-awesome-alt:before{content:"\f3a3"}.icon-forumbee:before{content:"\f211"}.icon-forward:before{content:"\f04e"}.icon-foursquare:before{content:"\f180"}.icon-free-code-camp:before{content:"\f2c5"}.icon-freebsd:before{content:"\f3a4"}.icon-frog:before{content:"\f52e"}.icon-frown:before{content:"\f119"}.icon-frown-open:before{content:"\f57a"}.icon-fulcrum:before{content:"\f50b"}.icon-funnel-dollar:before{content:"\f662"}.icon-futbol:before{content:"\f1e3"}.icon-galactic-republic:before{content:"\f50c"}.icon-galactic-senate:before{content:"\f50d"}.icon-gamepad:before{content:"\f11b"}.icon-gas-pump:before{content:"\f52f"}.icon-gavel:before{content:"\f0e3"}.icon-gem:before{content:"\f3a5"}.icon-genderless:before{content:"\f22d"}.icon-get-pocket:before{content:"\f265"}.icon-gg:before{content:"\f260"}.icon-gg-circle:before{content:"\f261"}.icon-ghost:before{content:"\f6e2"}.icon-gift:before{content:"\f06b"}.icon-gifts:before{content:"\f79c"}.icon-git:before{content:"\f1d3"}.icon-git-alt:before{content:"\f841"}.icon-git-square:before{content:"\f1d2"}.icon-github:before{content:"\f09b"}.icon-github-alt:before{content:"\f113"}.icon-github-square:before{content:"\f092"}.icon-gitkraken:before{content:"\f3a6"}.icon-gitlab:before{content:"\f296"}.icon-gitter:before{content:"\f426"}.icon-glass-cheers:before{content:"\f79f"}.icon-glass-martini:before{content:"\f000"}.icon-glass-martini-alt:before{content:"\f57b"}.icon-glass-whiskey:before{content:"\f7a0"}.icon-glasses:before{content:"\f530"}.icon-glide:before{content:"\f2a5"}.icon-glide-g:before{content:"\f2a6"}.icon-globe:before{content:"\f0ac"}.icon-globe-africa:before{content:"\f57c"}.icon-globe-americas:before{content:"\f57d"}.icon-globe-asia:before{content:"\f57e"}.icon-globe-europe:before{content:"\f7a2"}.icon-gofore:before{content:"\f3a7"}.icon-golf-ball:before{content:"\f450"}.icon-goodreads:before{content:"\f3a8"}.icon-goodreads-g:before{content:"\f3a9"}.icon-google:before{content:"\f1a0"}.icon-google-drive:before{content:"\f3aa"}.icon-google-pay:before{content:"\e079"}.icon-google-play:before{content:"\f3ab"}.icon-google-plus:before{content:"\f2b3"}.icon-google-plus-g:before{content:"\f0d5"}.icon-google-plus-square:before{content:"\f0d4"}.icon-google-wallet:before{content:"\f1ee"}.icon-gopuram:before{content:"\f664"}.icon-graduation-cap:before{content:"\f19d"}.icon-gratipay:before{content:"\f184"}.icon-grav:before{content:"\f2d6"}.icon-greater-than:before{content:"\f531"}.icon-greater-than-equal:before{content:"\f532"}.icon-grimace:before{content:"\f57f"}.icon-grin:before{content:"\f580"}.icon-grin-alt:before{content:"\f581"}.icon-grin-beam:before{content:"\f582"}.icon-grin-beam-sweat:before{content:"\f583"}.icon-grin-hearts:before{content:"\f584"}.icon-grin-squint:before{content:"\f585"}.icon-grin-squint-tears:before{content:"\f586"}.icon-grin-stars:before{content:"\f587"}.icon-grin-tears:before{content:"\f588"}.icon-grin-tongue:before{content:"\f589"}.icon-grin-tongue-squint:before{content:"\f58a"}.icon-grin-tongue-wink:before{content:"\f58b"}.icon-grin-wink:before{content:"\f58c"}.icon-grip-horizontal:before{content:"\f58d"}.icon-grip-lines:before{content:"\f7a4"}.icon-grip-lines-vertical:before{content:"\f7a5"}.icon-grip-vertical:before{content:"\f58e"}.icon-gripfire:before{content:"\f3ac"}.icon-grunt:before{content:"\f3ad"}.icon-guilded:before{content:"\e07e"}.icon-guitar:before{content:"\f7a6"}.icon-gulp:before{content:"\f3ae"}.icon-h-square:before{content:"\f0fd"}.icon-hacker-news:before{content:"\f1d4"}.icon-hacker-news-square:before{content:"\f3af"}.icon-hackerrank:before{content:"\f5f7"}.icon-hamburger:before{content:"\f805"}.icon-hammer:before{content:"\f6e3"}.icon-hamsa:before{content:"\f665"}.icon-hand-holding:before{content:"\f4bd"}.icon-hand-holding-heart:before{content:"\f4be"}.icon-hand-holding-medical:before{content:"\e05c"}.icon-hand-holding-usd:before{content:"\f4c0"}.icon-hand-holding-water:before{content:"\f4c1"}.icon-hand-lizard:before{content:"\f258"}.icon-hand-middle-finger:before{content:"\f806"}.icon-hand-paper:before{content:"\f256"}.icon-hand-peace:before{content:"\f25b"}.icon-hand-point-down:before{content:"\f0a7"}.icon-hand-point-left:before{content:"\f0a5"}.icon-hand-point-right:before{content:"\f0a4"}.icon-hand-point-up:before{content:"\f0a6"}.icon-hand-pointer:before{content:"\f25a"}.icon-hand-rock:before{content:"\f255"}.icon-hand-scissors:before{content:"\f257"}.icon-hand-sparkles:before{content:"\e05d"}.icon-hand-spock:before{content:"\f259"}.icon-hands:before{content:"\f4c2"}.icon-hands-helping:before{content:"\f4c4"}.icon-hands-wash:before{content:"\e05e"}.icon-handshake:before{content:"\f2b5"}.icon-handshake-alt-slash:before{content:"\e05f"}.icon-handshake-slash:before{content:"\e060"}.icon-hanukiah:before{content:"\f6e6"}.icon-hard-hat:before{content:"\f807"}.icon-hashtag:before{content:"\f292"}.icon-hat-cowboy:before{content:"\f8c0"}.icon-hat-cowboy-side:before{content:"\f8c1"}.icon-hat-wizard:before{content:"\f6e8"}.icon-hdd:before{content:"\f0a0"}.icon-head-side-cough:before{content:"\e061"}.icon-head-side-cough-slash:before{content:"\e062"}.icon-head-side-mask:before{content:"\e063"}.icon-head-side-virus:before{content:"\e064"}.icon-heading:before{content:"\f1dc"}.icon-headphones:before{content:"\f025"}.icon-headphones-alt:before{content:"\f58f"}.icon-headset:before{content:"\f590"}.icon-heart:before{content:"\f004"}.icon-heart-broken:before{content:"\f7a9"}.icon-heartbeat:before{content:"\f21e"}.icon-helicopter:before{content:"\f533"}.icon-highlighter:before{content:"\f591"}.icon-hiking:before{content:"\f6ec"}.icon-hippo:before{content:"\f6ed"}.icon-hips:before{content:"\f452"}.icon-hire-a-helper:before{content:"\f3b0"}.icon-history:before{content:"\f1da"}.icon-hive:before{content:"\e07f"}.icon-hockey-puck:before{content:"\f453"}.icon-holly-berry:before{content:"\f7aa"}.icon-home:before{content:"\f015"}.icon-hooli:before{content:"\f427"}.icon-hornbill:before{content:"\f592"}.icon-horse:before{content:"\f6f0"}.icon-horse-head:before{content:"\f7ab"}.icon-hospital:before{content:"\f0f8"}.icon-hospital-alt:before{content:"\f47d"}.icon-hospital-symbol:before{content:"\f47e"}.icon-hospital-user:before{content:"\f80d"}.icon-hot-tub:before{content:"\f593"}.icon-hotdog:before{content:"\f80f"}.icon-hotel:before{content:"\f594"}.icon-hotjar:before{content:"\f3b1"}.icon-hourglass:before{content:"\f254"}.icon-hourglass-end:before{content:"\f253"}.icon-hourglass-half:before{content:"\f252"}.icon-hourglass-start:before{content:"\f251"}.icon-house-damage:before{content:"\f6f1"}.icon-house-user:before{content:"\e065"}.icon-houzz:before{content:"\f27c"}.icon-hryvnia:before{content:"\f6f2"}.icon-html5:before{content:"\f13b"}.icon-hubspot:before{content:"\f3b2"}.icon-i-cursor:before{content:"\f246"}.icon-ice-cream:before{content:"\f810"}.icon-icicles:before{content:"\f7ad"}.icon-icons:before{content:"\f86d"}.icon-id-badge:before{content:"\f2c1"}.icon-id-card:before{content:"\f2c2"}.icon-id-card-alt:before{content:"\f47f"}.icon-ideal:before{content:"\e013"}.icon-igloo:before{content:"\f7ae"}.icon-image:before{content:"\f03e"}.icon-images:before{content:"\f302"}.icon-imdb:before{content:"\f2d8"}.icon-inbox:before{content:"\f01c"}.icon-indent:before{content:"\f03c"}.icon-industry:before{content:"\f275"}.icon-infinity:before{content:"\f534"}.icon-info:before{content:"\f129"}.icon-info-circle:before{content:"\f05a"}.icon-innosoft:before{content:"\e080"}.icon-instagram:before{content:"\f16d"}.icon-instagram-square:before{content:"\e055"}.icon-instalod:before{content:"\e081"}.icon-intercom:before{content:"\f7af"}.icon-internet-explorer:before{content:"\f26b"}.icon-invision:before{content:"\f7b0"}.icon-ioxhost:before{content:"\f208"}.icon-italic:before{content:"\f033"}.icon-itch-io:before{content:"\f83a"}.icon-itunes:before{content:"\f3b4"}.icon-itunes-note:before{content:"\f3b5"}.icon-java:before{content:"\f4e4"}.icon-jedi:before{content:"\f669"}.icon-jedi-order:before{content:"\f50e"}.icon-jenkins:before{content:"\f3b6"}.icon-jira:before{content:"\f7b1"}.icon-joget:before{content:"\f3b7"}.icon-joint:before{content:"\f595"}.icon-joomla:before{content:"\f1aa"}.icon-journal-whills:before{content:"\f66a"}.icon-js:before{content:"\f3b8"}.icon-js-square:before{content:"\f3b9"}.icon-jsfiddle:before{content:"\f1cc"}.icon-kaaba:before{content:"\f66b"}.icon-kaggle:before{content:"\f5fa"}.icon-key:before{content:"\f084"}.icon-keybase:before{content:"\f4f5"}.icon-keyboard:before{content:"\f11c"}.icon-keycdn:before{content:"\f3ba"}.icon-khanda:before{content:"\f66d"}.icon-kickstarter:before{content:"\f3bb"}.icon-kickstarter-k:before{content:"\f3bc"}.icon-kiss:before{content:"\f596"}.icon-kiss-beam:before{content:"\f597"}.icon-kiss-wink-heart:before{content:"\f598"}.icon-kiwi-bird:before{content:"\f535"}.icon-korvue:before{content:"\f42f"}.icon-landmark:before{content:"\f66f"}.icon-language:before{content:"\f1ab"}.icon-laptop:before{content:"\f109"}.icon-laptop-code:before{content:"\f5fc"}.icon-laptop-house:before{content:"\e066"}.icon-laptop-medical:before{content:"\f812"}.icon-laravel:before{content:"\f3bd"}.icon-lastfm:before{content:"\f202"}.icon-lastfm-square:before{content:"\f203"}.icon-laugh:before{content:"\f599"}.icon-laugh-beam:before{content:"\f59a"}.icon-laugh-squint:before{content:"\f59b"}.icon-laugh-wink:before{content:"\f59c"}.icon-layer-group:before{content:"\f5fd"}.icon-leaf:before{content:"\f06c"}.icon-leanpub:before{content:"\f212"}.icon-lemon:before{content:"\f094"}.icon-less:before{content:"\f41d"}.icon-less-than:before{content:"\f536"}.icon-less-than-equal:before{content:"\f537"}.icon-level-down-alt:before{content:"\f3be"}.icon-level-up-alt:before{content:"\f3bf"}.icon-life-ring:before{content:"\f1cd"}.icon-lightbulb:before{content:"\f0eb"}.icon-line:before{content:"\f3c0"}.icon-link:before{content:"\f0c1"}.icon-linkedin:before{content:"\f08c"}.icon-linkedin-in:before{content:"\f0e1"}.icon-linode:before{content:"\f2b8"}.icon-linux:before{content:"\f17c"}.icon-lira-sign:before{content:"\f195"}.icon-list:before{content:"\f03a"}.icon-list-alt:before{content:"\f022"}.icon-list-ol:before{content:"\f0cb"}.icon-list-ul:before{content:"\f0ca"}.icon-location-arrow:before{content:"\f124"}.icon-lock:before{content:"\f023"}.icon-lock-open:before{content:"\f3c1"}.icon-long-arrow-alt-down:before{content:"\f309"}.icon-long-arrow-alt-left:before{content:"\f30a"}.icon-long-arrow-alt-right:before{content:"\f30b"}.icon-long-arrow-alt-up:before{content:"\f30c"}.icon-low-vision:before{content:"\f2a8"}.icon-luggage-cart:before{content:"\f59d"}.icon-lungs:before{content:"\f604"}.icon-lungs-virus:before{content:"\e067"}.icon-lyft:before{content:"\f3c3"}.icon-magento:before{content:"\f3c4"}.icon-magic:before{content:"\f0d0"}.icon-magnet:before{content:"\f076"}.icon-mail-bulk:before{content:"\f674"}.icon-mailchimp:before{content:"\f59e"}.icon-male:before{content:"\f183"}.icon-mandalorian:before{content:"\f50f"}.icon-map:before{content:"\f279"}.icon-map-marked:before{content:"\f59f"}.icon-map-marked-alt:before{content:"\f5a0"}.icon-map-marker:before{content:"\f041"}.icon-map-marker-alt:before{content:"\f3c5"}.icon-map-pin:before{content:"\f276"}.icon-map-signs:before{content:"\f277"}.icon-markdown:before{content:"\f60f"}.icon-marker:before{content:"\f5a1"}.icon-mars:before{content:"\f222"}.icon-mars-double:before{content:"\f227"}.icon-mars-stroke:before{content:"\f229"}.icon-mars-stroke-h:before{content:"\f22b"}.icon-mars-stroke-v:before{content:"\f22a"}.icon-mask:before{content:"\f6fa"}.icon-mastodon:before{content:"\f4f6"}.icon-maxcdn:before{content:"\f136"}.icon-mdb:before{content:"\f8ca"}.icon-medal:before{content:"\f5a2"}.icon-medapps:before{content:"\f3c6"}.icon-medium:before{content:"\f23a"}.icon-medium-m:before{content:"\f3c7"}.icon-medkit:before{content:"\f0fa"}.icon-medrt:before{content:"\f3c8"}.icon-meetup:before{content:"\f2e0"}.icon-megaport:before{content:"\f5a3"}.icon-meh:before{content:"\f11a"}.icon-meh-blank:before{content:"\f5a4"}.icon-meh-rolling-eyes:before{content:"\f5a5"}.icon-memory:before{content:"\f538"}.icon-mendeley:before{content:"\f7b3"}.icon-menorah:before{content:"\f676"}.icon-mercury:before{content:"\f223"}.icon-meteor:before{content:"\f753"}.icon-microblog:before{content:"\e01a"}.icon-microchip:before{content:"\f2db"}.icon-microphone:before{content:"\f130"}.icon-microphone-alt:before{content:"\f3c9"}.icon-microphone-alt-slash:before{content:"\f539"}.icon-microphone-slash:before{content:"\f131"}.icon-microscope:before{content:"\f610"}.icon-microsoft:before{content:"\f3ca"}.icon-minus:before{content:"\f068"}.icon-minus-circle:before{content:"\f056"}.icon-minus-square:before{content:"\f146"}.icon-mitten:before{content:"\f7b5"}.icon-mix:before{content:"\f3cb"}.icon-mixcloud:before{content:"\f289"}.icon-mixer:before{content:"\e056"}.icon-mizuni:before{content:"\f3cc"}.icon-mobile:before{content:"\f10b"}.icon-mobile-alt:before{content:"\f3cd"}.icon-modx:before{content:"\f285"}.icon-monero:before{content:"\f3d0"}.icon-money-bill:before{content:"\f0d6"}.icon-money-bill-alt:before{content:"\f3d1"}.icon-money-bill-wave:before{content:"\f53a"}.icon-money-bill-wave-alt:before{content:"\f53b"}.icon-money-check:before{content:"\f53c"}.icon-money-check-alt:before{content:"\f53d"}.icon-monument:before{content:"\f5a6"}.icon-moon:before{content:"\f186"}.icon-mortar-pestle:before{content:"\f5a7"}.icon-mosque:before{content:"\f678"}.icon-motorcycle:before{content:"\f21c"}.icon-mountain:before{content:"\f6fc"}.icon-mouse:before{content:"\f8cc"}.icon-mouse-pointer:before{content:"\f245"}.icon-mug-hot:before{content:"\f7b6"}.icon-music:before{content:"\f001"}.icon-napster:before{content:"\f3d2"}.icon-neos:before{content:"\f612"}.icon-network-wired:before{content:"\f6ff"}.icon-neuter:before{content:"\f22c"}.icon-newspaper:before{content:"\f1ea"}.icon-nimblr:before{content:"\f5a8"}.icon-node:before{content:"\f419"}.icon-node-js:before{content:"\f3d3"}.icon-not-equal:before{content:"\f53e"}.icon-notes-medical:before{content:"\f481"}.icon-npm:before{content:"\f3d4"}.icon-ns8:before{content:"\f3d5"}.icon-nutritionix:before{content:"\f3d6"}.icon-object-group:before{content:"\f247"}.icon-object-ungroup:before{content:"\f248"}.icon-octopus-deploy:before{content:"\e082"}.icon-odnoklassniki:before{content:"\f263"}.icon-odnoklassniki-square:before{content:"\f264"}.icon-oil-can:before{content:"\f613"}.icon-old-republic:before{content:"\f510"}.icon-om:before{content:"\f679"}.icon-opencart:before{content:"\f23d"}.icon-openid:before{content:"\f19b"}.icon-opera:before{content:"\f26a"}.icon-optin-monster:before{content:"\f23c"}.icon-orcid:before{content:"\f8d2"}.icon-osi:before{content:"\f41a"}.icon-otter:before{content:"\f700"}.icon-outdent:before{content:"\f03b"}.icon-page4:before{content:"\f3d7"}.icon-pagelines:before{content:"\f18c"}.icon-pager:before{content:"\f815"}.icon-paint-brush:before{content:"\f1fc"}.icon-paint-roller:before{content:"\f5aa"}.icon-palette:before{content:"\f53f"}.icon-palfed:before{content:"\f3d8"}.icon-pallet:before{content:"\f482"}.icon-paper-plane:before{content:"\f1d8"}.icon-paperclip:before{content:"\f0c6"}.icon-parachute-box:before{content:"\f4cd"}.icon-paragraph:before{content:"\f1dd"}.icon-parking:before{content:"\f540"}.icon-passport:before{content:"\f5ab"}.icon-pastafarianism:before{content:"\f67b"}.icon-paste:before{content:"\f0ea"}.icon-patreon:before{content:"\f3d9"}.icon-pause:before{content:"\f04c"}.icon-pause-circle:before{content:"\f28b"}.icon-paw:before{content:"\f1b0"}.icon-paypal:before{content:"\f1ed"}.icon-peace:before{content:"\f67c"}.icon-pen:before{content:"\f304"}.icon-pen-alt:before{content:"\f305"}.icon-pen-fancy:before{content:"\f5ac"}.icon-pen-nib:before{content:"\f5ad"}.icon-pen-square:before{content:"\f14b"}.icon-pencil-alt:before{content:"\f303"}.icon-pencil-ruler:before{content:"\f5ae"}.icon-penny-arcade:before{content:"\f704"}.icon-people-arrows:before{content:"\e068"}.icon-people-carry:before{content:"\f4ce"}.icon-pepper-hot:before{content:"\f816"}.icon-perbyte:before{content:"\e083"}.icon-percent:before{content:"\f295"}.icon-percentage:before{content:"\f541"}.icon-periscope:before{content:"\f3da"}.icon-person-booth:before{content:"\f756"}.icon-phabricator:before{content:"\f3db"}.icon-phoenix-framework:before{content:"\f3dc"}.icon-phoenix-squadron:before{content:"\f511"}.icon-phone:before{content:"\f095"}.icon-phone-alt:before{content:"\f879"}.icon-phone-slash:before{content:"\f3dd"}.icon-phone-square:before{content:"\f098"}.icon-phone-square-alt:before{content:"\f87b"}.icon-phone-volume:before{content:"\f2a0"}.icon-photo-video:before{content:"\f87c"}.icon-php:before{content:"\f457"}.icon-pied-piper:before{content:"\f2ae"}.icon-pied-piper-alt:before{content:"\f1a8"}.icon-pied-piper-hat:before{content:"\f4e5"}.icon-pied-piper-pp:before{content:"\f1a7"}.icon-pied-piper-square:before{content:"\e01e"}.icon-piggy-bank:before{content:"\f4d3"}.icon-pills:before{content:"\f484"}.icon-pinterest:before{content:"\f0d2"}.icon-pinterest-p:before{content:"\f231"}.icon-pinterest-square:before{content:"\f0d3"}.icon-pizza-slice:before{content:"\f818"}.icon-place-of-worship:before{content:"\f67f"}.icon-plane:before{content:"\f072"}.icon-plane-arrival:before{content:"\f5af"}.icon-plane-departure:before{content:"\f5b0"}.icon-plane-slash:before{content:"\e069"}.icon-play:before{content:"\f04b"}.icon-play-circle:before{content:"\f144"}.icon-playstation:before{content:"\f3df"}.icon-plug:before{content:"\f1e6"}.icon-plus:before{content:"\f067"}.icon-plus-circle:before{content:"\f055"}.icon-plus-square:before{content:"\f0fe"}.icon-podcast:before{content:"\f2ce"}.icon-poll:before{content:"\f681"}.icon-poll-h:before{content:"\f682"}.icon-poo:before{content:"\f2fe"}.icon-poo-storm:before{content:"\f75a"}.icon-poop:before{content:"\f619"}.icon-portrait:before{content:"\f3e0"}.icon-pound-sign:before{content:"\f154"}.icon-power-off:before{content:"\f011"}.icon-pray:before{content:"\f683"}.icon-praying-hands:before{content:"\f684"}.icon-prescription:before{content:"\f5b1"}.icon-prescription-bottle:before{content:"\f485"}.icon-prescription-bottle-alt:before{content:"\f486"}.icon-print:before{content:"\f02f"}.icon-procedures:before{content:"\f487"}.icon-product-hunt:before{content:"\f288"}.icon-project-diagram:before{content:"\f542"}.icon-pump-medical:before{content:"\e06a"}.icon-pump-soap:before{content:"\e06b"}.icon-pushed:before{content:"\f3e1"}.icon-puzzle-piece:before{content:"\f12e"}.icon-python:before{content:"\f3e2"}.icon-qq:before{content:"\f1d6"}.icon-qrcode:before{content:"\f029"}.icon-question:before{content:"\f128"}.icon-question-circle:before{content:"\f059"}.icon-quidditch:before{content:"\f458"}.icon-quinscape:before{content:"\f459"}.icon-quora:before{content:"\f2c4"}.icon-quote-left:before{content:"\f10d"}.icon-quote-right:before{content:"\f10e"}.icon-quran:before{content:"\f687"}.icon-r-project:before{content:"\f4f7"}.icon-radiation:before{content:"\f7b9"}.icon-radiation-alt:before{content:"\f7ba"}.icon-rainbow:before{content:"\f75b"}.icon-random:before{content:"\f074"}.icon-raspberry-pi:before{content:"\f7bb"}.icon-ravelry:before{content:"\f2d9"}.icon-react:before{content:"\f41b"}.icon-reacteurope:before{content:"\f75d"}.icon-readme:before{content:"\f4d5"}.icon-rebel:before{content:"\f1d0"}.icon-receipt:before{content:"\f543"}.icon-record-vinyl:before{content:"\f8d9"}.icon-recycle:before{content:"\f1b8"}.icon-red-river:before{content:"\f3e3"}.icon-reddit:before{content:"\f1a1"}.icon-reddit-alien:before{content:"\f281"}.icon-reddit-square:before{content:"\f1a2"}.icon-redhat:before{content:"\f7bc"}.icon-redo:before{content:"\f01e"}.icon-redo-alt:before{content:"\f2f9"}.icon-registered:before{content:"\f25d"}.icon-remove-format:before{content:"\f87d"}.icon-renren:before{content:"\f18b"}.icon-reply:before{content:"\f3e5"}.icon-reply-all:before{content:"\f122"}.icon-replyd:before{content:"\f3e6"}.icon-republican:before{content:"\f75e"}.icon-researchgate:before{content:"\f4f8"}.icon-resolving:before{content:"\f3e7"}.icon-restroom:before{content:"\f7bd"}.icon-retweet:before{content:"\f079"}.icon-rev:before{content:"\f5b2"}.icon-ribbon:before{content:"\f4d6"}.icon-ring:before{content:"\f70b"}.icon-road:before{content:"\f018"}.icon-robot:before{content:"\f544"}.icon-rocket:before{content:"\f135"}.icon-rocketchat:before{content:"\f3e8"}.icon-rockrms:before{content:"\f3e9"}.icon-route:before{content:"\f4d7"}.icon-rss:before{content:"\f09e"}.icon-rss-square:before{content:"\f143"}.icon-ruble-sign:before{content:"\f158"}.icon-ruler:before{content:"\f545"}.icon-ruler-combined:before{content:"\f546"}.icon-ruler-horizontal:before{content:"\f547"}.icon-ruler-vertical:before{content:"\f548"}.icon-running:before{content:"\f70c"}.icon-rupee-sign:before{content:"\f156"}.icon-rust:before{content:"\e07a"}.icon-sad-cry:before{content:"\f5b3"}.icon-sad-tear:before{content:"\f5b4"}.icon-safari:before{content:"\f267"}.icon-salesforce:before{content:"\f83b"}.icon-sass:before{content:"\f41e"}.icon-satellite:before{content:"\f7bf"}.icon-satellite-dish:before{content:"\f7c0"}.icon-save:before{content:"\f0c7"}.icon-schlix:before{content:"\f3ea"}.icon-school:before{content:"\f549"}.icon-screwdriver:before{content:"\f54a"}.icon-scribd:before{content:"\f28a"}.icon-scroll:before{content:"\f70e"}.icon-sd-card:before{content:"\f7c2"}.icon-search:before{content:"\f002"}.icon-search-dollar:before{content:"\f688"}.icon-search-location:before{content:"\f689"}.icon-search-minus:before{content:"\f010"}.icon-search-plus:before{content:"\f00e"}.icon-searchengin:before{content:"\f3eb"}.icon-seedling:before{content:"\f4d8"}.icon-sellcast:before{content:"\f2da"}.icon-sellsy:before{content:"\f213"}.icon-server:before{content:"\f233"}.icon-servicestack:before{content:"\f3ec"}.icon-shapes:before{content:"\f61f"}.icon-share:before{content:"\f064"}.icon-share-alt:before{content:"\f1e0"}.icon-share-alt-square:before{content:"\f1e1"}.icon-share-square:before{content:"\f14d"}.icon-shekel-sign:before{content:"\f20b"}.icon-shield-alt:before{content:"\f3ed"}.icon-shield-virus:before{content:"\e06c"}.icon-ship:before{content:"\f21a"}.icon-shipping-fast:before{content:"\f48b"}.icon-shirtsinbulk:before{content:"\f214"}.icon-shoe-prints:before{content:"\f54b"}.icon-shopify:before{content:"\e057"}.icon-shopping-bag:before{content:"\f290"}.icon-shopping-basket:before{content:"\f291"}.icon-shopping-cart:before{content:"\f07a"}.icon-shopware:before{content:"\f5b5"}.icon-shower:before{content:"\f2cc"}.icon-shuttle-van:before{content:"\f5b6"}.icon-sign:before{content:"\f4d9"}.icon-sign-in-alt:before{content:"\f2f6"}.icon-sign-language:before{content:"\f2a7"}.icon-sign-out-alt:before{content:"\f2f5"}.icon-signal:before{content:"\f012"}.icon-signature:before{content:"\f5b7"}.icon-sim-card:before{content:"\f7c4"}.icon-simplybuilt:before{content:"\f215"}.icon-sink:before{content:"\e06d"}.icon-sistrix:before{content:"\f3ee"}.icon-sitemap:before{content:"\f0e8"}.icon-sith:before{content:"\f512"}.icon-skating:before{content:"\f7c5"}.icon-sketch:before{content:"\f7c6"}.icon-skiing:before{content:"\f7c9"}.icon-skiing-nordic:before{content:"\f7ca"}.icon-skull:before{content:"\f54c"}.icon-skull-crossbones:before{content:"\f714"}.icon-skyatlas:before{content:"\f216"}.icon-skype:before{content:"\f17e"}.icon-slack:before{content:"\f198"}.icon-slack-hash:before{content:"\f3ef"}.icon-slash:before{content:"\f715"}.icon-sleigh:before{content:"\f7cc"}.icon-sliders-h:before{content:"\f1de"}.icon-slideshare:before{content:"\f1e7"}.icon-smile:before{content:"\f118"}.icon-smile-beam:before{content:"\f5b8"}.icon-smile-wink:before{content:"\f4da"}.icon-smog:before{content:"\f75f"}.icon-smoking:before{content:"\f48d"}.icon-smoking-ban:before{content:"\f54d"}.icon-sms:before{content:"\f7cd"}.icon-snapchat:before{content:"\f2ab"}.icon-snapchat-ghost:before{content:"\f2ac"}.icon-snapchat-square:before{content:"\f2ad"}.icon-snowboarding:before{content:"\f7ce"}.icon-snowflake:before{content:"\f2dc"}.icon-snowman:before{content:"\f7d0"}.icon-snowplow:before{content:"\f7d2"}.icon-soap:before{content:"\e06e"}.icon-socks:before{content:"\f696"}.icon-solar-panel:before{content:"\f5ba"}.icon-sort:before{content:"\f0dc"}.icon-sort-alpha-down:before{content:"\f15d"}.icon-sort-alpha-down-alt:before{content:"\f881"}.icon-sort-alpha-up:before{content:"\f15e"}.icon-sort-alpha-up-alt:before{content:"\f882"}.icon-sort-amount-down:before{content:"\f160"}.icon-sort-amount-down-alt:before{content:"\f884"}.icon-sort-amount-up:before{content:"\f161"}.icon-sort-amount-up-alt:before{content:"\f885"}.icon-sort-down:before{content:"\f0dd"}.icon-sort-numeric-down:before{content:"\f162"}.icon-sort-numeric-down-alt:before{content:"\f886"}.icon-sort-numeric-up:before{content:"\f163"}.icon-sort-numeric-up-alt:before{content:"\f887"}.icon-sort-up:before{content:"\f0de"}.icon-soundcloud:before{content:"\f1be"}.icon-sourcetree:before{content:"\f7d3"}.icon-spa:before{content:"\f5bb"}.icon-space-shuttle:before{content:"\f197"}.icon-speakap:before{content:"\f3f3"}.icon-speaker-deck:before{content:"\f83c"}.icon-spell-check:before{content:"\f891"}.icon-spider:before{content:"\f717"}.icon-spinner:before{content:"\f110"}.icon-splotch:before{content:"\f5bc"}.icon-spotify:before{content:"\f1bc"}.icon-spray-can:before{content:"\f5bd"}.icon-square:before{content:"\f0c8"}.icon-square-full:before{content:"\f45c"}.icon-square-root-alt:before{content:"\f698"}.icon-squarespace:before{content:"\f5be"}.icon-stack-exchange:before{content:"\f18d"}.icon-stack-overflow:before{content:"\f16c"}.icon-stackpath:before{content:"\f842"}.icon-stamp:before{content:"\f5bf"}.icon-star:before{content:"\f005"}.icon-star-and-crescent:before{content:"\f699"}.icon-star-half:before{content:"\f089"}.icon-star-half-alt:before{content:"\f5c0"}.icon-star-of-david:before{content:"\f69a"}.icon-star-of-life:before{content:"\f621"}.icon-staylinked:before{content:"\f3f5"}.icon-steam:before{content:"\f1b6"}.icon-steam-square:before{content:"\f1b7"}.icon-steam-symbol:before{content:"\f3f6"}.icon-step-backward:before{content:"\f048"}.icon-step-forward:before{content:"\f051"}.icon-stethoscope:before{content:"\f0f1"}.icon-sticker-mule:before{content:"\f3f7"}.icon-sticky-note:before{content:"\f249"}.icon-stop:before{content:"\f04d"}.icon-stop-circle:before{content:"\f28d"}.icon-stopwatch:before{content:"\f2f2"}.icon-stopwatch-20:before{content:"\e06f"}.icon-store:before{content:"\f54e"}.icon-store-alt:before{content:"\f54f"}.icon-store-alt-slash:before{content:"\e070"}.icon-store-slash:before{content:"\e071"}.icon-strava:before{content:"\f428"}.icon-stream:before{content:"\f550"}.icon-street-view:before{content:"\f21d"}.icon-strikethrough:before{content:"\f0cc"}.icon-stripe:before{content:"\f429"}.icon-stripe-s:before{content:"\f42a"}.icon-stroopwafel:before{content:"\f551"}.icon-studiovinari:before{content:"\f3f8"}.icon-stumbleupon:before{content:"\f1a4"}.icon-stumbleupon-circle:before{content:"\f1a3"}.icon-subscript:before{content:"\f12c"}.icon-subway:before{content:"\f239"}.icon-suitcase:before{content:"\f0f2"}.icon-suitcase-rolling:before{content:"\f5c1"}.icon-sun:before{content:"\f185"}.icon-superpowers:before{content:"\f2dd"}.icon-superscript:before{content:"\f12b"}.icon-supple:before{content:"\f3f9"}.icon-surprise:before{content:"\f5c2"}.icon-suse:before{content:"\f7d6"}.icon-swatchbook:before{content:"\f5c3"}.icon-swift:before{content:"\f8e1"}.icon-swimmer:before{content:"\f5c4"}.icon-swimming-pool:before{content:"\f5c5"}.icon-symfony:before{content:"\f83d"}.icon-synagogue:before{content:"\f69b"}.icon-sync:before{content:"\f021"}.icon-sync-alt:before{content:"\f2f1"}.icon-syringe:before{content:"\f48e"}.icon-table:before{content:"\f0ce"}.icon-table-tennis:before{content:"\f45d"}.icon-tablet:before{content:"\f10a"}.icon-tablet-alt:before{content:"\f3fa"}.icon-tablets:before{content:"\f490"}.icon-tachometer-alt:before{content:"\f3fd"}.icon-tag:before{content:"\f02b"}.icon-tags:before{content:"\f02c"}.icon-tape:before{content:"\f4db"}.icon-tasks:before{content:"\f0ae"}.icon-taxi:before{content:"\f1ba"}.icon-teamspeak:before{content:"\f4f9"}.icon-teeth:before{content:"\f62e"}.icon-teeth-open:before{content:"\f62f"}.icon-telegram:before{content:"\f2c6"}.icon-telegram-plane:before{content:"\f3fe"}.icon-temperature-high:before{content:"\f769"}.icon-temperature-low:before{content:"\f76b"}.icon-tencent-weibo:before{content:"\f1d5"}.icon-tenge:before{content:"\f7d7"}.icon-terminal:before{content:"\f120"}.icon-text-height:before{content:"\f034"}.icon-text-width:before{content:"\f035"}.icon-th:before{content:"\f00a"}.icon-th-large:before{content:"\f009"}.icon-th-list:before{content:"\f00b"}.icon-the-red-yeti:before{content:"\f69d"}.icon-theater-masks:before{content:"\f630"}.icon-themeco:before{content:"\f5c6"}.icon-themeisle:before{content:"\f2b2"}.icon-thermometer:before{content:"\f491"}.icon-thermometer-empty:before{content:"\f2cb"}.icon-thermometer-full:before{content:"\f2c7"}.icon-thermometer-half:before{content:"\f2c9"}.icon-thermometer-quarter:before{content:"\f2ca"}.icon-thermometer-three-quarters:before{content:"\f2c8"}.icon-think-peaks:before{content:"\f731"}.icon-thumbs-down:before{content:"\f165"}.icon-thumbs-up:before{content:"\f164"}.icon-thumbtack:before{content:"\f08d"}.icon-ticket-alt:before{content:"\f3ff"}.icon-tiktok:before{content:"\e07b"}.icon-times:before{content:"\f00d"}.icon-times-circle:before{content:"\f057"}.icon-tint:before{content:"\f043"}.icon-tint-slash:before{content:"\f5c7"}.icon-tired:before{content:"\f5c8"}.icon-toggle-off:before{content:"\f204"}.icon-toggle-on:before{content:"\f205"}.icon-toilet:before{content:"\f7d8"}.icon-toilet-paper:before{content:"\f71e"}.icon-toilet-paper-slash:before{content:"\e072"}.icon-toolbox:before{content:"\f552"}.icon-tools:before{content:"\f7d9"}.icon-tooth:before{content:"\f5c9"}.icon-torah:before{content:"\f6a0"}.icon-torii-gate:before{content:"\f6a1"}.icon-tractor:before{content:"\f722"}.icon-trade-federation:before{content:"\f513"}.icon-trademark:before{content:"\f25c"}.icon-traffic-light:before{content:"\f637"}.icon-trailer:before{content:"\e041"}.icon-train:before{content:"\f238"}.icon-tram:before{content:"\f7da"}.icon-transgender:before{content:"\f224"}.icon-transgender-alt:before{content:"\f225"}.icon-trash:before{content:"\f1f8"}.icon-trash-alt:before{content:"\f2ed"}.icon-trash-restore:before{content:"\f829"}.icon-trash-restore-alt:before{content:"\f82a"}.icon-tree:before{content:"\f1bb"}.icon-trello:before{content:"\f181"}.icon-trophy:before{content:"\f091"}.icon-truck:before{content:"\f0d1"}.icon-truck-loading:before{content:"\f4de"}.icon-truck-monster:before{content:"\f63b"}.icon-truck-moving:before{content:"\f4df"}.icon-truck-pickup:before{content:"\f63c"}.icon-tshirt:before{content:"\f553"}.icon-tty:before{content:"\f1e4"}.icon-tumblr:before{content:"\f173"}.icon-tumblr-square:before{content:"\f174"}.icon-tv:before{content:"\f26c"}.icon-twitch:before{content:"\f1e8"}.icon-twitter:before{content:"\f099"}.icon-twitter-square:before{content:"\f081"}.icon-typo3:before{content:"\f42b"}.icon-uber:before{content:"\f402"}.icon-ubuntu:before{content:"\f7df"}.icon-uikit:before{content:"\f403"}.icon-umbraco:before{content:"\f8e8"}.icon-umbrella:before{content:"\f0e9"}.icon-umbrella-beach:before{content:"\f5ca"}.icon-uncharted:before{content:"\e084"}.icon-underline:before{content:"\f0cd"}.icon-undo:before{content:"\f0e2"}.icon-undo-alt:before{content:"\f2ea"}.icon-uniregistry:before{content:"\f404"}.icon-unity:before{content:"\e049"}.icon-universal-access:before{content:"\f29a"}.icon-university:before{content:"\f19c"}.icon-unlink:before{content:"\f127"}.icon-unlock:before{content:"\f09c"}.icon-unlock-alt:before{content:"\f13e"}.icon-unsplash:before{content:"\e07c"}.icon-untappd:before{content:"\f405"}.icon-upload:before{content:"\f093"}.icon-ups:before{content:"\f7e0"}.icon-usb:before{content:"\f287"}.icon-user:before{content:"\f007"}.icon-user-alt:before{content:"\f406"}.icon-user-alt-slash:before{content:"\f4fa"}.icon-user-astronaut:before{content:"\f4fb"}.icon-user-check:before{content:"\f4fc"}.icon-user-circle:before{content:"\f2bd"}.icon-user-clock:before{content:"\f4fd"}.icon-user-cog:before{content:"\f4fe"}.icon-user-edit:before{content:"\f4ff"}.icon-user-friends:before{content:"\f500"}.icon-user-graduate:before{content:"\f501"}.icon-user-injured:before{content:"\f728"}.icon-user-lock:before{content:"\f502"}.icon-user-md:before{content:"\f0f0"}.icon-user-minus:before{content:"\f503"}.icon-user-ninja:before{content:"\f504"}.icon-user-nurse:before{content:"\f82f"}.icon-user-plus:before{content:"\f234"}.icon-user-secret:before{content:"\f21b"}.icon-user-shield:before{content:"\f505"}.icon-user-slash:before{content:"\f506"}.icon-user-tag:before{content:"\f507"}.icon-user-tie:before{content:"\f508"}.icon-user-times:before{content:"\f235"}.icon-users:before{content:"\f0c0"}.icon-users-cog:before{content:"\f509"}.icon-users-slash:before{content:"\e073"}.icon-usps:before{content:"\f7e1"}.icon-ussunnah:before{content:"\f407"}.icon-utensil-spoon:before{content:"\f2e5"}.icon-utensils:before{content:"\f2e7"}.icon-vaadin:before{content:"\f408"}.icon-vector-square:before{content:"\f5cb"}.icon-venus:before{content:"\f221"}.icon-venus-double:before{content:"\f226"}.icon-venus-mars:before{content:"\f228"}.icon-vest:before{content:"\e085"}.icon-vest-patches:before{content:"\e086"}.icon-viacoin:before{content:"\f237"}.icon-viadeo:before{content:"\f2a9"}.icon-viadeo-square:before{content:"\f2aa"}.icon-vial:before{content:"\f492"}.icon-vials:before{content:"\f493"}.icon-viber:before{content:"\f409"}.icon-video:before{content:"\f03d"}.icon-video-slash:before{content:"\f4e2"}.icon-vihara:before{content:"\f6a7"}.icon-vimeo:before{content:"\f40a"}.icon-vimeo-square:before{content:"\f194"}.icon-vimeo-v:before{content:"\f27d"}.icon-vine:before{content:"\f1ca"}.icon-virus:before{content:"\e074"}.icon-virus-slash:before{content:"\e075"}.icon-viruses:before{content:"\e076"}.icon-vk:before{content:"\f189"}.icon-vnv:before{content:"\f40b"}.icon-voicemail:before{content:"\f897"}.icon-volleyball-ball:before{content:"\f45f"}.icon-volume-down:before{content:"\f027"}.icon-volume-mute:before{content:"\f6a9"}.icon-volume-off:before{content:"\f026"}.icon-volume-up:before{content:"\f028"}.icon-vote-yea:before{content:"\f772"}.icon-vr-cardboard:before{content:"\f729"}.icon-vuejs:before{content:"\f41f"}.icon-walking:before{content:"\f554"}.icon-wallet:before{content:"\f555"}.icon-warehouse:before{content:"\f494"}.icon-watchman-monitoring:before{content:"\e087"}.icon-water:before{content:"\f773"}.icon-wave-square:before{content:"\f83e"}.icon-waze:before{content:"\f83f"}.icon-weebly:before{content:"\f5cc"}.icon-weibo:before{content:"\f18a"}.icon-weight:before{content:"\f496"}.icon-weight-hanging:before{content:"\f5cd"}.icon-weixin:before{content:"\f1d7"}.icon-whatsapp:before{content:"\f232"}.icon-whatsapp-square:before{content:"\f40c"}.icon-wheelchair:before{content:"\f193"}.icon-whmcs:before{content:"\f40d"}.icon-wifi:before{content:"\f1eb"}.icon-wikipedia-w:before{content:"\f266"}.icon-wind:before{content:"\f72e"}.icon-window-close:before{content:"\f410"}.icon-window-maximize:before{content:"\f2d0"}.icon-window-minimize:before{content:"\f2d1"}.icon-window-restore:before{content:"\f2d2"}.icon-windows:before{content:"\f17a"}.icon-wine-bottle:before{content:"\f72f"}.icon-wine-glass:before{content:"\f4e3"}.icon-wine-glass-alt:before{content:"\f5ce"}.icon-wix:before{content:"\f5cf"}.icon-wizards-of-the-coast:before{content:"\f730"}.icon-wodu:before{content:"\e088"}.icon-wolf-pack-battalion:before{content:"\f514"}.icon-won-sign:before{content:"\f159"}.icon-wordpress:before{content:"\f19a"}.icon-wordpress-simple:before{content:"\f411"}.icon-wpbeginner:before{content:"\f297"}.icon-wpexplorer:before{content:"\f2de"}.icon-wpforms:before{content:"\f298"}.icon-wpressr:before{content:"\f3e4"}.icon-wrench:before{content:"\f0ad"}.icon-x-ray:before{content:"\f497"}.icon-xbox:before{content:"\f412"}.icon-xing:before{content:"\f168"}.icon-xing-square:before{content:"\f169"}.icon-y-combinator:before{content:"\f23b"}.icon-yahoo:before{content:"\f19e"}.icon-yammer:before{content:"\f840"}.icon-yandex:before{content:"\f413"}.icon-yandex-international:before{content:"\f414"}.icon-yarn:before{content:"\f7e3"}.icon-yelp:before{content:"\f1e9"}.icon-yen-sign:before{content:"\f157"}.icon-yin-yang:before{content:"\f6ad"}.icon-yoast:before{content:"\f2b1"}.icon-youtube:before{content:"\f167"}.icon-youtube-square:before{content:"\f431"}.icon-zhihu:before{content:"\f63f"}.sr-only{clip:rect(0,0,0,0);border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto} + */.fab,.fad,.fal,.far,.fas,.icon{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:auto;display:inline-block;font-style:normal;font-variant:normal;line-height:1}.icon-large,.icon-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-.0667em}.icon-xs{font-size:.75em}.icon-sm{font-size:.875em}.icon-1x{font-size:1em}.icon-2x{font-size:2em}.icon-3x{font-size:3em}.icon-4x{font-size:4em}.icon-5x{font-size:5em}.icon-6x{font-size:6em}.icon-7x{font-size:7em}.icon-8x{font-size:8em}.icon-9x{font-size:9em}.icon-10x{font-size:10em}.icon-fw{text-align:center;width:1.25em}.icon-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.icon-ul>li{position:relative}.icon-li{left:-2em;line-height:inherit;position:absolute;text-align:center;width:2em}.icon-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.icon-pull-left{float:left}.icon-pull-right{float:right}.fab.icon-pull-left,.fal.icon-pull-left,.far.icon-pull-left,.fas.icon-pull-left,.icon.icon-pull-left{margin-right:.3em}.fab.icon-pull-right,.fal.icon-pull-right,.far.icon-pull-right,.fas.icon-pull-right,.icon.icon-pull-right{margin-left:.3em}.icon-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.icon-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.icon-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.icon-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.icon-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.icon-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.icon-flip-vertical{transform:scaleY(-1)}.icon-flip-both,.icon-flip-horizontal.icon-flip-vertical,.icon-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.icon-flip-both,.icon-flip-horizontal.icon-flip-vertical{transform:scale(-1)}:root .icon-flip-both,:root .icon-flip-horizontal,:root .icon-flip-vertical,:root .icon-rotate-180,:root .icon-rotate-270,:root .icon-rotate-90{filter:none}.icon-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.icon-stack-1x,.icon-stack-2x{left:0;position:absolute;text-align:center;width:100%}.icon-stack-1x{line-height:inherit}.icon-stack-2x{font-size:2em}.icon-inverse{color:#fff}.icon-500px:before{content:"\f26e"}.icon-accessible-icon:before{content:"\f368"}.icon-accusoft:before{content:"\f369"}.icon-acquisitions-incorporated:before{content:"\f6af"}.icon-ad:before{content:"\f641"}.icon-address-book:before{content:"\f2b9"}.icon-address-card:before{content:"\f2bb"}.icon-adjust:before{content:"\f042"}.icon-adn:before{content:"\f170"}.icon-adversal:before{content:"\f36a"}.icon-affiliatetheme:before{content:"\f36b"}.icon-air-freshener:before{content:"\f5d0"}.icon-airbnb:before{content:"\f834"}.icon-algolia:before{content:"\f36c"}.icon-align-center:before{content:"\f037"}.icon-align-justify:before{content:"\f039"}.icon-align-left:before{content:"\f036"}.icon-align-right:before{content:"\f038"}.icon-alipay:before{content:"\f642"}.icon-allergies:before{content:"\f461"}.icon-amazon:before{content:"\f270"}.icon-amazon-pay:before{content:"\f42c"}.icon-ambulance:before{content:"\f0f9"}.icon-american-sign-language-interpreting:before{content:"\f2a3"}.icon-amilia:before{content:"\f36d"}.icon-anchor:before{content:"\f13d"}.icon-android:before{content:"\f17b"}.icon-angellist:before{content:"\f209"}.icon-angle-double-down:before{content:"\f103"}.icon-angle-double-left:before{content:"\f100"}.icon-angle-double-right:before{content:"\f101"}.icon-angle-double-up:before{content:"\f102"}.icon-angle-down:before{content:"\f107"}.icon-angle-left:before{content:"\f104"}.icon-angle-right:before{content:"\f105"}.icon-angle-up:before{content:"\f106"}.icon-angry:before{content:"\f556"}.icon-angrycreative:before{content:"\f36e"}.icon-angular:before{content:"\f420"}.icon-ankh:before{content:"\f644"}.icon-app-store:before{content:"\f36f"}.icon-app-store-ios:before{content:"\f370"}.icon-apper:before{content:"\f371"}.icon-apple:before{content:"\f179"}.icon-apple-alt:before{content:"\f5d1"}.icon-apple-pay:before{content:"\f415"}.icon-archive:before{content:"\f187"}.icon-archway:before{content:"\f557"}.icon-arrow-alt-circle-down:before{content:"\f358"}.icon-arrow-alt-circle-left:before{content:"\f359"}.icon-arrow-alt-circle-right:before{content:"\f35a"}.icon-arrow-alt-circle-up:before{content:"\f35b"}.icon-arrow-circle-down:before{content:"\f0ab"}.icon-arrow-circle-left:before{content:"\f0a8"}.icon-arrow-circle-right:before{content:"\f0a9"}.icon-arrow-circle-up:before{content:"\f0aa"}.icon-arrow-down:before{content:"\f063"}.icon-arrow-left:before{content:"\f060"}.icon-arrow-right:before{content:"\f061"}.icon-arrow-up:before{content:"\f062"}.icon-arrows-alt:before{content:"\f0b2"}.icon-arrows-alt-h:before{content:"\f337"}.icon-arrows-alt-v:before{content:"\f338"}.icon-artstation:before{content:"\f77a"}.icon-assistive-listening-systems:before{content:"\f2a2"}.icon-asterisk:before{content:"\f069"}.icon-asymmetrik:before{content:"\f372"}.icon-at:before{content:"\f1fa"}.icon-atlas:before{content:"\f558"}.icon-atlassian:before{content:"\f77b"}.icon-atom:before{content:"\f5d2"}.icon-audible:before{content:"\f373"}.icon-audio-description:before{content:"\f29e"}.icon-autoprefixer:before{content:"\f41c"}.icon-avianex:before{content:"\f374"}.icon-aviato:before{content:"\f421"}.icon-award:before{content:"\f559"}.icon-aws:before{content:"\f375"}.icon-baby:before{content:"\f77c"}.icon-baby-carriage:before{content:"\f77d"}.icon-backspace:before{content:"\f55a"}.icon-backward:before{content:"\f04a"}.icon-bacon:before{content:"\f7e5"}.icon-bacteria:before{content:"\e059"}.icon-bacterium:before{content:"\e05a"}.icon-bahai:before{content:"\f666"}.icon-balance-scale:before{content:"\f24e"}.icon-balance-scale-left:before{content:"\f515"}.icon-balance-scale-right:before{content:"\f516"}.icon-ban:before{content:"\f05e"}.icon-band-aid:before{content:"\f462"}.icon-bandcamp:before{content:"\f2d5"}.icon-barcode:before{content:"\f02a"}.icon-bars:before{content:"\f0c9"}.icon-baseball-ball:before{content:"\f433"}.icon-basketball-ball:before{content:"\f434"}.icon-bath:before{content:"\f2cd"}.icon-battery-empty:before{content:"\f244"}.icon-battery-full:before{content:"\f240"}.icon-battery-half:before{content:"\f242"}.icon-battery-quarter:before{content:"\f243"}.icon-battery-three-quarters:before{content:"\f241"}.icon-battle-net:before{content:"\f835"}.icon-bed:before{content:"\f236"}.icon-beer:before{content:"\f0fc"}.icon-behance:before{content:"\f1b4"}.icon-behance-square:before{content:"\f1b5"}.icon-bell:before{content:"\f0f3"}.icon-bell-slash:before{content:"\f1f6"}.icon-bezier-curve:before{content:"\f55b"}.icon-bible:before{content:"\f647"}.icon-bicycle:before{content:"\f206"}.icon-biking:before{content:"\f84a"}.icon-bimobject:before{content:"\f378"}.icon-binoculars:before{content:"\f1e5"}.icon-biohazard:before{content:"\f780"}.icon-birthday-cake:before{content:"\f1fd"}.icon-bitbucket:before{content:"\f171"}.icon-bitcoin:before{content:"\f379"}.icon-bity:before{content:"\f37a"}.icon-black-tie:before{content:"\f27e"}.icon-blackberry:before{content:"\f37b"}.icon-blender:before{content:"\f517"}.icon-blender-phone:before{content:"\f6b6"}.icon-blind:before{content:"\f29d"}.icon-blog:before{content:"\f781"}.icon-blogger:before{content:"\f37c"}.icon-blogger-b:before{content:"\f37d"}.icon-bluetooth:before{content:"\f293"}.icon-bluetooth-b:before{content:"\f294"}.icon-bold:before{content:"\f032"}.icon-bolt:before{content:"\f0e7"}.icon-bomb:before{content:"\f1e2"}.icon-bone:before{content:"\f5d7"}.icon-bong:before{content:"\f55c"}.icon-book:before{content:"\f02d"}.icon-book-dead:before{content:"\f6b7"}.icon-book-medical:before{content:"\f7e6"}.icon-book-open:before{content:"\f518"}.icon-book-reader:before{content:"\f5da"}.icon-bookmark:before{content:"\f02e"}.icon-bootstrap:before{content:"\f836"}.icon-border-all:before{content:"\f84c"}.icon-border-none:before{content:"\f850"}.icon-border-style:before{content:"\f853"}.icon-bowling-ball:before{content:"\f436"}.icon-box:before{content:"\f466"}.icon-box-open:before{content:"\f49e"}.icon-box-tissue:before{content:"\e05b"}.icon-boxes:before{content:"\f468"}.icon-braille:before{content:"\f2a1"}.icon-brain:before{content:"\f5dc"}.icon-bread-slice:before{content:"\f7ec"}.icon-briefcase:before{content:"\f0b1"}.icon-briefcase-medical:before{content:"\f469"}.icon-broadcast-tower:before{content:"\f519"}.icon-broom:before{content:"\f51a"}.icon-brush:before{content:"\f55d"}.icon-btc:before{content:"\f15a"}.icon-buffer:before{content:"\f837"}.icon-bug:before{content:"\f188"}.icon-building:before{content:"\f1ad"}.icon-bullhorn:before{content:"\f0a1"}.icon-bullseye:before{content:"\f140"}.icon-burn:before{content:"\f46a"}.icon-buromobelexperte:before{content:"\f37f"}.icon-bus:before{content:"\f207"}.icon-bus-alt:before{content:"\f55e"}.icon-business-time:before{content:"\f64a"}.icon-buy-n-large:before{content:"\f8a6"}.icon-buysellads:before{content:"\f20d"}.icon-calculator:before{content:"\f1ec"}.icon-calendar:before{content:"\f133"}.icon-calendar-alt:before{content:"\f073"}.icon-calendar-check:before{content:"\f274"}.icon-calendar-day:before{content:"\f783"}.icon-calendar-minus:before{content:"\f272"}.icon-calendar-plus:before{content:"\f271"}.icon-calendar-times:before{content:"\f273"}.icon-calendar-week:before{content:"\f784"}.icon-camera:before{content:"\f030"}.icon-camera-retro:before{content:"\f083"}.icon-campground:before{content:"\f6bb"}.icon-canadian-maple-leaf:before{content:"\f785"}.icon-candy-cane:before{content:"\f786"}.icon-cannabis:before{content:"\f55f"}.icon-capsules:before{content:"\f46b"}.icon-car:before{content:"\f1b9"}.icon-car-alt:before{content:"\f5de"}.icon-car-battery:before{content:"\f5df"}.icon-car-crash:before{content:"\f5e1"}.icon-car-side:before{content:"\f5e4"}.icon-caravan:before{content:"\f8ff"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.icon-caret-square-down:before{content:"\f150"}.icon-caret-square-left:before{content:"\f191"}.icon-caret-square-right:before{content:"\f152"}.icon-caret-square-up:before{content:"\f151"}.icon-caret-up:before{content:"\f0d8"}.icon-carrot:before{content:"\f787"}.icon-cart-arrow-down:before{content:"\f218"}.icon-cart-plus:before{content:"\f217"}.icon-cash-register:before{content:"\f788"}.icon-cat:before{content:"\f6be"}.icon-cc-amazon-pay:before{content:"\f42d"}.icon-cc-amex:before{content:"\f1f3"}.icon-cc-apple-pay:before{content:"\f416"}.icon-cc-diners-club:before{content:"\f24c"}.icon-cc-discover:before{content:"\f1f2"}.icon-cc-jcb:before{content:"\f24b"}.icon-cc-mastercard:before{content:"\f1f1"}.icon-cc-paypal:before{content:"\f1f4"}.icon-cc-stripe:before{content:"\f1f5"}.icon-cc-visa:before{content:"\f1f0"}.icon-centercode:before{content:"\f380"}.icon-centos:before{content:"\f789"}.icon-certificate:before{content:"\f0a3"}.icon-chair:before{content:"\f6c0"}.icon-chalkboard:before{content:"\f51b"}.icon-chalkboard-teacher:before{content:"\f51c"}.icon-charging-station:before{content:"\f5e7"}.icon-chart-area:before{content:"\f1fe"}.icon-chart-bar:before{content:"\f080"}.icon-chart-line:before{content:"\f201"}.icon-chart-pie:before{content:"\f200"}.icon-check:before{content:"\f00c"}.icon-check-circle:before{content:"\f058"}.icon-check-double:before{content:"\f560"}.icon-check-square:before{content:"\f14a"}.icon-cheese:before{content:"\f7ef"}.icon-chess:before{content:"\f439"}.icon-chess-bishop:before{content:"\f43a"}.icon-chess-board:before{content:"\f43c"}.icon-chess-king:before{content:"\f43f"}.icon-chess-knight:before{content:"\f441"}.icon-chess-pawn:before{content:"\f443"}.icon-chess-queen:before{content:"\f445"}.icon-chess-rook:before{content:"\f447"}.icon-chevron-circle-down:before{content:"\f13a"}.icon-chevron-circle-left:before{content:"\f137"}.icon-chevron-circle-right:before{content:"\f138"}.icon-chevron-circle-up:before{content:"\f139"}.icon-chevron-down:before{content:"\f078"}.icon-chevron-left:before{content:"\f053"}.icon-chevron-right:before{content:"\f054"}.icon-chevron-up:before{content:"\f077"}.icon-child:before{content:"\f1ae"}.icon-chrome:before{content:"\f268"}.icon-chromecast:before{content:"\f838"}.icon-church:before{content:"\f51d"}.icon-circle:before{content:"\f111"}.icon-circle-notch:before{content:"\f1ce"}.icon-city:before{content:"\f64f"}.icon-clinic-medical:before{content:"\f7f2"}.icon-clipboard:before{content:"\f328"}.icon-clipboard-check:before{content:"\f46c"}.icon-clipboard-list:before{content:"\f46d"}.icon-clock:before{content:"\f017"}.icon-clone:before{content:"\f24d"}.icon-closed-captioning:before{content:"\f20a"}.icon-cloud:before{content:"\f0c2"}.icon-cloud-download-alt:before{content:"\f381"}.icon-cloud-meatball:before{content:"\f73b"}.icon-cloud-moon:before{content:"\f6c3"}.icon-cloud-moon-rain:before{content:"\f73c"}.icon-cloud-rain:before{content:"\f73d"}.icon-cloud-showers-heavy:before{content:"\f740"}.icon-cloud-sun:before{content:"\f6c4"}.icon-cloud-sun-rain:before{content:"\f743"}.icon-cloud-upload-alt:before{content:"\f382"}.icon-cloudflare:before{content:"\e07d"}.icon-cloudscale:before{content:"\f383"}.icon-cloudsmith:before{content:"\f384"}.icon-cloudversify:before{content:"\f385"}.icon-cocktail:before{content:"\f561"}.icon-code:before{content:"\f121"}.icon-code-branch:before{content:"\f126"}.icon-codepen:before{content:"\f1cb"}.icon-codiepie:before{content:"\f284"}.icon-coffee:before{content:"\f0f4"}.icon-cog:before{content:"\f013"}.icon-cogs:before{content:"\f085"}.icon-coins:before{content:"\f51e"}.icon-columns:before{content:"\f0db"}.icon-comment:before{content:"\f075"}.icon-comment-alt:before{content:"\f27a"}.icon-comment-dollar:before{content:"\f651"}.icon-comment-dots:before{content:"\f4ad"}.icon-comment-medical:before{content:"\f7f5"}.icon-comment-slash:before{content:"\f4b3"}.icon-comments:before{content:"\f086"}.icon-comments-dollar:before{content:"\f653"}.icon-compact-disc:before{content:"\f51f"}.icon-compass:before{content:"\f14e"}.icon-compress:before{content:"\f066"}.icon-compress-alt:before{content:"\f422"}.icon-compress-arrows-alt:before{content:"\f78c"}.icon-concierge-bell:before{content:"\f562"}.icon-confluence:before{content:"\f78d"}.icon-connectdevelop:before{content:"\f20e"}.icon-contao:before{content:"\f26d"}.icon-cookie:before{content:"\f563"}.icon-cookie-bite:before{content:"\f564"}.icon-copy:before{content:"\f0c5"}.icon-copyright:before{content:"\f1f9"}.icon-cotton-bureau:before{content:"\f89e"}.icon-couch:before{content:"\f4b8"}.icon-cpanel:before{content:"\f388"}.icon-creative-commons:before{content:"\f25e"}.icon-creative-commons-by:before{content:"\f4e7"}.icon-creative-commons-nc:before{content:"\f4e8"}.icon-creative-commons-nc-eu:before{content:"\f4e9"}.icon-creative-commons-nc-jp:before{content:"\f4ea"}.icon-creative-commons-nd:before{content:"\f4eb"}.icon-creative-commons-pd:before{content:"\f4ec"}.icon-creative-commons-pd-alt:before{content:"\f4ed"}.icon-creative-commons-remix:before{content:"\f4ee"}.icon-creative-commons-sa:before{content:"\f4ef"}.icon-creative-commons-sampling:before{content:"\f4f0"}.icon-creative-commons-sampling-plus:before{content:"\f4f1"}.icon-creative-commons-share:before{content:"\f4f2"}.icon-creative-commons-zero:before{content:"\f4f3"}.icon-credit-card:before{content:"\f09d"}.icon-critical-role:before{content:"\f6c9"}.icon-crop:before{content:"\f125"}.icon-crop-alt:before{content:"\f565"}.icon-cross:before{content:"\f654"}.icon-crosshairs:before{content:"\f05b"}.icon-crow:before{content:"\f520"}.icon-crown:before{content:"\f521"}.icon-crutch:before{content:"\f7f7"}.icon-css3:before{content:"\f13c"}.icon-css3-alt:before{content:"\f38b"}.icon-cube:before{content:"\f1b2"}.icon-cubes:before{content:"\f1b3"}.icon-cut:before{content:"\f0c4"}.icon-cuttlefish:before{content:"\f38c"}.icon-d-and-d:before{content:"\f38d"}.icon-d-and-d-beyond:before{content:"\f6ca"}.icon-dailymotion:before{content:"\e052"}.icon-dashcube:before{content:"\f210"}.icon-database:before{content:"\f1c0"}.icon-deaf:before{content:"\f2a4"}.icon-deezer:before{content:"\e077"}.icon-delicious:before{content:"\f1a5"}.icon-democrat:before{content:"\f747"}.icon-deploydog:before{content:"\f38e"}.icon-deskpro:before{content:"\f38f"}.icon-desktop:before{content:"\f108"}.icon-dev:before{content:"\f6cc"}.icon-deviantart:before{content:"\f1bd"}.icon-dharmachakra:before{content:"\f655"}.icon-dhl:before{content:"\f790"}.icon-diagnoses:before{content:"\f470"}.icon-diaspora:before{content:"\f791"}.icon-dice:before{content:"\f522"}.icon-dice-d20:before{content:"\f6cf"}.icon-dice-d6:before{content:"\f6d1"}.icon-dice-five:before{content:"\f523"}.icon-dice-four:before{content:"\f524"}.icon-dice-one:before{content:"\f525"}.icon-dice-six:before{content:"\f526"}.icon-dice-three:before{content:"\f527"}.icon-dice-two:before{content:"\f528"}.icon-digg:before{content:"\f1a6"}.icon-digital-ocean:before{content:"\f391"}.icon-digital-tachograph:before{content:"\f566"}.icon-directions:before{content:"\f5eb"}.icon-discord:before{content:"\f392"}.icon-discourse:before{content:"\f393"}.icon-disease:before{content:"\f7fa"}.icon-divide:before{content:"\f529"}.icon-dizzy:before{content:"\f567"}.icon-dna:before{content:"\f471"}.icon-dochub:before{content:"\f394"}.icon-docker:before{content:"\f395"}.icon-dog:before{content:"\f6d3"}.icon-dollar-sign:before{content:"\f155"}.icon-dolly:before{content:"\f472"}.icon-dolly-flatbed:before{content:"\f474"}.icon-donate:before{content:"\f4b9"}.icon-door-closed:before{content:"\f52a"}.icon-door-open:before{content:"\f52b"}.icon-dot-circle:before{content:"\f192"}.icon-dove:before{content:"\f4ba"}.icon-download:before{content:"\f019"}.icon-draft2digital:before{content:"\f396"}.icon-drafting-compass:before{content:"\f568"}.icon-dragon:before{content:"\f6d5"}.icon-draw-polygon:before{content:"\f5ee"}.icon-dribbble:before{content:"\f17d"}.icon-dribbble-square:before{content:"\f397"}.icon-dropbox:before{content:"\f16b"}.icon-drum:before{content:"\f569"}.icon-drum-steelpan:before{content:"\f56a"}.icon-drumstick-bite:before{content:"\f6d7"}.icon-drupal:before{content:"\f1a9"}.icon-dumbbell:before{content:"\f44b"}.icon-dumpster:before{content:"\f793"}.icon-dumpster-fire:before{content:"\f794"}.icon-dungeon:before{content:"\f6d9"}.icon-dyalog:before{content:"\f399"}.icon-earlybirds:before{content:"\f39a"}.icon-ebay:before{content:"\f4f4"}.icon-edge:before{content:"\f282"}.icon-edge-legacy:before{content:"\e078"}.icon-edit:before{content:"\f044"}.icon-egg:before{content:"\f7fb"}.icon-eject:before{content:"\f052"}.icon-elementor:before{content:"\f430"}.icon-ellipsis-h:before{content:"\f141"}.icon-ellipsis-v:before{content:"\f142"}.icon-ello:before{content:"\f5f1"}.icon-ember:before{content:"\f423"}.icon-empire:before{content:"\f1d1"}.icon-envelope:before{content:"\f0e0"}.icon-envelope-open:before{content:"\f2b6"}.icon-envelope-open-text:before{content:"\f658"}.icon-envelope-square:before{content:"\f199"}.icon-envira:before{content:"\f299"}.icon-equals:before{content:"\f52c"}.icon-eraser:before{content:"\f12d"}.icon-erlang:before{content:"\f39d"}.icon-ethereum:before{content:"\f42e"}.icon-ethernet:before{content:"\f796"}.icon-etsy:before{content:"\f2d7"}.icon-euro-sign:before{content:"\f153"}.icon-evernote:before{content:"\f839"}.icon-exchange-alt:before{content:"\f362"}.icon-exclamation:before{content:"\f12a"}.icon-exclamation-circle:before{content:"\f06a"}.icon-exclamation-triangle:before{content:"\f071"}.icon-expand:before{content:"\f065"}.icon-expand-alt:before{content:"\f424"}.icon-expand-arrows-alt:before{content:"\f31e"}.icon-expeditedssl:before{content:"\f23e"}.icon-external-link-alt:before{content:"\f35d"}.icon-external-link-square-alt:before{content:"\f360"}.icon-eye:before{content:"\f06e"}.icon-eye-dropper:before{content:"\f1fb"}.icon-eye-slash:before{content:"\f070"}.icon-facebook:before{content:"\f09a"}.icon-facebook-f:before{content:"\f39e"}.icon-facebook-messenger:before{content:"\f39f"}.icon-facebook-square:before{content:"\f082"}.icon-fan:before{content:"\f863"}.icon-fantasy-flight-games:before{content:"\f6dc"}.icon-fast-backward:before{content:"\f049"}.icon-fast-forward:before{content:"\f050"}.icon-faucet:before{content:"\e005"}.icon-fax:before{content:"\f1ac"}.icon-feather:before{content:"\f52d"}.icon-feather-alt:before{content:"\f56b"}.icon-fedex:before{content:"\f797"}.icon-fedora:before{content:"\f798"}.icon-female:before{content:"\f182"}.icon-fighter-jet:before{content:"\f0fb"}.icon-figma:before{content:"\f799"}.icon-file:before{content:"\f15b"}.icon-file-alt:before{content:"\f15c"}.icon-file-archive:before{content:"\f1c6"}.icon-file-audio:before{content:"\f1c7"}.icon-file-code:before{content:"\f1c9"}.icon-file-contract:before{content:"\f56c"}.icon-file-csv:before{content:"\f6dd"}.icon-file-download:before{content:"\f56d"}.icon-file-excel:before{content:"\f1c3"}.icon-file-export:before{content:"\f56e"}.icon-file-image:before{content:"\f1c5"}.icon-file-import:before{content:"\f56f"}.icon-file-invoice:before{content:"\f570"}.icon-file-invoice-dollar:before{content:"\f571"}.icon-file-medical:before{content:"\f477"}.icon-file-medical-alt:before{content:"\f478"}.icon-file-pdf:before{content:"\f1c1"}.icon-file-powerpoint:before{content:"\f1c4"}.icon-file-prescription:before{content:"\f572"}.icon-file-signature:before{content:"\f573"}.icon-file-upload:before{content:"\f574"}.icon-file-video:before{content:"\f1c8"}.icon-file-word:before{content:"\f1c2"}.icon-fill:before{content:"\f575"}.icon-fill-drip:before{content:"\f576"}.icon-film:before{content:"\f008"}.icon-filter:before{content:"\f0b0"}.icon-fingerprint:before{content:"\f577"}.icon-fire:before{content:"\f06d"}.icon-fire-alt:before{content:"\f7e4"}.icon-fire-extinguisher:before{content:"\f134"}.icon-firefox:before{content:"\f269"}.icon-firefox-browser:before{content:"\e007"}.icon-first-aid:before{content:"\f479"}.icon-first-order:before{content:"\f2b0"}.icon-first-order-alt:before{content:"\f50a"}.icon-firstdraft:before{content:"\f3a1"}.icon-fish:before{content:"\f578"}.icon-fist-raised:before{content:"\f6de"}.icon-flag:before{content:"\f024"}.icon-flag-checkered:before{content:"\f11e"}.icon-flag-usa:before{content:"\f74d"}.icon-flask:before{content:"\f0c3"}.icon-flickr:before{content:"\f16e"}.icon-flipboard:before{content:"\f44d"}.icon-flushed:before{content:"\f579"}.icon-fly:before{content:"\f417"}.icon-folder:before{content:"\f07b"}.icon-folder-minus:before{content:"\f65d"}.icon-folder-open:before{content:"\f07c"}.icon-folder-plus:before{content:"\f65e"}.icon-font:before{content:"\f031"}.icon-font-awesome:before{content:"\f2b4"}.icon-font-awesome-alt:before{content:"\f35c"}.icon-font-awesome-flag:before{content:"\f425"}.icon-font-awesome-logo-full:before{content:"\f4e6"}.icon-fonticons:before{content:"\f280"}.icon-fonticons-fi:before{content:"\f3a2"}.icon-football-ball:before{content:"\f44e"}.icon-fort-awesome:before{content:"\f286"}.icon-fort-awesome-alt:before{content:"\f3a3"}.icon-forumbee:before{content:"\f211"}.icon-forward:before{content:"\f04e"}.icon-foursquare:before{content:"\f180"}.icon-free-code-camp:before{content:"\f2c5"}.icon-freebsd:before{content:"\f3a4"}.icon-frog:before{content:"\f52e"}.icon-frown:before{content:"\f119"}.icon-frown-open:before{content:"\f57a"}.icon-fulcrum:before{content:"\f50b"}.icon-funnel-dollar:before{content:"\f662"}.icon-futbol:before{content:"\f1e3"}.icon-galactic-republic:before{content:"\f50c"}.icon-galactic-senate:before{content:"\f50d"}.icon-gamepad:before{content:"\f11b"}.icon-gas-pump:before{content:"\f52f"}.icon-gavel:before{content:"\f0e3"}.icon-gem:before{content:"\f3a5"}.icon-genderless:before{content:"\f22d"}.icon-get-pocket:before{content:"\f265"}.icon-gg:before{content:"\f260"}.icon-gg-circle:before{content:"\f261"}.icon-ghost:before{content:"\f6e2"}.icon-gift:before{content:"\f06b"}.icon-gifts:before{content:"\f79c"}.icon-git:before{content:"\f1d3"}.icon-git-alt:before{content:"\f841"}.icon-git-square:before{content:"\f1d2"}.icon-github:before{content:"\f09b"}.icon-github-alt:before{content:"\f113"}.icon-github-square:before{content:"\f092"}.icon-gitkraken:before{content:"\f3a6"}.icon-gitlab:before{content:"\f296"}.icon-gitter:before{content:"\f426"}.icon-glass-cheers:before{content:"\f79f"}.icon-glass-martini:before{content:"\f000"}.icon-glass-martini-alt:before{content:"\f57b"}.icon-glass-whiskey:before{content:"\f7a0"}.icon-glasses:before{content:"\f530"}.icon-glide:before{content:"\f2a5"}.icon-glide-g:before{content:"\f2a6"}.icon-globe:before{content:"\f0ac"}.icon-globe-africa:before{content:"\f57c"}.icon-globe-americas:before{content:"\f57d"}.icon-globe-asia:before{content:"\f57e"}.icon-globe-europe:before{content:"\f7a2"}.icon-gofore:before{content:"\f3a7"}.icon-golf-ball:before{content:"\f450"}.icon-goodreads:before{content:"\f3a8"}.icon-goodreads-g:before{content:"\f3a9"}.icon-google:before{content:"\f1a0"}.icon-google-drive:before{content:"\f3aa"}.icon-google-pay:before{content:"\e079"}.icon-google-play:before{content:"\f3ab"}.icon-google-plus:before{content:"\f2b3"}.icon-google-plus-g:before{content:"\f0d5"}.icon-google-plus-square:before{content:"\f0d4"}.icon-google-wallet:before{content:"\f1ee"}.icon-gopuram:before{content:"\f664"}.icon-graduation-cap:before{content:"\f19d"}.icon-gratipay:before{content:"\f184"}.icon-grav:before{content:"\f2d6"}.icon-greater-than:before{content:"\f531"}.icon-greater-than-equal:before{content:"\f532"}.icon-grimace:before{content:"\f57f"}.icon-grin:before{content:"\f580"}.icon-grin-alt:before{content:"\f581"}.icon-grin-beam:before{content:"\f582"}.icon-grin-beam-sweat:before{content:"\f583"}.icon-grin-hearts:before{content:"\f584"}.icon-grin-squint:before{content:"\f585"}.icon-grin-squint-tears:before{content:"\f586"}.icon-grin-stars:before{content:"\f587"}.icon-grin-tears:before{content:"\f588"}.icon-grin-tongue:before{content:"\f589"}.icon-grin-tongue-squint:before{content:"\f58a"}.icon-grin-tongue-wink:before{content:"\f58b"}.icon-grin-wink:before{content:"\f58c"}.icon-grip-horizontal:before{content:"\f58d"}.icon-grip-lines:before{content:"\f7a4"}.icon-grip-lines-vertical:before{content:"\f7a5"}.icon-grip-vertical:before{content:"\f58e"}.icon-gripfire:before{content:"\f3ac"}.icon-grunt:before{content:"\f3ad"}.icon-guilded:before{content:"\e07e"}.icon-guitar:before{content:"\f7a6"}.icon-gulp:before{content:"\f3ae"}.icon-h-square:before{content:"\f0fd"}.icon-hacker-news:before{content:"\f1d4"}.icon-hacker-news-square:before{content:"\f3af"}.icon-hackerrank:before{content:"\f5f7"}.icon-hamburger:before{content:"\f805"}.icon-hammer:before{content:"\f6e3"}.icon-hamsa:before{content:"\f665"}.icon-hand-holding:before{content:"\f4bd"}.icon-hand-holding-heart:before{content:"\f4be"}.icon-hand-holding-medical:before{content:"\e05c"}.icon-hand-holding-usd:before{content:"\f4c0"}.icon-hand-holding-water:before{content:"\f4c1"}.icon-hand-lizard:before{content:"\f258"}.icon-hand-middle-finger:before{content:"\f806"}.icon-hand-paper:before{content:"\f256"}.icon-hand-peace:before{content:"\f25b"}.icon-hand-point-down:before{content:"\f0a7"}.icon-hand-point-left:before{content:"\f0a5"}.icon-hand-point-right:before{content:"\f0a4"}.icon-hand-point-up:before{content:"\f0a6"}.icon-hand-pointer:before{content:"\f25a"}.icon-hand-rock:before{content:"\f255"}.icon-hand-scissors:before{content:"\f257"}.icon-hand-sparkles:before{content:"\e05d"}.icon-hand-spock:before{content:"\f259"}.icon-hands:before{content:"\f4c2"}.icon-hands-helping:before{content:"\f4c4"}.icon-hands-wash:before{content:"\e05e"}.icon-handshake:before{content:"\f2b5"}.icon-handshake-alt-slash:before{content:"\e05f"}.icon-handshake-slash:before{content:"\e060"}.icon-hanukiah:before{content:"\f6e6"}.icon-hard-hat:before{content:"\f807"}.icon-hashtag:before{content:"\f292"}.icon-hat-cowboy:before{content:"\f8c0"}.icon-hat-cowboy-side:before{content:"\f8c1"}.icon-hat-wizard:before{content:"\f6e8"}.icon-hdd:before{content:"\f0a0"}.icon-head-side-cough:before{content:"\e061"}.icon-head-side-cough-slash:before{content:"\e062"}.icon-head-side-mask:before{content:"\e063"}.icon-head-side-virus:before{content:"\e064"}.icon-heading:before{content:"\f1dc"}.icon-headphones:before{content:"\f025"}.icon-headphones-alt:before{content:"\f58f"}.icon-headset:before{content:"\f590"}.icon-heart:before{content:"\f004"}.icon-heart-broken:before{content:"\f7a9"}.icon-heartbeat:before{content:"\f21e"}.icon-helicopter:before{content:"\f533"}.icon-highlighter:before{content:"\f591"}.icon-hiking:before{content:"\f6ec"}.icon-hippo:before{content:"\f6ed"}.icon-hips:before{content:"\f452"}.icon-hire-a-helper:before{content:"\f3b0"}.icon-history:before{content:"\f1da"}.icon-hive:before{content:"\e07f"}.icon-hockey-puck:before{content:"\f453"}.icon-holly-berry:before{content:"\f7aa"}.icon-home:before{content:"\f015"}.icon-hooli:before{content:"\f427"}.icon-hornbill:before{content:"\f592"}.icon-horse:before{content:"\f6f0"}.icon-horse-head:before{content:"\f7ab"}.icon-hospital:before{content:"\f0f8"}.icon-hospital-alt:before{content:"\f47d"}.icon-hospital-symbol:before{content:"\f47e"}.icon-hospital-user:before{content:"\f80d"}.icon-hot-tub:before{content:"\f593"}.icon-hotdog:before{content:"\f80f"}.icon-hotel:before{content:"\f594"}.icon-hotjar:before{content:"\f3b1"}.icon-hourglass:before{content:"\f254"}.icon-hourglass-end:before{content:"\f253"}.icon-hourglass-half:before{content:"\f252"}.icon-hourglass-start:before{content:"\f251"}.icon-house-damage:before{content:"\f6f1"}.icon-house-user:before{content:"\e065"}.icon-houzz:before{content:"\f27c"}.icon-hryvnia:before{content:"\f6f2"}.icon-html5:before{content:"\f13b"}.icon-hubspot:before{content:"\f3b2"}.icon-i-cursor:before{content:"\f246"}.icon-ice-cream:before{content:"\f810"}.icon-icicles:before{content:"\f7ad"}.icon-icons:before{content:"\f86d"}.icon-id-badge:before{content:"\f2c1"}.icon-id-card:before{content:"\f2c2"}.icon-id-card-alt:before{content:"\f47f"}.icon-ideal:before{content:"\e013"}.icon-igloo:before{content:"\f7ae"}.icon-image:before{content:"\f03e"}.icon-images:before{content:"\f302"}.icon-imdb:before{content:"\f2d8"}.icon-inbox:before{content:"\f01c"}.icon-indent:before{content:"\f03c"}.icon-industry:before{content:"\f275"}.icon-infinity:before{content:"\f534"}.icon-info:before{content:"\f129"}.icon-info-circle:before{content:"\f05a"}.icon-innosoft:before{content:"\e080"}.icon-instagram:before{content:"\f16d"}.icon-instagram-square:before{content:"\e055"}.icon-instalod:before{content:"\e081"}.icon-intercom:before{content:"\f7af"}.icon-internet-explorer:before{content:"\f26b"}.icon-invision:before{content:"\f7b0"}.icon-ioxhost:before{content:"\f208"}.icon-italic:before{content:"\f033"}.icon-itch-io:before{content:"\f83a"}.icon-itunes:before{content:"\f3b4"}.icon-itunes-note:before{content:"\f3b5"}.icon-java:before{content:"\f4e4"}.icon-jedi:before{content:"\f669"}.icon-jedi-order:before{content:"\f50e"}.icon-jenkins:before{content:"\f3b6"}.icon-jira:before{content:"\f7b1"}.icon-joget:before{content:"\f3b7"}.icon-joint:before{content:"\f595"}.icon-joomla:before{content:"\f1aa"}.icon-journal-whills:before{content:"\f66a"}.icon-js:before{content:"\f3b8"}.icon-js-square:before{content:"\f3b9"}.icon-jsfiddle:before{content:"\f1cc"}.icon-kaaba:before{content:"\f66b"}.icon-kaggle:before{content:"\f5fa"}.icon-key:before{content:"\f084"}.icon-keybase:before{content:"\f4f5"}.icon-keyboard:before{content:"\f11c"}.icon-keycdn:before{content:"\f3ba"}.icon-khanda:before{content:"\f66d"}.icon-kickstarter:before{content:"\f3bb"}.icon-kickstarter-k:before{content:"\f3bc"}.icon-kiss:before{content:"\f596"}.icon-kiss-beam:before{content:"\f597"}.icon-kiss-wink-heart:before{content:"\f598"}.icon-kiwi-bird:before{content:"\f535"}.icon-korvue:before{content:"\f42f"}.icon-landmark:before{content:"\f66f"}.icon-language:before{content:"\f1ab"}.icon-laptop:before{content:"\f109"}.icon-laptop-code:before{content:"\f5fc"}.icon-laptop-house:before{content:"\e066"}.icon-laptop-medical:before{content:"\f812"}.icon-laravel:before{content:"\f3bd"}.icon-lastfm:before{content:"\f202"}.icon-lastfm-square:before{content:"\f203"}.icon-laugh:before{content:"\f599"}.icon-laugh-beam:before{content:"\f59a"}.icon-laugh-squint:before{content:"\f59b"}.icon-laugh-wink:before{content:"\f59c"}.icon-layer-group:before{content:"\f5fd"}.icon-leaf:before{content:"\f06c"}.icon-leanpub:before{content:"\f212"}.icon-lemon:before{content:"\f094"}.icon-less:before{content:"\f41d"}.icon-less-than:before{content:"\f536"}.icon-less-than-equal:before{content:"\f537"}.icon-level-down-alt:before{content:"\f3be"}.icon-level-up-alt:before{content:"\f3bf"}.icon-life-ring:before{content:"\f1cd"}.icon-lightbulb:before{content:"\f0eb"}.icon-line:before{content:"\f3c0"}.icon-link:before{content:"\f0c1"}.icon-linkedin:before{content:"\f08c"}.icon-linkedin-in:before{content:"\f0e1"}.icon-linode:before{content:"\f2b8"}.icon-linux:before{content:"\f17c"}.icon-lira-sign:before{content:"\f195"}.icon-list:before{content:"\f03a"}.icon-list-alt:before{content:"\f022"}.icon-list-ol:before{content:"\f0cb"}.icon-list-ul:before{content:"\f0ca"}.icon-location-arrow:before{content:"\f124"}.icon-lock:before{content:"\f023"}.icon-lock-open:before{content:"\f3c1"}.icon-long-arrow-alt-down:before{content:"\f309"}.icon-long-arrow-alt-left:before{content:"\f30a"}.icon-long-arrow-alt-right:before{content:"\f30b"}.icon-long-arrow-alt-up:before{content:"\f30c"}.icon-low-vision:before{content:"\f2a8"}.icon-luggage-cart:before{content:"\f59d"}.icon-lungs:before{content:"\f604"}.icon-lungs-virus:before{content:"\e067"}.icon-lyft:before{content:"\f3c3"}.icon-magento:before{content:"\f3c4"}.icon-magic:before{content:"\f0d0"}.icon-magnet:before{content:"\f076"}.icon-mail-bulk:before{content:"\f674"}.icon-mailchimp:before{content:"\f59e"}.icon-male:before{content:"\f183"}.icon-mandalorian:before{content:"\f50f"}.icon-map:before{content:"\f279"}.icon-map-marked:before{content:"\f59f"}.icon-map-marked-alt:before{content:"\f5a0"}.icon-map-marker:before{content:"\f041"}.icon-map-marker-alt:before{content:"\f3c5"}.icon-map-pin:before{content:"\f276"}.icon-map-signs:before{content:"\f277"}.icon-markdown:before{content:"\f60f"}.icon-marker:before{content:"\f5a1"}.icon-mars:before{content:"\f222"}.icon-mars-double:before{content:"\f227"}.icon-mars-stroke:before{content:"\f229"}.icon-mars-stroke-h:before{content:"\f22b"}.icon-mars-stroke-v:before{content:"\f22a"}.icon-mask:before{content:"\f6fa"}.icon-mastodon:before{content:"\f4f6"}.icon-maxcdn:before{content:"\f136"}.icon-mdb:before{content:"\f8ca"}.icon-medal:before{content:"\f5a2"}.icon-medapps:before{content:"\f3c6"}.icon-medium:before{content:"\f23a"}.icon-medium-m:before{content:"\f3c7"}.icon-medkit:before{content:"\f0fa"}.icon-medrt:before{content:"\f3c8"}.icon-meetup:before{content:"\f2e0"}.icon-megaport:before{content:"\f5a3"}.icon-meh:before{content:"\f11a"}.icon-meh-blank:before{content:"\f5a4"}.icon-meh-rolling-eyes:before{content:"\f5a5"}.icon-memory:before{content:"\f538"}.icon-mendeley:before{content:"\f7b3"}.icon-menorah:before{content:"\f676"}.icon-mercury:before{content:"\f223"}.icon-meteor:before{content:"\f753"}.icon-microblog:before{content:"\e01a"}.icon-microchip:before{content:"\f2db"}.icon-microphone:before{content:"\f130"}.icon-microphone-alt:before{content:"\f3c9"}.icon-microphone-alt-slash:before{content:"\f539"}.icon-microphone-slash:before{content:"\f131"}.icon-microscope:before{content:"\f610"}.icon-microsoft:before{content:"\f3ca"}.icon-minus:before{content:"\f068"}.icon-minus-circle:before{content:"\f056"}.icon-minus-square:before{content:"\f146"}.icon-mitten:before{content:"\f7b5"}.icon-mix:before{content:"\f3cb"}.icon-mixcloud:before{content:"\f289"}.icon-mixer:before{content:"\e056"}.icon-mizuni:before{content:"\f3cc"}.icon-mobile:before{content:"\f10b"}.icon-mobile-alt:before{content:"\f3cd"}.icon-modx:before{content:"\f285"}.icon-monero:before{content:"\f3d0"}.icon-money-bill:before{content:"\f0d6"}.icon-money-bill-alt:before{content:"\f3d1"}.icon-money-bill-wave:before{content:"\f53a"}.icon-money-bill-wave-alt:before{content:"\f53b"}.icon-money-check:before{content:"\f53c"}.icon-money-check-alt:before{content:"\f53d"}.icon-monument:before{content:"\f5a6"}.icon-moon:before{content:"\f186"}.icon-mortar-pestle:before{content:"\f5a7"}.icon-mosque:before{content:"\f678"}.icon-motorcycle:before{content:"\f21c"}.icon-mountain:before{content:"\f6fc"}.icon-mouse:before{content:"\f8cc"}.icon-mouse-pointer:before{content:"\f245"}.icon-mug-hot:before{content:"\f7b6"}.icon-music:before{content:"\f001"}.icon-napster:before{content:"\f3d2"}.icon-neos:before{content:"\f612"}.icon-network-wired:before{content:"\f6ff"}.icon-neuter:before{content:"\f22c"}.icon-newspaper:before{content:"\f1ea"}.icon-nimblr:before{content:"\f5a8"}.icon-node:before{content:"\f419"}.icon-node-js:before{content:"\f3d3"}.icon-not-equal:before{content:"\f53e"}.icon-notes-medical:before{content:"\f481"}.icon-npm:before{content:"\f3d4"}.icon-ns8:before{content:"\f3d5"}.icon-nutritionix:before{content:"\f3d6"}.icon-object-group:before{content:"\f247"}.icon-object-ungroup:before{content:"\f248"}.icon-octopus-deploy:before{content:"\e082"}.icon-odnoklassniki:before{content:"\f263"}.icon-odnoklassniki-square:before{content:"\f264"}.icon-oil-can:before{content:"\f613"}.icon-old-republic:before{content:"\f510"}.icon-om:before{content:"\f679"}.icon-opencart:before{content:"\f23d"}.icon-openid:before{content:"\f19b"}.icon-opera:before{content:"\f26a"}.icon-optin-monster:before{content:"\f23c"}.icon-orcid:before{content:"\f8d2"}.icon-osi:before{content:"\f41a"}.icon-otter:before{content:"\f700"}.icon-outdent:before{content:"\f03b"}.icon-page4:before{content:"\f3d7"}.icon-pagelines:before{content:"\f18c"}.icon-pager:before{content:"\f815"}.icon-paint-brush:before{content:"\f1fc"}.icon-paint-roller:before{content:"\f5aa"}.icon-palette:before{content:"\f53f"}.icon-palfed:before{content:"\f3d8"}.icon-pallet:before{content:"\f482"}.icon-paper-plane:before{content:"\f1d8"}.icon-paperclip:before{content:"\f0c6"}.icon-parachute-box:before{content:"\f4cd"}.icon-paragraph:before{content:"\f1dd"}.icon-parking:before{content:"\f540"}.icon-passport:before{content:"\f5ab"}.icon-pastafarianism:before{content:"\f67b"}.icon-paste:before{content:"\f0ea"}.icon-patreon:before{content:"\f3d9"}.icon-pause:before{content:"\f04c"}.icon-pause-circle:before{content:"\f28b"}.icon-paw:before{content:"\f1b0"}.icon-paypal:before{content:"\f1ed"}.icon-peace:before{content:"\f67c"}.icon-pen:before{content:"\f304"}.icon-pen-alt:before{content:"\f305"}.icon-pen-fancy:before{content:"\f5ac"}.icon-pen-nib:before{content:"\f5ad"}.icon-pen-square:before{content:"\f14b"}.icon-pencil-alt:before{content:"\f303"}.icon-pencil-ruler:before{content:"\f5ae"}.icon-penny-arcade:before{content:"\f704"}.icon-people-arrows:before{content:"\e068"}.icon-people-carry:before{content:"\f4ce"}.icon-pepper-hot:before{content:"\f816"}.icon-perbyte:before{content:"\e083"}.icon-percent:before{content:"\f295"}.icon-percentage:before{content:"\f541"}.icon-periscope:before{content:"\f3da"}.icon-person-booth:before{content:"\f756"}.icon-phabricator:before{content:"\f3db"}.icon-phoenix-framework:before{content:"\f3dc"}.icon-phoenix-squadron:before{content:"\f511"}.icon-phone:before{content:"\f095"}.icon-phone-alt:before{content:"\f879"}.icon-phone-slash:before{content:"\f3dd"}.icon-phone-square:before{content:"\f098"}.icon-phone-square-alt:before{content:"\f87b"}.icon-phone-volume:before{content:"\f2a0"}.icon-photo-video:before{content:"\f87c"}.icon-php:before{content:"\f457"}.icon-pied-piper:before{content:"\f2ae"}.icon-pied-piper-alt:before{content:"\f1a8"}.icon-pied-piper-hat:before{content:"\f4e5"}.icon-pied-piper-pp:before{content:"\f1a7"}.icon-pied-piper-square:before{content:"\e01e"}.icon-piggy-bank:before{content:"\f4d3"}.icon-pills:before{content:"\f484"}.icon-pinterest:before{content:"\f0d2"}.icon-pinterest-p:before{content:"\f231"}.icon-pinterest-square:before{content:"\f0d3"}.icon-pizza-slice:before{content:"\f818"}.icon-place-of-worship:before{content:"\f67f"}.icon-plane:before{content:"\f072"}.icon-plane-arrival:before{content:"\f5af"}.icon-plane-departure:before{content:"\f5b0"}.icon-plane-slash:before{content:"\e069"}.icon-play:before{content:"\f04b"}.icon-play-circle:before{content:"\f144"}.icon-playstation:before{content:"\f3df"}.icon-plug:before{content:"\f1e6"}.icon-plus:before{content:"\f067"}.icon-plus-circle:before{content:"\f055"}.icon-plus-square:before{content:"\f0fe"}.icon-podcast:before{content:"\f2ce"}.icon-poll:before{content:"\f681"}.icon-poll-h:before{content:"\f682"}.icon-poo:before{content:"\f2fe"}.icon-poo-storm:before{content:"\f75a"}.icon-poop:before{content:"\f619"}.icon-portrait:before{content:"\f3e0"}.icon-pound-sign:before{content:"\f154"}.icon-power-off:before{content:"\f011"}.icon-pray:before{content:"\f683"}.icon-praying-hands:before{content:"\f684"}.icon-prescription:before{content:"\f5b1"}.icon-prescription-bottle:before{content:"\f485"}.icon-prescription-bottle-alt:before{content:"\f486"}.icon-print:before{content:"\f02f"}.icon-procedures:before{content:"\f487"}.icon-product-hunt:before{content:"\f288"}.icon-project-diagram:before{content:"\f542"}.icon-pump-medical:before{content:"\e06a"}.icon-pump-soap:before{content:"\e06b"}.icon-pushed:before{content:"\f3e1"}.icon-puzzle-piece:before{content:"\f12e"}.icon-python:before{content:"\f3e2"}.icon-qq:before{content:"\f1d6"}.icon-qrcode:before{content:"\f029"}.icon-question:before{content:"\f128"}.icon-question-circle:before{content:"\f059"}.icon-quidditch:before{content:"\f458"}.icon-quinscape:before{content:"\f459"}.icon-quora:before{content:"\f2c4"}.icon-quote-left:before{content:"\f10d"}.icon-quote-right:before{content:"\f10e"}.icon-quran:before{content:"\f687"}.icon-r-project:before{content:"\f4f7"}.icon-radiation:before{content:"\f7b9"}.icon-radiation-alt:before{content:"\f7ba"}.icon-rainbow:before{content:"\f75b"}.icon-random:before{content:"\f074"}.icon-raspberry-pi:before{content:"\f7bb"}.icon-ravelry:before{content:"\f2d9"}.icon-react:before{content:"\f41b"}.icon-reacteurope:before{content:"\f75d"}.icon-readme:before{content:"\f4d5"}.icon-rebel:before{content:"\f1d0"}.icon-receipt:before{content:"\f543"}.icon-record-vinyl:before{content:"\f8d9"}.icon-recycle:before{content:"\f1b8"}.icon-red-river:before{content:"\f3e3"}.icon-reddit:before{content:"\f1a1"}.icon-reddit-alien:before{content:"\f281"}.icon-reddit-square:before{content:"\f1a2"}.icon-redhat:before{content:"\f7bc"}.icon-redo:before{content:"\f01e"}.icon-redo-alt:before{content:"\f2f9"}.icon-registered:before{content:"\f25d"}.icon-remove-format:before{content:"\f87d"}.icon-renren:before{content:"\f18b"}.icon-reply:before{content:"\f3e5"}.icon-reply-all:before{content:"\f122"}.icon-replyd:before{content:"\f3e6"}.icon-republican:before{content:"\f75e"}.icon-researchgate:before{content:"\f4f8"}.icon-resolving:before{content:"\f3e7"}.icon-restroom:before{content:"\f7bd"}.icon-retweet:before{content:"\f079"}.icon-rev:before{content:"\f5b2"}.icon-ribbon:before{content:"\f4d6"}.icon-ring:before{content:"\f70b"}.icon-road:before{content:"\f018"}.icon-robot:before{content:"\f544"}.icon-rocket:before{content:"\f135"}.icon-rocketchat:before{content:"\f3e8"}.icon-rockrms:before{content:"\f3e9"}.icon-route:before{content:"\f4d7"}.icon-rss:before{content:"\f09e"}.icon-rss-square:before{content:"\f143"}.icon-ruble-sign:before{content:"\f158"}.icon-ruler:before{content:"\f545"}.icon-ruler-combined:before{content:"\f546"}.icon-ruler-horizontal:before{content:"\f547"}.icon-ruler-vertical:before{content:"\f548"}.icon-running:before{content:"\f70c"}.icon-rupee-sign:before{content:"\f156"}.icon-rust:before{content:"\e07a"}.icon-sad-cry:before{content:"\f5b3"}.icon-sad-tear:before{content:"\f5b4"}.icon-safari:before{content:"\f267"}.icon-salesforce:before{content:"\f83b"}.icon-sass:before{content:"\f41e"}.icon-satellite:before{content:"\f7bf"}.icon-satellite-dish:before{content:"\f7c0"}.icon-save:before{content:"\f0c7"}.icon-schlix:before{content:"\f3ea"}.icon-school:before{content:"\f549"}.icon-screwdriver:before{content:"\f54a"}.icon-scribd:before{content:"\f28a"}.icon-scroll:before{content:"\f70e"}.icon-sd-card:before{content:"\f7c2"}.icon-search:before{content:"\f002"}.icon-search-dollar:before{content:"\f688"}.icon-search-location:before{content:"\f689"}.icon-search-minus:before{content:"\f010"}.icon-search-plus:before{content:"\f00e"}.icon-searchengin:before{content:"\f3eb"}.icon-seedling:before{content:"\f4d8"}.icon-sellcast:before{content:"\f2da"}.icon-sellsy:before{content:"\f213"}.icon-server:before{content:"\f233"}.icon-servicestack:before{content:"\f3ec"}.icon-shapes:before{content:"\f61f"}.icon-share:before{content:"\f064"}.icon-share-alt:before{content:"\f1e0"}.icon-share-alt-square:before{content:"\f1e1"}.icon-share-square:before{content:"\f14d"}.icon-shekel-sign:before{content:"\f20b"}.icon-shield-alt:before{content:"\f3ed"}.icon-shield-virus:before{content:"\e06c"}.icon-ship:before{content:"\f21a"}.icon-shipping-fast:before{content:"\f48b"}.icon-shirtsinbulk:before{content:"\f214"}.icon-shoe-prints:before{content:"\f54b"}.icon-shopify:before{content:"\e057"}.icon-shopping-bag:before{content:"\f290"}.icon-shopping-basket:before{content:"\f291"}.icon-shopping-cart:before{content:"\f07a"}.icon-shopware:before{content:"\f5b5"}.icon-shower:before{content:"\f2cc"}.icon-shuttle-van:before{content:"\f5b6"}.icon-sign:before{content:"\f4d9"}.icon-sign-in-alt:before{content:"\f2f6"}.icon-sign-language:before{content:"\f2a7"}.icon-sign-out-alt:before{content:"\f2f5"}.icon-signal:before{content:"\f012"}.icon-signature:before{content:"\f5b7"}.icon-sim-card:before{content:"\f7c4"}.icon-simplybuilt:before{content:"\f215"}.icon-sink:before{content:"\e06d"}.icon-sistrix:before{content:"\f3ee"}.icon-sitemap:before{content:"\f0e8"}.icon-sith:before{content:"\f512"}.icon-skating:before{content:"\f7c5"}.icon-sketch:before{content:"\f7c6"}.icon-skiing:before{content:"\f7c9"}.icon-skiing-nordic:before{content:"\f7ca"}.icon-skull:before{content:"\f54c"}.icon-skull-crossbones:before{content:"\f714"}.icon-skyatlas:before{content:"\f216"}.icon-skype:before{content:"\f17e"}.icon-slack:before{content:"\f198"}.icon-slack-hash:before{content:"\f3ef"}.icon-slash:before{content:"\f715"}.icon-sleigh:before{content:"\f7cc"}.icon-sliders-h:before{content:"\f1de"}.icon-slideshare:before{content:"\f1e7"}.icon-smile:before{content:"\f118"}.icon-smile-beam:before{content:"\f5b8"}.icon-smile-wink:before{content:"\f4da"}.icon-smog:before{content:"\f75f"}.icon-smoking:before{content:"\f48d"}.icon-smoking-ban:before{content:"\f54d"}.icon-sms:before{content:"\f7cd"}.icon-snapchat:before{content:"\f2ab"}.icon-snapchat-ghost:before{content:"\f2ac"}.icon-snapchat-square:before{content:"\f2ad"}.icon-snowboarding:before{content:"\f7ce"}.icon-snowflake:before{content:"\f2dc"}.icon-snowman:before{content:"\f7d0"}.icon-snowplow:before{content:"\f7d2"}.icon-soap:before{content:"\e06e"}.icon-socks:before{content:"\f696"}.icon-solar-panel:before{content:"\f5ba"}.icon-sort:before{content:"\f0dc"}.icon-sort-alpha-down:before{content:"\f15d"}.icon-sort-alpha-down-alt:before{content:"\f881"}.icon-sort-alpha-up:before{content:"\f15e"}.icon-sort-alpha-up-alt:before{content:"\f882"}.icon-sort-amount-down:before{content:"\f160"}.icon-sort-amount-down-alt:before{content:"\f884"}.icon-sort-amount-up:before{content:"\f161"}.icon-sort-amount-up-alt:before{content:"\f885"}.icon-sort-down:before{content:"\f0dd"}.icon-sort-numeric-down:before{content:"\f162"}.icon-sort-numeric-down-alt:before{content:"\f886"}.icon-sort-numeric-up:before{content:"\f163"}.icon-sort-numeric-up-alt:before{content:"\f887"}.icon-sort-up:before{content:"\f0de"}.icon-soundcloud:before{content:"\f1be"}.icon-sourcetree:before{content:"\f7d3"}.icon-spa:before{content:"\f5bb"}.icon-space-shuttle:before{content:"\f197"}.icon-speakap:before{content:"\f3f3"}.icon-speaker-deck:before{content:"\f83c"}.icon-spell-check:before{content:"\f891"}.icon-spider:before{content:"\f717"}.icon-spinner:before{content:"\f110"}.icon-splotch:before{content:"\f5bc"}.icon-spotify:before{content:"\f1bc"}.icon-spray-can:before{content:"\f5bd"}.icon-square:before{content:"\f0c8"}.icon-square-full:before{content:"\f45c"}.icon-square-root-alt:before{content:"\f698"}.icon-squarespace:before{content:"\f5be"}.icon-stack-exchange:before{content:"\f18d"}.icon-stack-overflow:before{content:"\f16c"}.icon-stackpath:before{content:"\f842"}.icon-stamp:before{content:"\f5bf"}.icon-star:before{content:"\f005"}.icon-star-and-crescent:before{content:"\f699"}.icon-star-half:before{content:"\f089"}.icon-star-half-alt:before{content:"\f5c0"}.icon-star-of-david:before{content:"\f69a"}.icon-star-of-life:before{content:"\f621"}.icon-staylinked:before{content:"\f3f5"}.icon-steam:before{content:"\f1b6"}.icon-steam-square:before{content:"\f1b7"}.icon-steam-symbol:before{content:"\f3f6"}.icon-step-backward:before{content:"\f048"}.icon-step-forward:before{content:"\f051"}.icon-stethoscope:before{content:"\f0f1"}.icon-sticker-mule:before{content:"\f3f7"}.icon-sticky-note:before{content:"\f249"}.icon-stop:before{content:"\f04d"}.icon-stop-circle:before{content:"\f28d"}.icon-stopwatch:before{content:"\f2f2"}.icon-stopwatch-20:before{content:"\e06f"}.icon-store:before{content:"\f54e"}.icon-store-alt:before{content:"\f54f"}.icon-store-alt-slash:before{content:"\e070"}.icon-store-slash:before{content:"\e071"}.icon-strava:before{content:"\f428"}.icon-stream:before{content:"\f550"}.icon-street-view:before{content:"\f21d"}.icon-strikethrough:before{content:"\f0cc"}.icon-stripe:before{content:"\f429"}.icon-stripe-s:before{content:"\f42a"}.icon-stroopwafel:before{content:"\f551"}.icon-studiovinari:before{content:"\f3f8"}.icon-stumbleupon:before{content:"\f1a4"}.icon-stumbleupon-circle:before{content:"\f1a3"}.icon-subscript:before{content:"\f12c"}.icon-subway:before{content:"\f239"}.icon-suitcase:before{content:"\f0f2"}.icon-suitcase-rolling:before{content:"\f5c1"}.icon-sun:before{content:"\f185"}.icon-superpowers:before{content:"\f2dd"}.icon-superscript:before{content:"\f12b"}.icon-supple:before{content:"\f3f9"}.icon-surprise:before{content:"\f5c2"}.icon-suse:before{content:"\f7d6"}.icon-swatchbook:before{content:"\f5c3"}.icon-swift:before{content:"\f8e1"}.icon-swimmer:before{content:"\f5c4"}.icon-swimming-pool:before{content:"\f5c5"}.icon-symfony:before{content:"\f83d"}.icon-synagogue:before{content:"\f69b"}.icon-sync:before{content:"\f021"}.icon-sync-alt:before{content:"\f2f1"}.icon-syringe:before{content:"\f48e"}.icon-table:before{content:"\f0ce"}.icon-table-tennis:before{content:"\f45d"}.icon-tablet:before{content:"\f10a"}.icon-tablet-alt:before{content:"\f3fa"}.icon-tablets:before{content:"\f490"}.icon-tachometer-alt:before{content:"\f3fd"}.icon-tag:before{content:"\f02b"}.icon-tags:before{content:"\f02c"}.icon-tape:before{content:"\f4db"}.icon-tasks:before{content:"\f0ae"}.icon-taxi:before{content:"\f1ba"}.icon-teamspeak:before{content:"\f4f9"}.icon-teeth:before{content:"\f62e"}.icon-teeth-open:before{content:"\f62f"}.icon-telegram:before{content:"\f2c6"}.icon-telegram-plane:before{content:"\f3fe"}.icon-temperature-high:before{content:"\f769"}.icon-temperature-low:before{content:"\f76b"}.icon-tencent-weibo:before{content:"\f1d5"}.icon-tenge:before{content:"\f7d7"}.icon-terminal:before{content:"\f120"}.icon-text-height:before{content:"\f034"}.icon-text-width:before{content:"\f035"}.icon-th:before{content:"\f00a"}.icon-th-large:before{content:"\f009"}.icon-th-list:before{content:"\f00b"}.icon-the-red-yeti:before{content:"\f69d"}.icon-theater-masks:before{content:"\f630"}.icon-themeco:before{content:"\f5c6"}.icon-themeisle:before{content:"\f2b2"}.icon-thermometer:before{content:"\f491"}.icon-thermometer-empty:before{content:"\f2cb"}.icon-thermometer-full:before{content:"\f2c7"}.icon-thermometer-half:before{content:"\f2c9"}.icon-thermometer-quarter:before{content:"\f2ca"}.icon-thermometer-three-quarters:before{content:"\f2c8"}.icon-think-peaks:before{content:"\f731"}.icon-thumbs-down:before{content:"\f165"}.icon-thumbs-up:before{content:"\f164"}.icon-thumbtack:before{content:"\f08d"}.icon-ticket-alt:before{content:"\f3ff"}.icon-tiktok:before{content:"\e07b"}.icon-times:before{content:"\f00d"}.icon-times-circle:before{content:"\f057"}.icon-tint:before{content:"\f043"}.icon-tint-slash:before{content:"\f5c7"}.icon-tired:before{content:"\f5c8"}.icon-toggle-off:before{content:"\f204"}.icon-toggle-on:before{content:"\f205"}.icon-toilet:before{content:"\f7d8"}.icon-toilet-paper:before{content:"\f71e"}.icon-toilet-paper-slash:before{content:"\e072"}.icon-toolbox:before{content:"\f552"}.icon-tools:before{content:"\f7d9"}.icon-tooth:before{content:"\f5c9"}.icon-torah:before{content:"\f6a0"}.icon-torii-gate:before{content:"\f6a1"}.icon-tractor:before{content:"\f722"}.icon-trade-federation:before{content:"\f513"}.icon-trademark:before{content:"\f25c"}.icon-traffic-light:before{content:"\f637"}.icon-trailer:before{content:"\e041"}.icon-train:before{content:"\f238"}.icon-tram:before{content:"\f7da"}.icon-transgender:before{content:"\f224"}.icon-transgender-alt:before{content:"\f225"}.icon-trash:before{content:"\f1f8"}.icon-trash-alt:before{content:"\f2ed"}.icon-trash-restore:before{content:"\f829"}.icon-trash-restore-alt:before{content:"\f82a"}.icon-tree:before{content:"\f1bb"}.icon-trello:before{content:"\f181"}.icon-trophy:before{content:"\f091"}.icon-truck:before{content:"\f0d1"}.icon-truck-loading:before{content:"\f4de"}.icon-truck-monster:before{content:"\f63b"}.icon-truck-moving:before{content:"\f4df"}.icon-truck-pickup:before{content:"\f63c"}.icon-tshirt:before{content:"\f553"}.icon-tty:before{content:"\f1e4"}.icon-tumblr:before{content:"\f173"}.icon-tumblr-square:before{content:"\f174"}.icon-tv:before{content:"\f26c"}.icon-twitch:before{content:"\f1e8"}.icon-twitter:before{content:"\f099"}.icon-twitter-square:before{content:"\f081"}.icon-typo3:before{content:"\f42b"}.icon-uber:before{content:"\f402"}.icon-ubuntu:before{content:"\f7df"}.icon-uikit:before{content:"\f403"}.icon-umbraco:before{content:"\f8e8"}.icon-umbrella:before{content:"\f0e9"}.icon-umbrella-beach:before{content:"\f5ca"}.icon-uncharted:before{content:"\e084"}.icon-underline:before{content:"\f0cd"}.icon-undo:before{content:"\f0e2"}.icon-undo-alt:before{content:"\f2ea"}.icon-uniregistry:before{content:"\f404"}.icon-unity:before{content:"\e049"}.icon-universal-access:before{content:"\f29a"}.icon-university:before{content:"\f19c"}.icon-unlink:before{content:"\f127"}.icon-unlock:before{content:"\f09c"}.icon-unlock-alt:before{content:"\f13e"}.icon-unsplash:before{content:"\e07c"}.icon-untappd:before{content:"\f405"}.icon-upload:before{content:"\f093"}.icon-ups:before{content:"\f7e0"}.icon-usb:before{content:"\f287"}.icon-user:before{content:"\f007"}.icon-user-alt:before{content:"\f406"}.icon-user-alt-slash:before{content:"\f4fa"}.icon-user-astronaut:before{content:"\f4fb"}.icon-user-check:before{content:"\f4fc"}.icon-user-circle:before{content:"\f2bd"}.icon-user-clock:before{content:"\f4fd"}.icon-user-cog:before{content:"\f4fe"}.icon-user-edit:before{content:"\f4ff"}.icon-user-friends:before{content:"\f500"}.icon-user-graduate:before{content:"\f501"}.icon-user-injured:before{content:"\f728"}.icon-user-lock:before{content:"\f502"}.icon-user-md:before{content:"\f0f0"}.icon-user-minus:before{content:"\f503"}.icon-user-ninja:before{content:"\f504"}.icon-user-nurse:before{content:"\f82f"}.icon-user-plus:before{content:"\f234"}.icon-user-secret:before{content:"\f21b"}.icon-user-shield:before{content:"\f505"}.icon-user-slash:before{content:"\f506"}.icon-user-tag:before{content:"\f507"}.icon-user-tie:before{content:"\f508"}.icon-user-times:before{content:"\f235"}.icon-users:before{content:"\f0c0"}.icon-users-cog:before{content:"\f509"}.icon-users-slash:before{content:"\e073"}.icon-usps:before{content:"\f7e1"}.icon-ussunnah:before{content:"\f407"}.icon-utensil-spoon:before{content:"\f2e5"}.icon-utensils:before{content:"\f2e7"}.icon-vaadin:before{content:"\f408"}.icon-vector-square:before{content:"\f5cb"}.icon-venus:before{content:"\f221"}.icon-venus-double:before{content:"\f226"}.icon-venus-mars:before{content:"\f228"}.icon-vest:before{content:"\e085"}.icon-vest-patches:before{content:"\e086"}.icon-viacoin:before{content:"\f237"}.icon-viadeo:before{content:"\f2a9"}.icon-viadeo-square:before{content:"\f2aa"}.icon-vial:before{content:"\f492"}.icon-vials:before{content:"\f493"}.icon-viber:before{content:"\f409"}.icon-video:before{content:"\f03d"}.icon-video-slash:before{content:"\f4e2"}.icon-vihara:before{content:"\f6a7"}.icon-vimeo:before{content:"\f40a"}.icon-vimeo-square:before{content:"\f194"}.icon-vimeo-v:before{content:"\f27d"}.icon-vine:before{content:"\f1ca"}.icon-virus:before{content:"\e074"}.icon-virus-slash:before{content:"\e075"}.icon-viruses:before{content:"\e076"}.icon-vk:before{content:"\f189"}.icon-vnv:before{content:"\f40b"}.icon-voicemail:before{content:"\f897"}.icon-volleyball-ball:before{content:"\f45f"}.icon-volume-down:before{content:"\f027"}.icon-volume-mute:before{content:"\f6a9"}.icon-volume-off:before{content:"\f026"}.icon-volume-up:before{content:"\f028"}.icon-vote-yea:before{content:"\f772"}.icon-vr-cardboard:before{content:"\f729"}.icon-vuejs:before{content:"\f41f"}.icon-walking:before{content:"\f554"}.icon-wallet:before{content:"\f555"}.icon-warehouse:before{content:"\f494"}.icon-watchman-monitoring:before{content:"\e087"}.icon-water:before{content:"\f773"}.icon-wave-square:before{content:"\f83e"}.icon-waze:before{content:"\f83f"}.icon-weebly:before{content:"\f5cc"}.icon-weibo:before{content:"\f18a"}.icon-weight:before{content:"\f496"}.icon-weight-hanging:before{content:"\f5cd"}.icon-weixin:before{content:"\f1d7"}.icon-whatsapp:before{content:"\f232"}.icon-whatsapp-square:before{content:"\f40c"}.icon-wheelchair:before{content:"\f193"}.icon-whmcs:before{content:"\f40d"}.icon-wifi:before{content:"\f1eb"}.icon-wikipedia-w:before{content:"\f266"}.icon-wind:before{content:"\f72e"}.icon-window-close:before{content:"\f410"}.icon-window-maximize:before{content:"\f2d0"}.icon-window-minimize:before{content:"\f2d1"}.icon-window-restore:before{content:"\f2d2"}.icon-windows:before{content:"\f17a"}.icon-wine-bottle:before{content:"\f72f"}.icon-wine-glass:before{content:"\f4e3"}.icon-wine-glass-alt:before{content:"\f5ce"}.icon-wix:before{content:"\f5cf"}.icon-wizards-of-the-coast:before{content:"\f730"}.icon-wodu:before{content:"\e088"}.icon-wolf-pack-battalion:before{content:"\f514"}.icon-won-sign:before{content:"\f159"}.icon-wordpress:before{content:"\f19a"}.icon-wordpress-simple:before{content:"\f411"}.icon-wpbeginner:before{content:"\f297"}.icon-wpexplorer:before{content:"\f2de"}.icon-wpforms:before{content:"\f298"}.icon-wpressr:before{content:"\f3e4"}.icon-wrench:before{content:"\f0ad"}.icon-x-ray:before{content:"\f497"}.icon-xbox:before{content:"\f412"}.icon-xing:before{content:"\f168"}.icon-xing-square:before{content:"\f169"}.icon-y-combinator:before{content:"\f23b"}.icon-yahoo:before{content:"\f19e"}.icon-yammer:before{content:"\f840"}.icon-yandex:before{content:"\f413"}.icon-yandex-international:before{content:"\f414"}.icon-yarn:before{content:"\f7e3"}.icon-yelp:before{content:"\f1e9"}.icon-yen-sign:before{content:"\f157"}.icon-yin-yang:before{content:"\f6ad"}.icon-yoast:before{content:"\f2b1"}.icon-youtube:before{content:"\f167"}.icon-youtube-square:before{content:"\f431"}.icon-zhihu:before{content:"\f63f"}.sr-only{clip:rect(0,0,0,0);border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto} /*! * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com diff --git a/setup/assets/css/installer.css b/setup/assets/css/installer.css index 67a69335412..276bef3d06b 100644 --- a/setup/assets/css/installer.css +++ b/setup/assets/css/installer.css @@ -24,12 +24,9 @@ * IE on Windows Phone and in iOS. */ html { - line-height: 1.15; - /* 1 */ - -ms-text-size-adjust: 100%; - /* 2 */ - -webkit-text-size-adjust: 100%; - /* 2 */ + line-height: 1.15; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ } /* Sections @@ -84,12 +81,9 @@ figure { * 2. Show the overflow in Edge and IE. */ hr { - box-sizing: content-box; - /* 1 */ - height: 0; - /* 1 */ - overflow: visible; - /* 2 */ + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ } /** @@ -104,10 +98,8 @@ main { * 2. Correct the odd `em` font sizing in all browsers. */ pre { - font-family: monospace, monospace; - /* 1 */ - font-size: 1em; - /* 2 */ + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ } /* Links @@ -117,10 +109,8 @@ pre { * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. */ a { - background-color: transparent; - /* 1 */ - -webkit-text-decoration-skip: objects; - /* 2 */ + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ } /* Text-level semantics @@ -130,12 +120,9 @@ a { * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. */ abbr[title] { - border-bottom: none; - /* 1 */ - text-decoration: underline; - /* 2 */ - text-decoration: underline dotted; - /* 2 */ + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ } /** @@ -161,10 +148,8 @@ strong { code, kbd, samp { - font-family: monospace, monospace; - /* 1 */ - font-size: 1em; - /* 2 */ + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ } /** @@ -252,14 +237,10 @@ input, optgroup, select, textarea { - font-family: sans-serif; - /* 1 */ - font-size: 100%; - /* 1 */ - line-height: 1.15; - /* 1 */ - margin: 0; - /* 2 */ + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ } /** @@ -274,8 +255,7 @@ button { * 1. Remove the inheritance of text transform in Firefox. */ button, -select { - /* 1 */ +select { /* 1 */ text-transform: none; } @@ -288,8 +268,7 @@ button, html [type=button], [type=reset], [type=submit] { - -webkit-appearance: button; - /* 2 */ + -webkit-appearance: button; /* 2 */ } button, @@ -330,10 +309,8 @@ input { */ [type=checkbox], [type=radio] { - box-sizing: border-box; - /* 1 */ - padding: 0; - /* 2 */ + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ } /** @@ -349,10 +326,8 @@ input { * 2. Correct the outline style in Safari. */ [type=search] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ /** * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. */ @@ -366,10 +341,8 @@ input { * 2. Change font properties to `inherit` in Safari. */ ::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ } /** @@ -386,18 +359,12 @@ fieldset { * `fieldset` elements in all browsers. */ legend { - box-sizing: border-box; - /* 1 */ - display: table; - /* 1 */ - max-width: 100%; - /* 1 */ - padding: 0; - /* 3 */ - color: inherit; - /* 2 */ - white-space: normal; - /* 1 */ + box-sizing: border-box; /* 1 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + color: inherit; /* 2 */ + white-space: normal; /* 1 */ } /** @@ -405,10 +372,8 @@ legend { * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. */ progress { - display: inline-block; - /* 1 */ - vertical-align: baseline; - /* 2 */ + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ } /** @@ -13404,13 +13369,11 @@ footer { .modx-installer-steps li span.title { display: none; } - .button { font-size: 14px; padding: 15px 10px; width: 140px; } - #content .content-inside .wrapper { padding: 20px; } @@ -13423,20 +13386,17 @@ footer { #content .content-inside .wrapper .content_header_title { font-size: 24px; } - #cck-div, pre, .custom-select, #install p { font-size: 14px; } - #welcome input#config_key { font-size: 14px; margin-left: 0; margin-top: 5px; } - #install .labelHolder { flex-direction: column; } @@ -13456,15 +13416,12 @@ pre, #install h3 { text-align: center; } - small { font-size: 100%; } - #install ul.checklist { word-break: break-all; } - .setup_navbar.complete { display: flex !important; flex-direction: column; @@ -13472,7 +13429,6 @@ pre, .setup_navbar.complete span.cleanup { display: flex; } - .cleanup [type=checkbox]:not(:checked) + label:before, .cleanup [type=checkbox]:checked + label:before { top: 5px !important; @@ -13481,7 +13437,6 @@ pre, .cleanup [type=checkbox]:checked + label:after { top: 8px !important; } - footer { font-size: 12px; padding: 0;