From da9f948c4268ebc2df04e702d40e84c81b2e3503 Mon Sep 17 00:00:00 2001 From: Llewellyn van der Merwe Date: Thu, 2 Jun 2022 17:01:28 +0200 Subject: [PATCH] first commit --- LICENSE.txt | 340 ++ admin/access.xml | 83 + admin/assets/css/admin.css | 119 + admin/assets/css/dashboard.css | 172 + admin/assets/css/download.css | 22 + admin/assets/css/downloads.css | 22 + admin/assets/css/extension.css | 487 +++ admin/assets/css/extensions.css | 26 + admin/assets/css/import.css | 22 + admin/assets/css/index.html | 1 + admin/assets/css/key.css | 23 + admin/assets/css/keys.css | 22 + admin/assets/css/maintenance.css | 22 + admin/assets/css/package.css | 487 +++ admin/assets/css/packages.css | 75 + admin/assets/css/release.css | 50 + admin/assets/css/releases.css | 477 +++ admin/assets/css/statistics.css | 22 + admin/assets/css/subscription.css | 23 + admin/assets/css/subscriptions.css | 26 + admin/assets/images/icons/downloads.png | Bin 0 -> 3000 bytes admin/assets/images/icons/extensions.png | Bin 0 -> 3886 bytes .../assets/images/icons/extensions_catid.png | Bin 0 -> 966 bytes admin/assets/images/icons/import.png | Bin 0 -> 3671 bytes admin/assets/images/icons/index.html | 1 + admin/assets/images/icons/keys.png | Bin 0 -> 2874 bytes admin/assets/images/icons/maintenance.png | Bin 0 -> 5001 bytes admin/assets/images/icons/packages.png | Bin 0 -> 4791 bytes admin/assets/images/icons/statistics.png | Bin 0 -> 3347 bytes admin/assets/images/icons/subscriptions.png | Bin 0 -> 4372 bytes admin/assets/images/import.gif | Bin 0 -> 65516 bytes admin/assets/images/index.html | 1 + admin/assets/images/vdm-component.png | Bin 0 -> 14590 bytes admin/assets/index.html | 1 + admin/assets/js/admin.js | 20 + admin/assets/js/index.html | 1 + admin/assets/security/htaccess.txt | 26 + admin/assets/security/index.htm | 9 + admin/assets/security/index.html | 9 + admin/assets/security/index.php | 21 + admin/assets/security/web.config | 35 + admin/config.xml | 356 ++ admin/controller.php | 127 + admin/controllers/ajax.json.php | 119 + admin/controllers/download.php | 320 ++ admin/controllers/downloads.php | 81 + admin/controllers/extension.php | 787 ++++ admin/controllers/extensiondistributor.php | 33 + admin/controllers/extensions.php | 55 + admin/controllers/import.php | 42 + admin/controllers/index.html | 1 + admin/controllers/key.php | 320 ++ admin/controllers/keys.php | 55 + admin/controllers/maintenance.php | 187 + admin/controllers/package.php | 413 ++ admin/controllers/packages.php | 55 + admin/controllers/release.php | 429 ++ admin/controllers/releases.php | 55 + admin/controllers/statistics.php | 42 + admin/controllers/subscription.php | 320 ++ admin/controllers/subscriptions.php | 55 + admin/extensiondistributor.php | 51 + admin/helpers/extensiondistributor.php | 2352 +++++++++++ admin/helpers/headercheck.php | 80 + admin/helpers/html/batch_.php | 87 + admin/helpers/html/index.html | 1 + admin/helpers/index.html | 1 + admin/index.html | 1 + .../en-GB/en-GB.com_extensiondistributor.ini | 714 ++++ .../en-GB.com_extensiondistributor.sys.ini | 86 + admin/language/en-GB/index.html | 1 + admin/language/index.html | 1 + admin/layouts/batchselection.php | 57 + admin/layouts/download/details_left.php | 56 + admin/layouts/download/details_right.php | 51 + admin/layouts/download/index.html | 1 + admin/layouts/download/publishing.php | 54 + admin/layouts/download/publlshing.php | 54 + admin/layouts/extension/details_above.php | 54 + admin/layouts/extension/details_left.php | 54 + admin/layouts/extension/details_right.php | 59 + admin/layouts/extension/index.html | 1 + admin/layouts/extension/publishing.php | 55 + admin/layouts/extension/publlshing.php | 55 + .../layouts/extension/releases_fullwidth.php | 185 + admin/layouts/importfiles.php | 67 + admin/layouts/index.html | 1 + admin/layouts/key/details_left.php | 53 + admin/layouts/key/index.html | 1 + admin/layouts/key/publishing.php | 55 + admin/layouts/key/publlshing.php | 55 + admin/layouts/package/configuration_left.php | 56 + admin/layouts/package/configuration_right.php | 55 + admin/layouts/package/details_above.php | 54 + admin/layouts/package/details_left.php | 51 + admin/layouts/package/details_right.php | 63 + admin/layouts/package/files_left.php | 51 + admin/layouts/package/index.html | 1 + admin/layouts/package/metadata.php | 50 + admin/layouts/package/publishing.php | 61 + admin/layouts/package/releases_fullwidth.php | 185 + admin/layouts/package/script_fullwidth.php | 64 + admin/layouts/release/changelog_fullwidth.php | 53 + admin/layouts/release/details_above.php | 57 + admin/layouts/release/details_left.php | 54 + admin/layouts/release/details_right.php | 53 + admin/layouts/release/details_under.php | 53 + admin/layouts/release/index.html | 1 + admin/layouts/release/publishing.php | 55 + admin/layouts/release/publlshing.php | 55 + admin/layouts/statewidget.php | 84 + admin/layouts/subscription/details_left.php | 55 + admin/layouts/subscription/details_right.php | 51 + admin/layouts/subscription/index.html | 1 + admin/layouts/subscription/publishing.php | 54 + admin/layouts/subscription/publlshing.php | 55 + admin/models/ajax.php | 114 + admin/models/download.php | 856 ++++ admin/models/downloads.php | 484 +++ admin/models/extension.php | 1259 ++++++ admin/models/extensiondistributor.php | 247 ++ admin/models/extensions.php | 493 +++ .../fields/downloadsfiltertransaction.php | 79 + admin/models/fields/downloadsfilteruser.php | 75 + admin/models/fields/extension.php | 186 + admin/models/fields/extensionkey.php | 82 + .../models/fields/extensionsfilterclient.php | 80 + admin/models/fields/extensionsfiltergroup.php | 76 + admin/models/fields/extensionsfiltertype.php | 79 + admin/models/fields/fileselect.css | 433 ++ admin/models/fields/fileselect.php | 167 + admin/models/fields/index.html | 1 + admin/models/fields/keysfilteruser.php | 75 + admin/models/fields/packagekey.php | 186 + admin/models/fields/packagekeys.php | 83 + admin/models/fields/packagelog.php | 82 + admin/models/fields/packagesfilterfree.php | 80 + admin/models/fields/releasesfilterreltype.php | 79 + .../models/fields/releasesfilterstability.php | 79 + .../fields/subscriptionsfilteractive.php | 80 + .../models/fields/subscriptionsfilteruser.php | 75 + admin/models/forms/download.xml | 157 + admin/models/forms/extension.xml | 275 ++ admin/models/forms/filter_downloads.xml | 81 + admin/models/forms/filter_extensions.xml | 97 + admin/models/forms/filter_keys.xml | 64 + admin/models/forms/filter_packages.xml | 85 + admin/models/forms/filter_releases.xml | 98 + admin/models/forms/filter_subscriptions.xml | 85 + admin/models/forms/index.html | 1 + admin/models/forms/key.xml | 137 + admin/models/forms/package.xml | 649 +++ admin/models/forms/release.xml | 296 ++ admin/models/forms/subscription.xml | 166 + admin/models/import.php | 159 + admin/models/index.html | 1 + admin/models/key.php | 885 +++++ admin/models/keys.php | 304 ++ admin/models/maintenance.php | 280 ++ admin/models/package.php | 1301 ++++++ admin/models/packages.php | 425 ++ admin/models/release.php | 1060 +++++ admin/models/releases.php | 578 +++ admin/models/rules/code.php | 62 + admin/models/rules/index.html | 1 + admin/models/statistics.php | 159 + admin/models/subscription.php | 895 +++++ admin/models/subscriptions.php | 461 +++ admin/sql/index.html | 1 + admin/sql/install.mysql.utf8.sql | 243 ++ admin/sql/uninstall.mysql.utf8.sql | 7 + admin/sql/updates/index.html | 1 + admin/sql/updates/mysql/1.0.0.sql | 1 + admin/sql/updates/mysql/index.html | 1 + admin/tables/download.php | 330 ++ admin/tables/extension.php | 349 ++ admin/tables/index.html | 1 + admin/tables/key.php | 333 ++ admin/tables/package.php | 346 ++ admin/tables/release.php | 333 ++ admin/tables/subscription.php | 330 ++ admin/views/download/submitbutton.js | 34 + admin/views/download/tmpl/edit.php | 95 + admin/views/download/tmpl/index.html | 1 + admin/views/download/view.html.php | 201 + admin/views/downloads/index.html | 1 + admin/views/downloads/tmpl/default.php | 98 + .../downloads/tmpl/default_batch_body.php | 27 + .../downloads/tmpl/default_batch_footer.php | 32 + admin/views/downloads/tmpl/default_body.php | 92 + admin/views/downloads/tmpl/default_foot.php | 27 + admin/views/downloads/tmpl/default_head.php | 62 + .../views/downloads/tmpl/default_toolbar.php | 54 + admin/views/downloads/tmpl/index.html | 1 + admin/views/downloads/view.html.php | 290 ++ admin/views/extension/submitbutton.js | 34 + admin/views/extension/tmpl/edit.php | 159 + admin/views/extension/tmpl/index.html | 1 + admin/views/extension/view.html.php | 224 ++ admin/views/extensiondistributor/index.html | 1 + .../extensiondistributor/tmpl/default.php | 42 + .../tmpl/default_main.php | 39 + .../extensiondistributor/tmpl/default_vdm.php | 47 + .../extensiondistributor/tmpl/index.html | 1 + .../views/extensiondistributor/view.html.php | 99 + admin/views/extensions/index.html | 1 + admin/views/extensions/tmpl/default.php | 75 + .../extensions/tmpl/default_batch_body.php | 27 + .../extensions/tmpl/default_batch_footer.php | 32 + admin/views/extensions/tmpl/default_body.php | 173 + admin/views/extensions/tmpl/default_foot.php | 27 + admin/views/extensions/tmpl/default_head.php | 68 + .../views/extensions/tmpl/default_toolbar.php | 54 + admin/views/extensions/tmpl/index.html | 1 + admin/views/extensions/view.html.php | 293 ++ admin/views/import/index.html | 1 + admin/views/import/tmpl/default.php | 174 + admin/views/import/tmpl/index.html | 1 + admin/views/import/view.html.php | 143 + admin/views/index.html | 1 + admin/views/key/submitbutton.js | 34 + admin/views/key/tmpl/edit.php | 138 + admin/views/key/tmpl/index.html | 1 + admin/views/key/view.html.php | 206 + admin/views/keys/index.html | 1 + admin/views/keys/tmpl/default.php | 92 + admin/views/keys/tmpl/default_batch_body.php | 27 + .../views/keys/tmpl/default_batch_footer.php | 32 + admin/views/keys/tmpl/default_body.php | 106 + admin/views/keys/tmpl/default_foot.php | 27 + admin/views/keys/tmpl/default_head.php | 62 + admin/views/keys/tmpl/default_toolbar.php | 54 + admin/views/keys/tmpl/index.html | 1 + admin/views/keys/view.html.php | 239 ++ admin/views/maintenance/index.html | 1 + admin/views/maintenance/tmpl/default.php | 229 ++ admin/views/maintenance/tmpl/index.html | 1 + admin/views/maintenance/view.html.php | 141 + admin/views/package/submitbutton.js | 34 + admin/views/package/tmpl/edit.php | 327 ++ admin/views/package/tmpl/index.html | 1 + admin/views/package/view.html.php | 224 ++ admin/views/packages/index.html | 1 + admin/views/packages/tmpl/default.php | 99 + .../packages/tmpl/default_batch_body.php | 27 + .../packages/tmpl/default_batch_footer.php | 32 + admin/views/packages/tmpl/default_body.php | 254 ++ admin/views/packages/tmpl/default_foot.php | 27 + admin/views/packages/tmpl/default_head.php | 71 + admin/views/packages/tmpl/default_toolbar.php | 54 + admin/views/packages/tmpl/index.html | 1 + admin/views/packages/view.html.php | 260 ++ admin/views/release/submitbutton.js | 34 + admin/views/release/tmpl/edit.php | 211 + admin/views/release/tmpl/index.html | 1 + admin/views/release/view.html.php | 214 + admin/views/releases/index.html | 1 + admin/views/releases/tmpl/default.php | 77 + .../releases/tmpl/default_batch_body.php | 27 + .../releases/tmpl/default_batch_footer.php | 32 + admin/views/releases/tmpl/default_body.php | 146 + admin/views/releases/tmpl/default_foot.php | 27 + admin/views/releases/tmpl/default_head.php | 77 + admin/views/releases/tmpl/default_toolbar.php | 54 + admin/views/releases/tmpl/index.html | 1 + admin/views/releases/view.html.php | 304 ++ admin/views/statistics/index.html | 1 + admin/views/statistics/tmpl/default.php | 67 + admin/views/statistics/tmpl/index.html | 1 + admin/views/statistics/view.html.php | 137 + admin/views/subscription/submitbutton.js | 34 + admin/views/subscription/tmpl/edit.php | 122 + admin/views/subscription/tmpl/index.html | 1 + admin/views/subscription/view.html.php | 201 + admin/views/subscriptions/index.html | 1 + admin/views/subscriptions/tmpl/default.php | 93 + .../subscriptions/tmpl/default_batch_body.php | 27 + .../tmpl/default_batch_footer.php | 32 + .../views/subscriptions/tmpl/default_body.php | 117 + .../views/subscriptions/tmpl/default_foot.php | 27 + .../views/subscriptions/tmpl/default_head.php | 68 + .../subscriptions/tmpl/default_toolbar.php | 54 + admin/views/subscriptions/tmpl/index.html | 1 + admin/views/subscriptions/view.html.php | 282 ++ extensiondistributor.xml | 107 + index.html | 1 + media/css/index.html | 1 + media/footable-v3/LICENSE-GPLv3 | 14 + media/footable-v3/css/footable.bootstrap.css | 266 ++ .../css/footable.bootstrap.min.css | 1 + .../css/footable.core.bootstrap.css | 175 + .../css/footable.core.bootstrap.min.css | 1 + .../css/footable.core.standalone.css | 675 ++++ .../css/footable.core.standalone.min.css | 1 + media/footable-v3/css/footable.editing.css | 50 + .../footable-v3/css/footable.editing.min.css | 1 + media/footable-v3/css/footable.filtering.css | 55 + .../css/footable.filtering.min.css | 1 + media/footable-v3/css/footable.paging.css | 31 + media/footable-v3/css/footable.paging.min.css | 1 + media/footable-v3/css/footable.sorting.css | 46 + .../footable-v3/css/footable.sorting.min.css | 1 + media/footable-v3/css/footable.standalone.css | 727 ++++ .../css/footable.standalone.min.css | 1 + media/footable-v3/css/index.html | 1 + media/footable-v3/index.html | 1 + media/footable-v3/js/footable.core.js | 3507 +++++++++++++++++ media/footable-v3/js/footable.core.min.js | 9 + media/footable-v3/js/footable.editing.js | 802 ++++ media/footable-v3/js/footable.editing.min.js | 8 + media/footable-v3/js/footable.filtering.js | 1044 +++++ .../footable-v3/js/footable.filtering.min.js | 8 + media/footable-v3/js/footable.min.js | 9 + media/footable-v3/js/footable.paging.js | 761 ++++ media/footable-v3/js/footable.paging.min.js | 8 + media/footable-v3/js/footable.sorting.js | 490 +++ media/footable-v3/js/footable.sorting.min.js | 8 + media/footable-v3/js/footable.state.js | 362 ++ media/footable-v3/js/footable.state.min.js | 8 + media/footable-v3/js/index.html | 1 + media/images/index.html | 1 + media/images/spinner.svg | 1 + media/index.html | 1 + media/js/download.js | 20 + media/js/extension.js | 113 + media/js/index.html | 1 + media/js/key.js | 20 + media/js/package.js | 133 + media/js/release.js | 20 + media/js/subscription.js | 20 + media/uikit-v3/css/index.html | 1 + media/uikit-v3/css/uikit.min.css | 1 + media/uikit-v3/index.html | 1 + media/uikit-v3/js/index.html | 1 + media/uikit-v3/js/uikit-icons.min.js | 3 + media/uikit-v3/js/uikit.min.js | 3 + script.php | 1684 ++++++++ site/assets/css/index.html | 1 + site/assets/css/keys.css | 22 + site/assets/css/package.css | 22 + site/assets/css/site.css | 24 + site/assets/images/index.html | 1 + site/assets/index.html | 1 + site/assets/js/index.html | 1 + site/assets/js/site.js | 20 + site/controller.php | 132 + site/controllers/index.html | 1 + site/controllers/package.php | 198 + site/extensiondistributor.php | 42 + site/helpers/category.php | 37 + site/helpers/categoryextension.php | 42 + site/helpers/extensiondistributor.php | 2263 +++++++++++ site/helpers/headercheck.php | 80 + site/helpers/index.html | 1 + site/helpers/route.php | 352 ++ site/index.html | 1 + .../en-GB/en-GB.com_extensiondistributor.ini | 24 + .../en-GB.com_extensiondistributor.sys.ini | 3 + site/language/en-GB/index.html | 1 + .../fr-FR/fr-FR.com_extensiondistributor.ini | 16 + .../fr-FR.com_extensiondistributor.sys.ini | 2 + site/language/index.html | 1 + site/layouts/index.html | 1 + site/models/index.html | 1 + site/models/keys.php | 218 + site/models/package.php | 152 + site/router.php | 252 ++ site/views/index.html | 1 + site/views/keys/index.html | 1 + site/views/keys/tmpl/default.php | 42 + site/views/keys/tmpl/default.xml | 8 + site/views/keys/tmpl/index.html | 1 + site/views/keys/view.html.php | 108 + site/views/package/index.html | 1 + site/views/package/submitbutton.js | 34 + site/views/package/tmpl/default.php | 33 + site/views/package/tmpl/index.html | 1 + site/views/package/view.html.php | 102 + 378 files changed, 52240 insertions(+) create mode 100644 LICENSE.txt create mode 100644 admin/access.xml create mode 100644 admin/assets/css/admin.css create mode 100644 admin/assets/css/dashboard.css create mode 100644 admin/assets/css/download.css create mode 100644 admin/assets/css/downloads.css create mode 100644 admin/assets/css/extension.css create mode 100644 admin/assets/css/extensions.css create mode 100644 admin/assets/css/import.css create mode 100644 admin/assets/css/index.html create mode 100644 admin/assets/css/key.css create mode 100644 admin/assets/css/keys.css create mode 100644 admin/assets/css/maintenance.css create mode 100644 admin/assets/css/package.css create mode 100644 admin/assets/css/packages.css create mode 100644 admin/assets/css/release.css create mode 100644 admin/assets/css/releases.css create mode 100644 admin/assets/css/statistics.css create mode 100644 admin/assets/css/subscription.css create mode 100644 admin/assets/css/subscriptions.css create mode 100644 admin/assets/images/icons/downloads.png create mode 100644 admin/assets/images/icons/extensions.png create mode 100644 admin/assets/images/icons/extensions_catid.png create mode 100644 admin/assets/images/icons/import.png create mode 100644 admin/assets/images/icons/index.html create mode 100644 admin/assets/images/icons/keys.png create mode 100644 admin/assets/images/icons/maintenance.png create mode 100644 admin/assets/images/icons/packages.png create mode 100644 admin/assets/images/icons/statistics.png create mode 100644 admin/assets/images/icons/subscriptions.png create mode 100644 admin/assets/images/import.gif create mode 100644 admin/assets/images/index.html create mode 100644 admin/assets/images/vdm-component.png create mode 100644 admin/assets/index.html create mode 100644 admin/assets/js/admin.js create mode 100644 admin/assets/js/index.html create mode 100644 admin/assets/security/htaccess.txt create mode 100644 admin/assets/security/index.htm create mode 100644 admin/assets/security/index.html create mode 100644 admin/assets/security/index.php create mode 100644 admin/assets/security/web.config create mode 100644 admin/config.xml create mode 100644 admin/controller.php create mode 100644 admin/controllers/ajax.json.php create mode 100644 admin/controllers/download.php create mode 100644 admin/controllers/downloads.php create mode 100644 admin/controllers/extension.php create mode 100644 admin/controllers/extensiondistributor.php create mode 100644 admin/controllers/extensions.php create mode 100644 admin/controllers/import.php create mode 100644 admin/controllers/index.html create mode 100644 admin/controllers/key.php create mode 100644 admin/controllers/keys.php create mode 100644 admin/controllers/maintenance.php create mode 100644 admin/controllers/package.php create mode 100644 admin/controllers/packages.php create mode 100644 admin/controllers/release.php create mode 100644 admin/controllers/releases.php create mode 100644 admin/controllers/statistics.php create mode 100644 admin/controllers/subscription.php create mode 100644 admin/controllers/subscriptions.php create mode 100644 admin/extensiondistributor.php create mode 100644 admin/helpers/extensiondistributor.php create mode 100644 admin/helpers/headercheck.php create mode 100644 admin/helpers/html/batch_.php create mode 100644 admin/helpers/html/index.html create mode 100644 admin/helpers/index.html create mode 100644 admin/index.html create mode 100644 admin/language/en-GB/en-GB.com_extensiondistributor.ini create mode 100644 admin/language/en-GB/en-GB.com_extensiondistributor.sys.ini create mode 100644 admin/language/en-GB/index.html create mode 100644 admin/language/index.html create mode 100644 admin/layouts/batchselection.php create mode 100644 admin/layouts/download/details_left.php create mode 100644 admin/layouts/download/details_right.php create mode 100644 admin/layouts/download/index.html create mode 100644 admin/layouts/download/publishing.php create mode 100644 admin/layouts/download/publlshing.php create mode 100644 admin/layouts/extension/details_above.php create mode 100644 admin/layouts/extension/details_left.php create mode 100644 admin/layouts/extension/details_right.php create mode 100644 admin/layouts/extension/index.html create mode 100644 admin/layouts/extension/publishing.php create mode 100644 admin/layouts/extension/publlshing.php create mode 100644 admin/layouts/extension/releases_fullwidth.php create mode 100644 admin/layouts/importfiles.php create mode 100644 admin/layouts/index.html create mode 100644 admin/layouts/key/details_left.php create mode 100644 admin/layouts/key/index.html create mode 100644 admin/layouts/key/publishing.php create mode 100644 admin/layouts/key/publlshing.php create mode 100644 admin/layouts/package/configuration_left.php create mode 100644 admin/layouts/package/configuration_right.php create mode 100644 admin/layouts/package/details_above.php create mode 100644 admin/layouts/package/details_left.php create mode 100644 admin/layouts/package/details_right.php create mode 100644 admin/layouts/package/files_left.php create mode 100644 admin/layouts/package/index.html create mode 100644 admin/layouts/package/metadata.php create mode 100644 admin/layouts/package/publishing.php create mode 100644 admin/layouts/package/releases_fullwidth.php create mode 100644 admin/layouts/package/script_fullwidth.php create mode 100644 admin/layouts/release/changelog_fullwidth.php create mode 100644 admin/layouts/release/details_above.php create mode 100644 admin/layouts/release/details_left.php create mode 100644 admin/layouts/release/details_right.php create mode 100644 admin/layouts/release/details_under.php create mode 100644 admin/layouts/release/index.html create mode 100644 admin/layouts/release/publishing.php create mode 100644 admin/layouts/release/publlshing.php create mode 100644 admin/layouts/statewidget.php create mode 100644 admin/layouts/subscription/details_left.php create mode 100644 admin/layouts/subscription/details_right.php create mode 100644 admin/layouts/subscription/index.html create mode 100644 admin/layouts/subscription/publishing.php create mode 100644 admin/layouts/subscription/publlshing.php create mode 100644 admin/models/ajax.php create mode 100644 admin/models/download.php create mode 100644 admin/models/downloads.php create mode 100644 admin/models/extension.php create mode 100644 admin/models/extensiondistributor.php create mode 100644 admin/models/extensions.php create mode 100644 admin/models/fields/downloadsfiltertransaction.php create mode 100644 admin/models/fields/downloadsfilteruser.php create mode 100644 admin/models/fields/extension.php create mode 100644 admin/models/fields/extensionkey.php create mode 100644 admin/models/fields/extensionsfilterclient.php create mode 100644 admin/models/fields/extensionsfiltergroup.php create mode 100644 admin/models/fields/extensionsfiltertype.php create mode 100644 admin/models/fields/fileselect.css create mode 100644 admin/models/fields/fileselect.php create mode 100644 admin/models/fields/index.html create mode 100644 admin/models/fields/keysfilteruser.php create mode 100644 admin/models/fields/packagekey.php create mode 100644 admin/models/fields/packagekeys.php create mode 100644 admin/models/fields/packagelog.php create mode 100644 admin/models/fields/packagesfilterfree.php create mode 100644 admin/models/fields/releasesfilterreltype.php create mode 100644 admin/models/fields/releasesfilterstability.php create mode 100644 admin/models/fields/subscriptionsfilteractive.php create mode 100644 admin/models/fields/subscriptionsfilteruser.php create mode 100644 admin/models/forms/download.xml create mode 100644 admin/models/forms/extension.xml create mode 100644 admin/models/forms/filter_downloads.xml create mode 100644 admin/models/forms/filter_extensions.xml create mode 100644 admin/models/forms/filter_keys.xml create mode 100644 admin/models/forms/filter_packages.xml create mode 100644 admin/models/forms/filter_releases.xml create mode 100644 admin/models/forms/filter_subscriptions.xml create mode 100644 admin/models/forms/index.html create mode 100644 admin/models/forms/key.xml create mode 100644 admin/models/forms/package.xml create mode 100644 admin/models/forms/release.xml create mode 100644 admin/models/forms/subscription.xml create mode 100644 admin/models/import.php create mode 100644 admin/models/index.html create mode 100644 admin/models/key.php create mode 100644 admin/models/keys.php create mode 100644 admin/models/maintenance.php create mode 100644 admin/models/package.php create mode 100644 admin/models/packages.php create mode 100644 admin/models/release.php create mode 100644 admin/models/releases.php create mode 100644 admin/models/rules/code.php create mode 100644 admin/models/rules/index.html create mode 100644 admin/models/statistics.php create mode 100644 admin/models/subscription.php create mode 100644 admin/models/subscriptions.php create mode 100644 admin/sql/index.html create mode 100644 admin/sql/install.mysql.utf8.sql create mode 100644 admin/sql/uninstall.mysql.utf8.sql create mode 100644 admin/sql/updates/index.html create mode 100644 admin/sql/updates/mysql/1.0.0.sql create mode 100644 admin/sql/updates/mysql/index.html create mode 100644 admin/tables/download.php create mode 100644 admin/tables/extension.php create mode 100644 admin/tables/index.html create mode 100644 admin/tables/key.php create mode 100644 admin/tables/package.php create mode 100644 admin/tables/release.php create mode 100644 admin/tables/subscription.php create mode 100644 admin/views/download/submitbutton.js create mode 100644 admin/views/download/tmpl/edit.php create mode 100644 admin/views/download/tmpl/index.html create mode 100644 admin/views/download/view.html.php create mode 100644 admin/views/downloads/index.html create mode 100644 admin/views/downloads/tmpl/default.php create mode 100644 admin/views/downloads/tmpl/default_batch_body.php create mode 100644 admin/views/downloads/tmpl/default_batch_footer.php create mode 100644 admin/views/downloads/tmpl/default_body.php create mode 100644 admin/views/downloads/tmpl/default_foot.php create mode 100644 admin/views/downloads/tmpl/default_head.php create mode 100644 admin/views/downloads/tmpl/default_toolbar.php create mode 100644 admin/views/downloads/tmpl/index.html create mode 100644 admin/views/downloads/view.html.php create mode 100644 admin/views/extension/submitbutton.js create mode 100644 admin/views/extension/tmpl/edit.php create mode 100644 admin/views/extension/tmpl/index.html create mode 100644 admin/views/extension/view.html.php create mode 100644 admin/views/extensiondistributor/index.html create mode 100644 admin/views/extensiondistributor/tmpl/default.php create mode 100644 admin/views/extensiondistributor/tmpl/default_main.php create mode 100644 admin/views/extensiondistributor/tmpl/default_vdm.php create mode 100644 admin/views/extensiondistributor/tmpl/index.html create mode 100644 admin/views/extensiondistributor/view.html.php create mode 100644 admin/views/extensions/index.html create mode 100644 admin/views/extensions/tmpl/default.php create mode 100644 admin/views/extensions/tmpl/default_batch_body.php create mode 100644 admin/views/extensions/tmpl/default_batch_footer.php create mode 100644 admin/views/extensions/tmpl/default_body.php create mode 100644 admin/views/extensions/tmpl/default_foot.php create mode 100644 admin/views/extensions/tmpl/default_head.php create mode 100644 admin/views/extensions/tmpl/default_toolbar.php create mode 100644 admin/views/extensions/tmpl/index.html create mode 100644 admin/views/extensions/view.html.php create mode 100644 admin/views/import/index.html create mode 100644 admin/views/import/tmpl/default.php create mode 100644 admin/views/import/tmpl/index.html create mode 100644 admin/views/import/view.html.php create mode 100644 admin/views/index.html create mode 100644 admin/views/key/submitbutton.js create mode 100644 admin/views/key/tmpl/edit.php create mode 100644 admin/views/key/tmpl/index.html create mode 100644 admin/views/key/view.html.php create mode 100644 admin/views/keys/index.html create mode 100644 admin/views/keys/tmpl/default.php create mode 100644 admin/views/keys/tmpl/default_batch_body.php create mode 100644 admin/views/keys/tmpl/default_batch_footer.php create mode 100644 admin/views/keys/tmpl/default_body.php create mode 100644 admin/views/keys/tmpl/default_foot.php create mode 100644 admin/views/keys/tmpl/default_head.php create mode 100644 admin/views/keys/tmpl/default_toolbar.php create mode 100644 admin/views/keys/tmpl/index.html create mode 100644 admin/views/keys/view.html.php create mode 100644 admin/views/maintenance/index.html create mode 100644 admin/views/maintenance/tmpl/default.php create mode 100644 admin/views/maintenance/tmpl/index.html create mode 100644 admin/views/maintenance/view.html.php create mode 100644 admin/views/package/submitbutton.js create mode 100644 admin/views/package/tmpl/edit.php create mode 100644 admin/views/package/tmpl/index.html create mode 100644 admin/views/package/view.html.php create mode 100644 admin/views/packages/index.html create mode 100644 admin/views/packages/tmpl/default.php create mode 100644 admin/views/packages/tmpl/default_batch_body.php create mode 100644 admin/views/packages/tmpl/default_batch_footer.php create mode 100644 admin/views/packages/tmpl/default_body.php create mode 100644 admin/views/packages/tmpl/default_foot.php create mode 100644 admin/views/packages/tmpl/default_head.php create mode 100644 admin/views/packages/tmpl/default_toolbar.php create mode 100644 admin/views/packages/tmpl/index.html create mode 100644 admin/views/packages/view.html.php create mode 100644 admin/views/release/submitbutton.js create mode 100644 admin/views/release/tmpl/edit.php create mode 100644 admin/views/release/tmpl/index.html create mode 100644 admin/views/release/view.html.php create mode 100644 admin/views/releases/index.html create mode 100644 admin/views/releases/tmpl/default.php create mode 100644 admin/views/releases/tmpl/default_batch_body.php create mode 100644 admin/views/releases/tmpl/default_batch_footer.php create mode 100644 admin/views/releases/tmpl/default_body.php create mode 100644 admin/views/releases/tmpl/default_foot.php create mode 100644 admin/views/releases/tmpl/default_head.php create mode 100644 admin/views/releases/tmpl/default_toolbar.php create mode 100644 admin/views/releases/tmpl/index.html create mode 100644 admin/views/releases/view.html.php create mode 100644 admin/views/statistics/index.html create mode 100644 admin/views/statistics/tmpl/default.php create mode 100644 admin/views/statistics/tmpl/index.html create mode 100644 admin/views/statistics/view.html.php create mode 100644 admin/views/subscription/submitbutton.js create mode 100644 admin/views/subscription/tmpl/edit.php create mode 100644 admin/views/subscription/tmpl/index.html create mode 100644 admin/views/subscription/view.html.php create mode 100644 admin/views/subscriptions/index.html create mode 100644 admin/views/subscriptions/tmpl/default.php create mode 100644 admin/views/subscriptions/tmpl/default_batch_body.php create mode 100644 admin/views/subscriptions/tmpl/default_batch_footer.php create mode 100644 admin/views/subscriptions/tmpl/default_body.php create mode 100644 admin/views/subscriptions/tmpl/default_foot.php create mode 100644 admin/views/subscriptions/tmpl/default_head.php create mode 100644 admin/views/subscriptions/tmpl/default_toolbar.php create mode 100644 admin/views/subscriptions/tmpl/index.html create mode 100644 admin/views/subscriptions/view.html.php create mode 100644 extensiondistributor.xml create mode 100644 index.html create mode 100644 media/css/index.html create mode 100644 media/footable-v3/LICENSE-GPLv3 create mode 100644 media/footable-v3/css/footable.bootstrap.css create mode 100644 media/footable-v3/css/footable.bootstrap.min.css create mode 100644 media/footable-v3/css/footable.core.bootstrap.css create mode 100644 media/footable-v3/css/footable.core.bootstrap.min.css create mode 100644 media/footable-v3/css/footable.core.standalone.css create mode 100644 media/footable-v3/css/footable.core.standalone.min.css create mode 100644 media/footable-v3/css/footable.editing.css create mode 100644 media/footable-v3/css/footable.editing.min.css create mode 100644 media/footable-v3/css/footable.filtering.css create mode 100644 media/footable-v3/css/footable.filtering.min.css create mode 100644 media/footable-v3/css/footable.paging.css create mode 100644 media/footable-v3/css/footable.paging.min.css create mode 100644 media/footable-v3/css/footable.sorting.css create mode 100644 media/footable-v3/css/footable.sorting.min.css create mode 100644 media/footable-v3/css/footable.standalone.css create mode 100644 media/footable-v3/css/footable.standalone.min.css create mode 100644 media/footable-v3/css/index.html create mode 100644 media/footable-v3/index.html create mode 100644 media/footable-v3/js/footable.core.js create mode 100644 media/footable-v3/js/footable.core.min.js create mode 100644 media/footable-v3/js/footable.editing.js create mode 100644 media/footable-v3/js/footable.editing.min.js create mode 100644 media/footable-v3/js/footable.filtering.js create mode 100644 media/footable-v3/js/footable.filtering.min.js create mode 100644 media/footable-v3/js/footable.min.js create mode 100644 media/footable-v3/js/footable.paging.js create mode 100644 media/footable-v3/js/footable.paging.min.js create mode 100644 media/footable-v3/js/footable.sorting.js create mode 100644 media/footable-v3/js/footable.sorting.min.js create mode 100644 media/footable-v3/js/footable.state.js create mode 100644 media/footable-v3/js/footable.state.min.js create mode 100644 media/footable-v3/js/index.html create mode 100644 media/images/index.html create mode 100644 media/images/spinner.svg create mode 100644 media/index.html create mode 100644 media/js/download.js create mode 100644 media/js/extension.js create mode 100644 media/js/index.html create mode 100644 media/js/key.js create mode 100644 media/js/package.js create mode 100644 media/js/release.js create mode 100644 media/js/subscription.js create mode 100644 media/uikit-v3/css/index.html create mode 100644 media/uikit-v3/css/uikit.min.css create mode 100644 media/uikit-v3/index.html create mode 100644 media/uikit-v3/js/index.html create mode 100644 media/uikit-v3/js/uikit-icons.min.js create mode 100644 media/uikit-v3/js/uikit.min.js create mode 100644 script.php create mode 100644 site/assets/css/index.html create mode 100644 site/assets/css/keys.css create mode 100644 site/assets/css/package.css create mode 100644 site/assets/css/site.css create mode 100644 site/assets/images/index.html create mode 100644 site/assets/index.html create mode 100644 site/assets/js/index.html create mode 100644 site/assets/js/site.js create mode 100644 site/controller.php create mode 100644 site/controllers/index.html create mode 100644 site/controllers/package.php create mode 100644 site/extensiondistributor.php create mode 100644 site/helpers/category.php create mode 100644 site/helpers/categoryextension.php create mode 100644 site/helpers/extensiondistributor.php create mode 100644 site/helpers/headercheck.php create mode 100644 site/helpers/index.html create mode 100644 site/helpers/route.php create mode 100644 site/index.html create mode 100644 site/language/en-GB/en-GB.com_extensiondistributor.ini create mode 100644 site/language/en-GB/en-GB.com_extensiondistributor.sys.ini create mode 100644 site/language/en-GB/index.html create mode 100644 site/language/fr-FR/fr-FR.com_extensiondistributor.ini create mode 100644 site/language/fr-FR/fr-FR.com_extensiondistributor.sys.ini create mode 100644 site/language/index.html create mode 100644 site/layouts/index.html create mode 100644 site/models/index.html create mode 100644 site/models/keys.php create mode 100644 site/models/package.php create mode 100644 site/router.php create mode 100644 site/views/index.html create mode 100644 site/views/keys/index.html create mode 100644 site/views/keys/tmpl/default.php create mode 100644 site/views/keys/tmpl/default.xml create mode 100644 site/views/keys/tmpl/index.html create mode 100644 site/views/keys/view.html.php create mode 100644 site/views/package/index.html create mode 100644 site/views/package/submitbutton.js create mode 100644 site/views/package/tmpl/default.php create mode 100644 site/views/package/tmpl/index.html create mode 100644 site/views/package/view.html.php diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..df50810 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,340 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/admin/access.xml b/admin/access.xml new file mode 100644 index 0000000..f2a7f07 --- /dev/null +++ b/admin/access.xml @@ -0,0 +1,83 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + +
+
+ + + + +
+
+ + + + + +
+
\ No newline at end of file diff --git a/admin/assets/css/admin.css b/admin/assets/css/admin.css new file mode 100644 index 0000000..490d553 --- /dev/null +++ b/admin/assets/css/admin.css @@ -0,0 +1,119 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage admin.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ +.no-click { + pointer-events: none; +} +.dashboard-content a { + min-height: 170px !important; +} +#dashboard_right #vdm img { + padding: 10px 10px 20px 0px !important; +} +#maintenance_accordian .accordion-heading, +#dashboard_left .accordion-heading, +#dashboard_right .accordion-heading { + display: none; +} +#maintenance_accordian .accordion-inner, +#dashboard_left .accordion-inner, +#dashboard_right .accordion-inner { + border: none; +} +.dashboard-content img { + width: 96px; + margin: 15px 0; +} +.love { + font-family: fantasy; + color: red; + display: inline-block; + position: relative; + top: .2em; + font-size: 1.4em; + -webkit-transform: scale(.9); + -moz-transform: scale(.9); + transform: scale(.9); + -webkit-animation: love .5s infinite linear alternate-reverse; + -moz-animation: love .5s infinite linear alternate-reverse; + animation: love .5s infinite linear alternate-reverse +} + +@-webkit-keyframes love { + to { + -webkit-transform: scale(1.2) + } +} + +@-moz-keyframes love { + to { + -moz-transform: scale(1.2) + } +} + +@keyframes love { + to { + transform: scale(1.2) + } +} + +/* Margin */ +.mt10 { margin-top: 10px !important; } +.mt20 { margin-top: 20px !important; } +.mt30 { margin-top: 30px !important; } +.mt40 { margin-top: 40px !important; } +.mt50 { margin-top: 50px !important; } +.mb10 { margin-bottom: 10px !important; } +.mb20 { margin-bottom: 20px !important; } +.mb30 { margin-bottom: 30px !important; } +.mb40 { margin-bottom: 40px !important; } +.mb50 { margin-bottom: 50px !important; } +.ml10 { margin-left: 10px !important; } +.ml20 { margin-left: 20px !important; } +.ml30 { margin-left: 30px !important; } +.ml40 { margin-left: 40px !important; } +.ml50 { margin-left: 50px !important; } +.mr10 { margin-right: 10px !important; } +.mr20 { margin-right: 20px !important; } +.mr30 { margin-right: 30px !important; } +.mr40 { margin-right: 40px !important; } +.mr50 { margin-right: 50px !important; } +/* Padding */ +.pt10 { padding-top: 10px !important; } +.pt20 { padding-top: 20px !important; } +.pt30 { padding-top: 30px !important; } +.pt40 { padding-top: 40px !important; } +.pt50 { padding-top: 50px !important; } +.pb10 { padding-bottom: 10px !important; } +.pb20 { padding-bottom: 20px !important; } +.pb30 { padding-bottom: 30px !important; } +.pb40 { padding-bottom: 40px !important; } +.pb50 { padding-bottom: 50px !important; } +.pl10 { padding-left: 10px !important; } +.pl20 { padding-left: 20px !important; } +.pl30 { padding-left: 30px !important; } +.pl40 { padding-left: 40px !important; } +.pl50 { padding-left: 50px !important; } +.pr10 { padding-right: 10px !important; } +.pr20 { padding-right: 20px !important; } +.pr30 { padding-right: 30px !important; } +.pr40 { padding-right: 40px !important; } +.pr50 { padding-right: 50px !important; } + diff --git a/admin/assets/css/dashboard.css b/admin/assets/css/dashboard.css new file mode 100644 index 0000000..f54974f --- /dev/null +++ b/admin/assets/css/dashboard.css @@ -0,0 +1,172 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage dashboard.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + +.dashboard-container { + margin-left: 10px; + margin-top: 10px; + width: 100%; +} +.dashboard-container [class*="span"] { + display: block; + float: none; + margin-left: 0; + width: auto; +} +.dashboard-container:before, .dashboard-container:after { + content: ""; + display: table; +} +.dashboard-container:after { + clear: both; +} +.dashboard-container [class*="span"] { + box-sizing: border-box; + display: block; + float: left; + min-height: 145px; + min-width: 96px; + width: 100%; +} +.dashboard-container [class*="span"]:first-child { + margin-left: 0; +} +.dashboard-wraper { + background: none repeat scroll 0 0 hsl(0, 0%, 93%); + border-radius: 5px; + float: left; + margin: 1%; + padding: 3px; + width: 17%; + min-height: 194px; + min-width: 96px; +} +.dashboard-content a { + background: linear-gradient(to bottom, hsl(0, 0%, 100%) 0%, hsl(0, 0%, 96%) 47%, hsl(0, 0%, 93%) 100%) repeat scroll 0 0 hsla(0, 0%, 0%, 0); + border: 1px solid hsl(0, 0%, 85%); + border-radius: 4px; + box-shadow: 0 0 3px hsla(0, 0%, 0%, 0.1) inset; + color: hsl(0, 0%, 20%); + display: block; + min-height: 89px; + padding: 10px; + text-align: center; + text-decoration: none; +} +.dashboard-content a:hover { + background: linear-gradient(to bottom, hsl(0, 0%, 90%) 0%, hsl(0, 0%, 100%) 100%) repeat scroll 0 0 hsla(0, 0%, 0%, 0); + color: hsl(200, 100%, 30%); + text-decoration: none; +} +.dashboard-title { + display: block; + padding-top: 5px; +} +.dashboard-info { + background: linear-gradient(to bottom, hsl(0, 0%, 100%) 0%, hsl(0, 0%, 96%) 47%, hsl(0, 0%, 93%) 100%) repeat scroll 0 0 hsla(0, 0%, 0%, 0); + border: 1px solid hsl(0, 0%, 85%); + border-radius: 4px; + box-shadow: 0 0 3px hsla(0, 0%, 0%, 0.1) inset; + color: hsl(0, 0%, 20%); + display: block; + font-size: 12px; + padding: 10px; + text-align: center; +} +.dashboard-info span { + display: block; + text-align: center; +} +.dashboard-info img { + margin: 0 auto; +} +.dashboard-table { + border-top: 1px solid hsl(0, 0%, 87%); + margin-top: 5px; + width: 100%; +} +.dashboard-info h5 { + font-size: 11px; + font-weight: bold; +} +.dashboard-block { + background: linear-gradient(to bottom, hsl(0, 0%, 95%) 0%, hsl(0, 0%, 86%) 100%) repeat scroll 0 0 hsla(0, 0%, 0%, 0); + border: 1px solid hsl(0, 0%, 76%); + border-radius: 3px; + box-shadow: 0 1px 0 hsl(0, 0%, 98%) inset; + margin-bottom: 20px; +} +.dashboard-block .dashboard-block-head { + background: linear-gradient(to bottom, hsl(0, 0%, 95%) 0%, hsl(0, 0%, 86%) 100%) repeat scroll 0 0 hsla(0, 0%, 0%, 0); + border-bottom: 1px solid hsl(0, 0%, 76%); + border-radius: 3px 3px 0 0; + box-shadow: 0 1px 0 hsl(0, 0%, 98%) inset, 0 0 3px hsl(0, 0%, 87%); + height: 40px; + position: relative; +} +.dashboard-block .dashboard-block-head h5 { + font-size: 12px; + margin: 0; + padding-left: 10px; + padding-top: 11px; + text-transform: uppercase; +} +.dashboard-block .dashboard-block-content { + background: none repeat scroll 0 0 hsl(0, 0%, 93%); + border-radius: 0 0 3px 3px; +} +.dashboard-block .dashboard-block-box { + background: none repeat scroll 0 0 hsl(0, 0%, 100%); + border-top: 1px solid hsl(0, 0%, 82%); + box-shadow: 0 0 5px hsl(0, 0%, 87%) inset; +} +.dashboard-block .dashboard-block-content .dashboard-block-box { + margin-top: 0 !important; +} +.dashboard-block .dashboard-block-content .dashboard-block-box .dashboard-block-table { + margin-bottom: 0 !important; +} +.dashboard-block-table { + background-color: hsla(0, 0%, 0%, 0); + border-collapse: collapse; + border-spacing: 0; + margin-bottom: 20px; + max-width: 100%; + width: 100%; +} +.dashboard-block-table th, .dashboard-block-table td { + border-top: 1px solid hsl(0, 0%, 87%); + line-height: 20px; + padding: 5px; + text-align: left; + vertical-align: middle; +} +.dashboard-badge { + background-color: hsl(0, 0%, 60%); + border-radius: 9px; + color: hsl(0, 0%, 100%); + font-size: 11.844px; + font-weight: bold; + line-height: 14px; + padding: 1px 9px 2px; + text-shadow: 0 -1px 0 hsla(0, 0%, 0%, 0.25); + vertical-align: baseline; + white-space: nowrap; +} \ No newline at end of file diff --git a/admin/assets/css/download.css b/admin/assets/css/download.css new file mode 100644 index 0000000..1b8e6a9 --- /dev/null +++ b/admin/assets/css/download.css @@ -0,0 +1,22 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage download.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + diff --git a/admin/assets/css/downloads.css b/admin/assets/css/downloads.css new file mode 100644 index 0000000..d34b29b --- /dev/null +++ b/admin/assets/css/downloads.css @@ -0,0 +1,22 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage downloads.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + diff --git a/admin/assets/css/extension.css b/admin/assets/css/extension.css new file mode 100644 index 0000000..dca7332 --- /dev/null +++ b/admin/assets/css/extension.css @@ -0,0 +1,487 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage extension.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + +.footable-filtering { + display: none; +} +.footable .caret { + vertical-align: top !important; + margin-left : 0 !important; +} +table td { + vertical-align: middle !important; +} +.footable .label { + vertical-align: text-top !important; +} + +/* Specific for the sub-view because of footable */ +.agxbtn { + display: inline-block; + *display: inline; + *zoom: 1; + padding: 4px 12px; + margin-bottom: 0; + font-size: 13px; + line-height: 18px; + text-align: center; + vertical-align: middle; + cursor: pointer; + background-color: #f3f3f3; + color: #333; + border: 1px solid #b3b3b3; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0,0,0,0.05); +} +.agxbtn:hover, +.agxbtn:focus { + background-color: #e6e6e6; + text-decoration: none; + text-shadow: none; +} +.agxbtn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.agxbtn.active, +.agxbtn:active { + background-image: none; + outline: 0; +} +.agxbtn.disabled, +.agxbtn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.agxbtn-large { + padding: 11px 19px; + font-size: 16.25px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.agxbtn-large [class^="icon-"], +.agxbtn-large [class*=" icon-"] { + margin-top: 4px; +} +.agxbtn-small { + padding: 2px 10px; + font-size: 12px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.agxbtn-small [class^="icon-"], +.agxbtn-small [class*=" icon-"] { + margin-top: 0; +} +.agxbtn-mini [class^="icon-"], +.agxbtn-mini [class*=" icon-"] { + margin-top: -1px; +} +.agxbtn-mini { + padding: 0 6px; + font-size: 9.75px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.agxbtn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.agxbtn-block + .agxbtn-block { + margin-top: 5px; +} +input[type="submit"].agxbtn-block, +input[type="reset"].agxbtn-block, +input[type="button"].agxbtn-block { + width: 100%; +} +.agxbtn-primary, +.agxbtn-warning, +.agxbtn-danger, +.agxbtn-success, +.agxbtn-info, +.agxbtn-inverse { + box-shadow: 0 1px 2px rgba(0,0,0,0.05); +} +.agxbtn-primary { + border: 1px solid #15497c; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #2384d3; +} +.agxbtn-primary:hover, +.agxbtn-primary:focus { + background-color: #185b91; + color: #fff; + text-decoration: none; +} +.agxbtn-warning { + border: 1px solid #f89406; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #f89406; +} +.agxbtn-warning:hover, +.agxbtn-warning:focus { + background-color: #ad6704; + color: #fff; + text-decoration: none; + text-shadow: none; +} +.agxbtn-danger { + border: 1px solid #bd362f; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #bd362f; +} +.agxbtn-danger:hover, +.agxbtn-danger:focus { + background-color: #802420; + color: #fff; + text-decoration: none; +} +.agxbtn-success { + border: 1px solid #378137; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #46a546; +} +.agxbtn-success:hover, +.agxbtn-success:focus { + background-color: #2f6f2f; + color: #fff; + text-decoration: none; +} +.agxbtn-info { + border: 1px solid #2f96b4; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #2f96b4; +} +.agxbtn-info:hover, +.agxbtn-info:focus { + background-color: #1f6377; + color: #fff; + text-decoration: none; +} +.agxbtn-inverse { + border: 1px solid #444; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #444; +} +.agxbtn-inverse:hover, +.agxbtn-inverse:focus { + background-color: #1e1e1e; + color: #fff; + text-decoration: none; +} +button.agxbtn, +input[type="submit"].agxbtn { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.agxbtn::-moz-focus-inner, +input[type="submit"].agxbtn::-moz-focus-inner { + padding: 0; + border: 0; +} +button.agxbtn.agxbtn-large, +input[type="submit"].agxbtn.agxbtn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} +button.agxbtn.agxbtn-small, +input[type="submit"].agxbtn.agxbtn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.agxbtn.agxbtn-mini, +input[type="submit"].agxbtn.agxbtn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} +.agxbtn-link, +.agxbtn-link:active, +.agxbtn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.agxbtn-link { + border-color: transparent; + cursor: pointer; + color: #3071a9; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.agxbtn-link:hover, +.agxbtn-link:focus { + color: #1f496e; + text-decoration: underline; + background-color: transparent; +} +.agxbtn-link[disabled]:hover, +.agxbtn-link[disabled]:focus { + color: #333; + text-decoration: none; +} +.agxbtn-group { + position: relative; + display: inline-block; + *display: inline; + *zoom: 1; + font-size: 0; + vertical-align: middle; + white-space: nowrap; + *margin-left: .3em; +} +.agxbtn-group:first-child { + *margin-left: 0; +} +.agxbtn-group .agxbtn + .agxbtn { + margin-left: -1px; +} +.agxbtn-group + .agxbtn-group { + margin-left: 5px; +} +.agxbtn-toolbar { + font-size: 0; + margin-top: 9px; + margin-bottom: 9px; +} +.agxbtn-toolbar > .agxbtn + .agxbtn, +.agxbtn-toolbar > .agxbtn-group + .agxbtn, +.agxbtn-toolbar > .agxbtn + .agxbtn-group { + margin-left: 5px; +} +.agxbtn-group > .agxbtn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.agxbtn-group > .agxbtn-micro { + margin-left: -1px; +} +.agxbtn-group > .agxbtn, +.agxbtn-group > .dropdown-menu, +.agxbtn-group > .popover { + font-size: 13px; +} +.agxbtn-group > .agxbtn-mini { + font-size: 9.75px; +} +.agxbtn-group > .agxbtn-small { + font-size: 12px; +} +.agxbtn-group > .agxbtn-large { + font-size: 16.25px; +} +.agxbtn-group > .agxbtn:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 3px; + -moz-border-radius-topleft: 3px; + border-top-left-radius: 3px; + -webkit-border-bottom-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + border-bottom-left-radius: 3px; +} +.agxbtn-group > .agxbtn:last-child, +.agxbtn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 3px; + -moz-border-radius-topright: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + -moz-border-radius-bottomright: 3px; + border-bottom-right-radius: 3px; +} +.agxbtn-group > .agxbtn.large:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.agxbtn-group > .agxbtn.large:last-child, +.agxbtn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.agxbtn-group > .agxbtn:hover, +.agxbtn-group > .agxbtn:focus, +.agxbtn-group > .agxbtn:active, +.agxbtn-group > .agxbtn.active { + z-index: 2; +} +.agxbtn-group .dropdown-toggle:active, +.agxbtn-group.open .dropdown-toggle { + outline: 0; +} +.agxbtn-group > .agxbtn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + *padding-top: 5px; + *padding-bottom: 5px; +} +.agxbtn-group > .agxbtn-mini + .dropdown-toggle { + padding-left: 5px; + padding-right: 5px; + *padding-top: 2px; + *padding-bottom: 2px; +} +.agxbtn-group > .agxbtn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} +.agxbtn-group > .agxbtn-large + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; + *padding-top: 7px; + *padding-bottom: 7px; +} +.agxbtn-group.open .dropdown-toggle { + background-image: none; +} +.agxbtn-group.open .agxbtn.dropdown-toggle { + background-color: #e6e6e6; +} +.agxbtn-group.open .agxbtn-primary.dropdown-toggle { + background-color: #15497c; +} +.agxbtn-group.open .agxbtn-warning.dropdown-toggle { + background-color: #c67605; +} +.agxbtn-group.open .agxbtn-danger.dropdown-toggle { + background-color: #942a25; +} +.agxbtn-group.open .agxbtn-success.dropdown-toggle { + background-color: #378137; +} +.agxbtn-group.open .agxbtn-info.dropdown-toggle { + background-color: #24748c; +} +.agxbtn-group.open .agxbtn-inverse.dropdown-toggle { + background-color: #222; +} +.agxbtn .caret { + margin-top: 8px; + margin-left: 0; +} +.agxbtn-large .caret { + margin-top: 6px; +} +.agxbtn-large .caret { + border-left-width: 5px; + border-right-width: 5px; + border-top-width: 5px; +} +.agxbtn-mini .caret, +.agxbtn-small .caret { + margin-top: 8px; +} +.dropup .agxbtn-large .caret { + border-bottom-width: 5px; +} +.agxbtn-primary .caret { + border-top-color: #1f496e; + border-bottom-color: #1f496e; +} +.agxbtn-warning .caret, +.agxbtn-danger .caret, +.agxbtn-info .caret, +.agxbtn-success .caret, +.agxbtn-inverse .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.agxbtn-group-vertical { + display: inline-block; + *display: inline; + *zoom: 1; +} +.agxbtn-group-vertical > .agxbtn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.agxbtn-group-vertical > .agxbtn + .agxbtn { + margin-left: 0; + margin-top: -1px; +} +.agxbtn-group-vertical > .agxbtn:first-child { + -webkit-border-radius: 3px 3px 0 0; + -moz-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; +} +.agxbtn-group-vertical > .agxbtn:last-child { + -webkit-border-radius: 0 0 3px 3px; + -moz-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; +} +.agxbtn-group-vertical > .agxbtn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} +.agxbtn-group-vertical > .agxbtn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.agxbtn-micro { + padding: 1px 4px; + font-size: 10px; + line-height: 8px; +} +.agxbtn-group > .agxbtn-micro { + font-size: 10px; +} + diff --git a/admin/assets/css/extensions.css b/admin/assets/css/extensions.css new file mode 100644 index 0000000..f98f09d --- /dev/null +++ b/admin/assets/css/extensions.css @@ -0,0 +1,26 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage extensions.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + +.table td { + vertical-align: middle !important; +} + diff --git a/admin/assets/css/import.css b/admin/assets/css/import.css new file mode 100644 index 0000000..0ded300 --- /dev/null +++ b/admin/assets/css/import.css @@ -0,0 +1,22 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage import.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + diff --git a/admin/assets/css/index.html b/admin/assets/css/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/assets/css/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/assets/css/key.css b/admin/assets/css/key.css new file mode 100644 index 0000000..b877751 --- /dev/null +++ b/admin/assets/css/key.css @@ -0,0 +1,23 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage key.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + +.icon-save::before { color: #FFF !important; } diff --git a/admin/assets/css/keys.css b/admin/assets/css/keys.css new file mode 100644 index 0000000..1ce441a --- /dev/null +++ b/admin/assets/css/keys.css @@ -0,0 +1,22 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage keys.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + diff --git a/admin/assets/css/maintenance.css b/admin/assets/css/maintenance.css new file mode 100644 index 0000000..e46effe --- /dev/null +++ b/admin/assets/css/maintenance.css @@ -0,0 +1,22 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage maintenance.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + diff --git a/admin/assets/css/package.css b/admin/assets/css/package.css new file mode 100644 index 0000000..cb22cfd --- /dev/null +++ b/admin/assets/css/package.css @@ -0,0 +1,487 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage package.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + +.footable-filtering { + display: none; +} +.footable .caret { + vertical-align: top !important; + margin-left : 0 !important; +} +table td { + vertical-align: middle !important; +} +.footable .label { + vertical-align: text-top !important; +} + +/* Specific for the sub-view because of footable */ +.agxbtn { + display: inline-block; + *display: inline; + *zoom: 1; + padding: 4px 12px; + margin-bottom: 0; + font-size: 13px; + line-height: 18px; + text-align: center; + vertical-align: middle; + cursor: pointer; + background-color: #f3f3f3; + color: #333; + border: 1px solid #b3b3b3; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0,0,0,0.05); +} +.agxbtn:hover, +.agxbtn:focus { + background-color: #e6e6e6; + text-decoration: none; + text-shadow: none; +} +.agxbtn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.agxbtn.active, +.agxbtn:active { + background-image: none; + outline: 0; +} +.agxbtn.disabled, +.agxbtn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.agxbtn-large { + padding: 11px 19px; + font-size: 16.25px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.agxbtn-large [class^="icon-"], +.agxbtn-large [class*=" icon-"] { + margin-top: 4px; +} +.agxbtn-small { + padding: 2px 10px; + font-size: 12px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.agxbtn-small [class^="icon-"], +.agxbtn-small [class*=" icon-"] { + margin-top: 0; +} +.agxbtn-mini [class^="icon-"], +.agxbtn-mini [class*=" icon-"] { + margin-top: -1px; +} +.agxbtn-mini { + padding: 0 6px; + font-size: 9.75px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.agxbtn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.agxbtn-block + .agxbtn-block { + margin-top: 5px; +} +input[type="submit"].agxbtn-block, +input[type="reset"].agxbtn-block, +input[type="button"].agxbtn-block { + width: 100%; +} +.agxbtn-primary, +.agxbtn-warning, +.agxbtn-danger, +.agxbtn-success, +.agxbtn-info, +.agxbtn-inverse { + box-shadow: 0 1px 2px rgba(0,0,0,0.05); +} +.agxbtn-primary { + border: 1px solid #15497c; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #2384d3; +} +.agxbtn-primary:hover, +.agxbtn-primary:focus { + background-color: #185b91; + color: #fff; + text-decoration: none; +} +.agxbtn-warning { + border: 1px solid #f89406; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #f89406; +} +.agxbtn-warning:hover, +.agxbtn-warning:focus { + background-color: #ad6704; + color: #fff; + text-decoration: none; + text-shadow: none; +} +.agxbtn-danger { + border: 1px solid #bd362f; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #bd362f; +} +.agxbtn-danger:hover, +.agxbtn-danger:focus { + background-color: #802420; + color: #fff; + text-decoration: none; +} +.agxbtn-success { + border: 1px solid #378137; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #46a546; +} +.agxbtn-success:hover, +.agxbtn-success:focus { + background-color: #2f6f2f; + color: #fff; + text-decoration: none; +} +.agxbtn-info { + border: 1px solid #2f96b4; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #2f96b4; +} +.agxbtn-info:hover, +.agxbtn-info:focus { + background-color: #1f6377; + color: #fff; + text-decoration: none; +} +.agxbtn-inverse { + border: 1px solid #444; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #444; +} +.agxbtn-inverse:hover, +.agxbtn-inverse:focus { + background-color: #1e1e1e; + color: #fff; + text-decoration: none; +} +button.agxbtn, +input[type="submit"].agxbtn { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.agxbtn::-moz-focus-inner, +input[type="submit"].agxbtn::-moz-focus-inner { + padding: 0; + border: 0; +} +button.agxbtn.agxbtn-large, +input[type="submit"].agxbtn.agxbtn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} +button.agxbtn.agxbtn-small, +input[type="submit"].agxbtn.agxbtn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.agxbtn.agxbtn-mini, +input[type="submit"].agxbtn.agxbtn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} +.agxbtn-link, +.agxbtn-link:active, +.agxbtn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.agxbtn-link { + border-color: transparent; + cursor: pointer; + color: #3071a9; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.agxbtn-link:hover, +.agxbtn-link:focus { + color: #1f496e; + text-decoration: underline; + background-color: transparent; +} +.agxbtn-link[disabled]:hover, +.agxbtn-link[disabled]:focus { + color: #333; + text-decoration: none; +} +.agxbtn-group { + position: relative; + display: inline-block; + *display: inline; + *zoom: 1; + font-size: 0; + vertical-align: middle; + white-space: nowrap; + *margin-left: .3em; +} +.agxbtn-group:first-child { + *margin-left: 0; +} +.agxbtn-group .agxbtn + .agxbtn { + margin-left: -1px; +} +.agxbtn-group + .agxbtn-group { + margin-left: 5px; +} +.agxbtn-toolbar { + font-size: 0; + margin-top: 9px; + margin-bottom: 9px; +} +.agxbtn-toolbar > .agxbtn + .agxbtn, +.agxbtn-toolbar > .agxbtn-group + .agxbtn, +.agxbtn-toolbar > .agxbtn + .agxbtn-group { + margin-left: 5px; +} +.agxbtn-group > .agxbtn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.agxbtn-group > .agxbtn-micro { + margin-left: -1px; +} +.agxbtn-group > .agxbtn, +.agxbtn-group > .dropdown-menu, +.agxbtn-group > .popover { + font-size: 13px; +} +.agxbtn-group > .agxbtn-mini { + font-size: 9.75px; +} +.agxbtn-group > .agxbtn-small { + font-size: 12px; +} +.agxbtn-group > .agxbtn-large { + font-size: 16.25px; +} +.agxbtn-group > .agxbtn:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 3px; + -moz-border-radius-topleft: 3px; + border-top-left-radius: 3px; + -webkit-border-bottom-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + border-bottom-left-radius: 3px; +} +.agxbtn-group > .agxbtn:last-child, +.agxbtn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 3px; + -moz-border-radius-topright: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + -moz-border-radius-bottomright: 3px; + border-bottom-right-radius: 3px; +} +.agxbtn-group > .agxbtn.large:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.agxbtn-group > .agxbtn.large:last-child, +.agxbtn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.agxbtn-group > .agxbtn:hover, +.agxbtn-group > .agxbtn:focus, +.agxbtn-group > .agxbtn:active, +.agxbtn-group > .agxbtn.active { + z-index: 2; +} +.agxbtn-group .dropdown-toggle:active, +.agxbtn-group.open .dropdown-toggle { + outline: 0; +} +.agxbtn-group > .agxbtn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + *padding-top: 5px; + *padding-bottom: 5px; +} +.agxbtn-group > .agxbtn-mini + .dropdown-toggle { + padding-left: 5px; + padding-right: 5px; + *padding-top: 2px; + *padding-bottom: 2px; +} +.agxbtn-group > .agxbtn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} +.agxbtn-group > .agxbtn-large + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; + *padding-top: 7px; + *padding-bottom: 7px; +} +.agxbtn-group.open .dropdown-toggle { + background-image: none; +} +.agxbtn-group.open .agxbtn.dropdown-toggle { + background-color: #e6e6e6; +} +.agxbtn-group.open .agxbtn-primary.dropdown-toggle { + background-color: #15497c; +} +.agxbtn-group.open .agxbtn-warning.dropdown-toggle { + background-color: #c67605; +} +.agxbtn-group.open .agxbtn-danger.dropdown-toggle { + background-color: #942a25; +} +.agxbtn-group.open .agxbtn-success.dropdown-toggle { + background-color: #378137; +} +.agxbtn-group.open .agxbtn-info.dropdown-toggle { + background-color: #24748c; +} +.agxbtn-group.open .agxbtn-inverse.dropdown-toggle { + background-color: #222; +} +.agxbtn .caret { + margin-top: 8px; + margin-left: 0; +} +.agxbtn-large .caret { + margin-top: 6px; +} +.agxbtn-large .caret { + border-left-width: 5px; + border-right-width: 5px; + border-top-width: 5px; +} +.agxbtn-mini .caret, +.agxbtn-small .caret { + margin-top: 8px; +} +.dropup .agxbtn-large .caret { + border-bottom-width: 5px; +} +.agxbtn-primary .caret { + border-top-color: #1f496e; + border-bottom-color: #1f496e; +} +.agxbtn-warning .caret, +.agxbtn-danger .caret, +.agxbtn-info .caret, +.agxbtn-success .caret, +.agxbtn-inverse .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.agxbtn-group-vertical { + display: inline-block; + *display: inline; + *zoom: 1; +} +.agxbtn-group-vertical > .agxbtn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.agxbtn-group-vertical > .agxbtn + .agxbtn { + margin-left: 0; + margin-top: -1px; +} +.agxbtn-group-vertical > .agxbtn:first-child { + -webkit-border-radius: 3px 3px 0 0; + -moz-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; +} +.agxbtn-group-vertical > .agxbtn:last-child { + -webkit-border-radius: 0 0 3px 3px; + -moz-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; +} +.agxbtn-group-vertical > .agxbtn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} +.agxbtn-group-vertical > .agxbtn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.agxbtn-micro { + padding: 1px 4px; + font-size: 10px; + line-height: 8px; +} +.agxbtn-group > .agxbtn-micro { + font-size: 10px; +} + diff --git a/admin/assets/css/packages.css b/admin/assets/css/packages.css new file mode 100644 index 0000000..e00d5a9 --- /dev/null +++ b/admin/assets/css/packages.css @@ -0,0 +1,75 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage packages.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + +.table td { + vertical-align: middle !important; +} + +.circle { + box-sizing:border-box; + -webkit-border-radius:12px; + -moz-border-radius:12px; + border-radius:12px; + width:24px; + height:24px; + padding:6px 5px; + margin:5px 0; + font-size:12px; + line-height:12px; + text-transform:uppercase; + text-align:center; + display: inline-block; +} +.circle-component { + background-color:#00a500 +} +.circle-module { + background-color:#ff4b39 +} +.circle-plugin { + background-color:#e20079 +} +.circle-search { + background-color:#ff8100 +} +.circle-library { + background-color:#ffc40d; + color:#333 +} +.circle-template { + background-color:#eab31d +} +.circle-language { + background-color:#ff2121 +} +.circle-file { + background-color:#b700ff +} +.circle a:hover { + text-decoration:none +} +a .circle { + color:#fff; + font-size: 10px; + font-weight: bold; +} + diff --git a/admin/assets/css/release.css b/admin/assets/css/release.css new file mode 100644 index 0000000..ea59110 --- /dev/null +++ b/admin/assets/css/release.css @@ -0,0 +1,50 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage release.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + +.control-wrapper-changelog > .control-label { + display: none; +} +.control-wrapper-changelog .subform-repeatable > table > thead > tr > th:first-child { + width: 15%; +} + +/* Etat Clôturé résolu */ +.chzn-single.chzn-color-state[rel="value_Add"] { + background-color: #46a546 !important; + color: #FFF; +} +/* Etat Clôturé non-résolu */ +.chzn-single.chzn-color-state[rel="value_Remove"] { + background-color: #bd362f !important; + color: #FFF; +} +/* Etat Nouveau */ +.chzn-single.chzn-color-state[rel="value_Change"] { + background-color: #f89406 !important; + color: #FFF; +} +/* Etat Suspendu */ +.chzn-single.chzn-color-state[rel="value_Bugfix"] { + background-color: #444444 !important; + color: #FFF; +} + diff --git a/admin/assets/css/releases.css b/admin/assets/css/releases.css new file mode 100644 index 0000000..4f767ff --- /dev/null +++ b/admin/assets/css/releases.css @@ -0,0 +1,477 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage releases.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + +.table td { + vertical-align: middle !important; +} + +/* Specific for the sub-view because of footable */ +.agxbtn { + display: inline-block; + *display: inline; + *zoom: 1; + padding: 4px 12px; + margin-bottom: 0; + font-size: 13px; + line-height: 18px; + text-align: center; + vertical-align: middle; + cursor: pointer; + background-color: #f3f3f3; + color: #333; + border: 1px solid #b3b3b3; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(0,0,0,0.05); +} +.agxbtn:hover, +.agxbtn:focus { + background-color: #e6e6e6; + text-decoration: none; + text-shadow: none; +} +.agxbtn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.agxbtn.active, +.agxbtn:active { + background-image: none; + outline: 0; +} +.agxbtn.disabled, +.agxbtn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.agxbtn-large { + padding: 11px 19px; + font-size: 16.25px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.agxbtn-large [class^="icon-"], +.agxbtn-large [class*=" icon-"] { + margin-top: 4px; +} +.agxbtn-small { + padding: 2px 10px; + font-size: 12px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.agxbtn-small [class^="icon-"], +.agxbtn-small [class*=" icon-"] { + margin-top: 0; +} +.agxbtn-mini [class^="icon-"], +.agxbtn-mini [class*=" icon-"] { + margin-top: -1px; +} +.agxbtn-mini { + padding: 0 6px; + font-size: 9.75px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.agxbtn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.agxbtn-block + .agxbtn-block { + margin-top: 5px; +} +input[type="submit"].agxbtn-block, +input[type="reset"].agxbtn-block, +input[type="button"].agxbtn-block { + width: 100%; +} +.agxbtn-primary, +.agxbtn-warning, +.agxbtn-danger, +.agxbtn-success, +.agxbtn-info, +.agxbtn-inverse { + box-shadow: 0 1px 2px rgba(0,0,0,0.05); +} +.agxbtn-primary { + border: 1px solid #15497c; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #2384d3; +} +.agxbtn-primary:hover, +.agxbtn-primary:focus { + background-color: #185b91; + color: #fff; + text-decoration: none; +} +.agxbtn-warning { + border: 1px solid #f89406; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #f89406; +} +.agxbtn-warning:hover, +.agxbtn-warning:focus { + background-color: #ad6704; + color: #fff; + text-decoration: none; + text-shadow: none; +} +.agxbtn-danger { + border: 1px solid #bd362f; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #bd362f; +} +.agxbtn-danger:hover, +.agxbtn-danger:focus { + background-color: #802420; + color: #fff; + text-decoration: none; +} +.agxbtn-success { + border: 1px solid #378137; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #46a546; +} +.agxbtn-success:hover, +.agxbtn-success:focus { + background-color: #2f6f2f; + color: #fff; + text-decoration: none; +} +.agxbtn-info { + border: 1px solid #2f96b4; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #2f96b4; +} +.agxbtn-info:hover, +.agxbtn-info:focus { + background-color: #1f6377; + color: #fff; + text-decoration: none; +} +.agxbtn-inverse { + border: 1px solid #444; + border: 1px solid rgba(0,0,0,0.2); + color: #fff; + background-color: #444; +} +.agxbtn-inverse:hover, +.agxbtn-inverse:focus { + background-color: #1e1e1e; + color: #fff; + text-decoration: none; +} +button.agxbtn, +input[type="submit"].agxbtn { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.agxbtn::-moz-focus-inner, +input[type="submit"].agxbtn::-moz-focus-inner { + padding: 0; + border: 0; +} +button.agxbtn.agxbtn-large, +input[type="submit"].agxbtn.agxbtn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} +button.agxbtn.agxbtn-small, +input[type="submit"].agxbtn.agxbtn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.agxbtn.agxbtn-mini, +input[type="submit"].agxbtn.agxbtn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} +.agxbtn-link, +.agxbtn-link:active, +.agxbtn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.agxbtn-link { + border-color: transparent; + cursor: pointer; + color: #3071a9; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.agxbtn-link:hover, +.agxbtn-link:focus { + color: #1f496e; + text-decoration: underline; + background-color: transparent; +} +.agxbtn-link[disabled]:hover, +.agxbtn-link[disabled]:focus { + color: #333; + text-decoration: none; +} +.agxbtn-group { + position: relative; + display: inline-block; + *display: inline; + *zoom: 1; + font-size: 0; + vertical-align: middle; + white-space: nowrap; + *margin-left: .3em; +} +.agxbtn-group:first-child { + *margin-left: 0; +} +.agxbtn-group .agxbtn + .agxbtn { + margin-left: -1px; +} +.agxbtn-group + .agxbtn-group { + margin-left: 5px; +} +.agxbtn-toolbar { + font-size: 0; + margin-top: 9px; + margin-bottom: 9px; +} +.agxbtn-toolbar > .agxbtn + .agxbtn, +.agxbtn-toolbar > .agxbtn-group + .agxbtn, +.agxbtn-toolbar > .agxbtn + .agxbtn-group { + margin-left: 5px; +} +.agxbtn-group > .agxbtn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.agxbtn-group > .agxbtn-micro { + margin-left: -1px; +} +.agxbtn-group > .agxbtn, +.agxbtn-group > .dropdown-menu, +.agxbtn-group > .popover { + font-size: 13px; +} +.agxbtn-group > .agxbtn-mini { + font-size: 9.75px; +} +.agxbtn-group > .agxbtn-small { + font-size: 12px; +} +.agxbtn-group > .agxbtn-large { + font-size: 16.25px; +} +.agxbtn-group > .agxbtn:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 3px; + -moz-border-radius-topleft: 3px; + border-top-left-radius: 3px; + -webkit-border-bottom-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + border-bottom-left-radius: 3px; +} +.agxbtn-group > .agxbtn:last-child, +.agxbtn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 3px; + -moz-border-radius-topright: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + -moz-border-radius-bottomright: 3px; + border-bottom-right-radius: 3px; +} +.agxbtn-group > .agxbtn.large:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.agxbtn-group > .agxbtn.large:last-child, +.agxbtn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.agxbtn-group > .agxbtn:hover, +.agxbtn-group > .agxbtn:focus, +.agxbtn-group > .agxbtn:active, +.agxbtn-group > .agxbtn.active { + z-index: 2; +} +.agxbtn-group .dropdown-toggle:active, +.agxbtn-group.open .dropdown-toggle { + outline: 0; +} +.agxbtn-group > .agxbtn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + *padding-top: 5px; + *padding-bottom: 5px; +} +.agxbtn-group > .agxbtn-mini + .dropdown-toggle { + padding-left: 5px; + padding-right: 5px; + *padding-top: 2px; + *padding-bottom: 2px; +} +.agxbtn-group > .agxbtn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} +.agxbtn-group > .agxbtn-large + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; + *padding-top: 7px; + *padding-bottom: 7px; +} +.agxbtn-group.open .dropdown-toggle { + background-image: none; +} +.agxbtn-group.open .agxbtn.dropdown-toggle { + background-color: #e6e6e6; +} +.agxbtn-group.open .agxbtn-primary.dropdown-toggle { + background-color: #15497c; +} +.agxbtn-group.open .agxbtn-warning.dropdown-toggle { + background-color: #c67605; +} +.agxbtn-group.open .agxbtn-danger.dropdown-toggle { + background-color: #942a25; +} +.agxbtn-group.open .agxbtn-success.dropdown-toggle { + background-color: #378137; +} +.agxbtn-group.open .agxbtn-info.dropdown-toggle { + background-color: #24748c; +} +.agxbtn-group.open .agxbtn-inverse.dropdown-toggle { + background-color: #222; +} +.agxbtn .caret { + margin-top: 8px; + margin-left: 0; +} +.agxbtn-large .caret { + margin-top: 6px; +} +.agxbtn-large .caret { + border-left-width: 5px; + border-right-width: 5px; + border-top-width: 5px; +} +.agxbtn-mini .caret, +.agxbtn-small .caret { + margin-top: 8px; +} +.dropup .agxbtn-large .caret { + border-bottom-width: 5px; +} +.agxbtn-primary .caret { + border-top-color: #1f496e; + border-bottom-color: #1f496e; +} +.agxbtn-warning .caret, +.agxbtn-danger .caret, +.agxbtn-info .caret, +.agxbtn-success .caret, +.agxbtn-inverse .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.agxbtn-group-vertical { + display: inline-block; + *display: inline; + *zoom: 1; +} +.agxbtn-group-vertical > .agxbtn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.agxbtn-group-vertical > .agxbtn + .agxbtn { + margin-left: 0; + margin-top: -1px; +} +.agxbtn-group-vertical > .agxbtn:first-child { + -webkit-border-radius: 3px 3px 0 0; + -moz-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; +} +.agxbtn-group-vertical > .agxbtn:last-child { + -webkit-border-radius: 0 0 3px 3px; + -moz-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; +} +.agxbtn-group-vertical > .agxbtn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} +.agxbtn-group-vertical > .agxbtn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.agxbtn-micro { + padding: 1px 4px; + font-size: 10px; + line-height: 8px; +} +.agxbtn-group > .agxbtn-micro { + font-size: 10px; +} + diff --git a/admin/assets/css/statistics.css b/admin/assets/css/statistics.css new file mode 100644 index 0000000..fdddb82 --- /dev/null +++ b/admin/assets/css/statistics.css @@ -0,0 +1,22 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage statistics.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + diff --git a/admin/assets/css/subscription.css b/admin/assets/css/subscription.css new file mode 100644 index 0000000..0660c26 --- /dev/null +++ b/admin/assets/css/subscription.css @@ -0,0 +1,23 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage subscription.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + +.icon-save::before { color: #FFF !important; } diff --git a/admin/assets/css/subscriptions.css b/admin/assets/css/subscriptions.css new file mode 100644 index 0000000..185f017 --- /dev/null +++ b/admin/assets/css/subscriptions.css @@ -0,0 +1,26 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage subscriptions.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + +.table td { + vertical-align: middle !important; +} + diff --git a/admin/assets/images/icons/downloads.png b/admin/assets/images/icons/downloads.png new file mode 100644 index 0000000000000000000000000000000000000000..132e2f98b494e96b1a6e7088245bd82134ac140d GIT binary patch literal 3000 zcmbVOi8mB%7k_4k!88~ogg37xOIb_CKDM$>Xr@pm%9a|$STba?FUb;->{OPBY}v<7 z_WhMDTiF|wHGKLPzUQ2KfA`*V?(^LHyXT(sgc)P6F(b|*007K-y4t2ENck&_@Dtsn zX5D!LbZ(k@IL4FX!)OzF;xoDEn!5o2EB-5x-vMiSCqW*Pjv48OvmMFP+SL|#dU{Gd zaC+!=-`d4i%GuTa&AQ5208Yv4X=~uTlGjqmF1TU-w)*;#sMTPaZn_05^NKco+z1rm zxRT{)OUf(jMz?SUoQR_DwQ!0ZxW1J(*Hun=1 zW`Oa51V{rUz;^D0dsl_F^ZDaP)x7(LMCFO^-M1q=P zLPG`nq8ymQMG>7hka!9BH(v@NTua5z>7j4+@(1hUL;EW)_chf4QP112eO74QrlI_4-MhH~ zJRf2_AA-U{saK7obl=Iip^|d=F2e2)L}yCJ-q~9sGdtaAe)ufq;X?zCdGn8sn|RH@ zy`gh{`NaKzx#XT$rYAyRy28sp?w-)ILKme@)SWMjeh-fo{M4qF1eRjCptQ!I`)_qW zFnEOzng<6w%bkyOMxzV9Bj1-~2W&%K#Bi~pbIhpo$|)i+i8;0DPKNiYmm8M^nlfsE z+%x>xubs0p7uKT+-IbbM946_79W_r;%?du*JsQ}H7ShN5nJzZ~e-@?%Ct$}tQ8*R` z!8&K3P6Sb>?$i3$+IRJd5GPY9zoB_1218}tg=oc}OCQMPS~FVE`R0dbIPa+&m^p$`iR^EtGC zixosiENyU2yLM{^d? zeKU5DgCr=X2-(ZnRT$kRWem#3_Ui-j#}$J-u90+;Go)afQg*RL3Jcf$Ai>9V`{c3LxG}tt&q9@1~8Y#M3JcCB@KT0KT&uixyyqez1lFoEQ(>ujFR$yZ)_Pp=cB93J_WP9eH#v2^g;(7iN^;_O}iZ!(fHQ2 zX=~lDT>Y2SM-Vw$ybwC_RBYVgQ!76mkw&~HvNA6Uk5ns$HTR02^F|4hMtg!5mBKUE z&e?Emb;_ZRd%Ue%97>olTjBgbt=HqRqHA2Ej24x0QM}wVK0A5r}zXG(5DLkDmJa;Q;pxWs2I^3ky^-t zkHHEkRmpA_FVK!hzm^5ispN~rvX@p}iH5|U(jyPj)B$+->(3}OfFwc#BM zY|HLRKA1}ofN}Zr0Ngbz&^k1UQ!9Zt82z-d6#6`)8mCqa7aXvcX8}G^zJxU?j!`Ud zbXun$loM&mZq7*{P}>vk(xC$s{<)+-QXAHve}oif*UzLBYKQtn=+6dT}c zXCrxK_Jiy-Hg=1mM8swHm+I4PCg0=*GeDGCO>vzD&1I)4BLyo7R86r70=&E?YM{&r z6*J_l+0Ui3G$7A-v}K8Jf&$TOWVZuR?a(+2?jR1pZql18l)jwO?`_p4Uw_S~6EhbV zuPzDwra9jzpXK?M8+Q9vT|rfjL@6f#fzQ>OspsS6YZ=B(Mm{6!U^UcN{SGUsDQ*tNUm~LI&!26($u`K7BLLsEAoHqJmQI z-z@*`o&k=%Sq8#!*VQOatj$JyGJBZRlcgzj99H(9=LDynbA~mj8iIFjOnoitU%I#y zs{jRUqjam42rD8sNB3TSTT?=m^Mi?HQnX?F1qh%d@$Q5=*qmYPOzCljwz@(4FVr6$ zh1X)Qb;Q;D$7HBeQ?-3w(1PU~@7PGuZU7rO(Q9K&^1_hsBZo^*D&V@U<2B3fCU;0| z-0sG;0qDz&q=HqObjvy-d5vL7EbO!4Os;7L`G zMzO|qu}6=x-&eZS!!V;Fe3&yY4sC|4!gjUbWfU*1yIjP)Kc8j8p&~z^r9pd4GNpS* z=fYb8MJECfeW$h$=ND$J(vWcWjZd<&o8)*xmIFQ9!VYJQjS`v1QDD{tP3pj% z7uNBl9wrjI-No06VNVnC_Z2zP*Tjw)pVw<{VYNIVw1rAT&#n*YKio`IL|+b$v|OU@ z-Y8tIMhaT3y^8YkkP5$$mcETuNhGAz5Kt3VvPsPkozRc@w4A2ojs=%!Wa++kFGsuRVHA z;z~ozuO~m)DW$rS6XU_V$w7p{nsqBQ(c`+-#nKR-Q+-cQUofhE|F3y@2oIBsK$iiS zNLvzsliS~^8dfuR?RZFYKAC{O6U^F{Dd@PoMj*tPkkPAwNgmSDdey3YreJV=LH8UYf0m7Hh_+(;iW-0xKRaDS~L?mShM0_KZDCpXdm2Y+|3Z=05_=d?1r^ltKk zStzBffc)g2J9oVU8Ns{IE&e)RC^A8%NHl9n~SEW_HP4m-x zb0DbR(6uO1{yd(^VtsApeYQKv7i)pADqeXi>~=ef^s=ZEuo&iz79=K%wh6AAzT7&ITMKc?i+{|1DHaV*UN`T0pX zx;cB>JoUgyxqI2CZz*yD0E`8i>i3>J`>>f2=wZI-+Z?hy75uOa7btAPc6BW23YH6c z-TEI}EwQf1$Iy@H9|fAo*RK(uHR)YFO;z&}QI7uKKGJJ)rm;c7KpZ5YM9nS$&kBNv zL<6Gc?v_3Y`)r~4moPP?@|n8y;hyrA&6kEIjs!cyqrbhoTe?S64M&9K(1e5pUG3XR zgA7QUfV>g^V=`K?Xma4QTYPrK@$Y-u)d|K(0?W{ReQ!5i`#N|+-5{cr-sqzXFbTws z7Q1<(5s4#44y4_-JbvIpy~EW2vQ^ z9~vgeYE3YHKn`ugsl{06uR6;tmcs@2QW8)6d%{_}{h-5c(;^P+p9QUY%^N9?O7P0`BXCzIjLJ4E@@?ivM>c7l1=Y_{a-| zD3_ocox3BYZn=Z6Yp%}HOaOuyxpXZznMvqQTQP^uZv}OWO)eRu{Arc|1BP$6gJ8!Z zS~-*Z7}C{ly-qQ3~Lk^+arM^Fn_t8%ZWc^Dr3-VuYL9Bk`;lH`FmRwiY(T0Q?b& zrn$9HpH;i)I}*+to_!T%KxN}tq(_FWcuKpyq1qLQg5lvpG!9~7DpoYfh&EUIK56O% zkw5dLh|7W$AZO?%ttk>Y;;0q{oWd}3S~0+X6AH?bi_?yRC&NV zlR1qUrpcG-l8mKte{|c+bg6a@8X*8B*Ub-AXYhlH;L#o*NJ_h^fD!#c=X?J!?-CV;zh#-#-gjXQ$y!v5Q7@qp;-m#(r@=n_E2a`$Y8>9 zh#W3l&m0`avl}-$8VoByN_&c_ZJWFXEocjFU?C<=Ie^>N#&XP$?p6v_%Ye)5s9n7cgBYt zheF#TMhYu>;OpoOoYg7xRa{V{jo=8D?EUq}qooEwUTgY>&XlIWWh{KxOZb`Rfj@!e zl22!~=7S*HIT7xeVLb)aEBQn(9Mbu4N%Pv#b~$>1POz2OZg2a@2h*SLA$Z3B8$iA}ovv%;-SAyM`CG zJWz`LN=8}*NVkOHWUIiX%! z+pOFJ1RaZc=aw!JwkqgN<`EnE#PO6bcOw#CE)wnitetzDshnB=acSMPurDQyW%5g9 zs?k!lg9NOMha?k5@8n2=&;?t*th`ck<%sT5y{DW73NUG;GGD7b%DGJBalkpWLJ1g=cvikXAKmTDGH5 z$1=@tl5)<3lL9J4h;SueUV*b`Y^w)UVWB4;sIB~?Gm;%pqb#7}9T_uow0>npHaZ`M zNCe!Y`gLC!QS1b!MqBd;7*SBB>%?JH37DVf6ptDW$`wKPXh|Bh>TK48c{EqPeO~p* z&1*Q!{MoaF`bV27cO8Gt2Q?tfGEkyonS4G;)!f;>6Oy=7B~M8E5P}GfNfe!>!#hY*!0HakYLbHdP`iG-z2fZp<2=8UNHZ zTzNX*g$FM&ww1{;Dp_M9c8w>_OSjbSSm@P>W8{=Y`BtjygD~?qzF8c>Hs5nEY;&Br zLSKp;6y`0_f3HhocrDRdoOly49KGL6?8?mG@%VMa*fG#MCDqfJ>FN!)9JveMB-`u& z4Mn+=te?Z*E)uu-2mWXp?TOi>EwpuFm@#<;f15v1yMLMO`DyE#aT^Ex!975oY0MDE zbHTY)0f-V=)tS=LJ==z5vla)r{nh}Dy%#OZ;#^ z0M|tdEf|}In4;9=iZWQ5tsPGZhV61JVh!gPRw_pq$q?vnmK1hnlrLho_Y0d5_@MI7OBHImg|L01cyy%FP;s zxm-XhbA4K_4*$1wd>DEm3z0W;CF9kx zq>I6(XrU>>t)$t(+CNSZNY_Qjn)}~%(3vlAKLpp$Q81H&`x9SGd1@bET=r~n{Vgo+ z_GcyV^>h+we1vAJuceBNbf!w!j!591GHw{xV2{SzU1l`y7L&_lJzUONx#gGKHWYom zU3_;A5%q7<3L%dfDhnC6GV^%{(FGB8kNpN&nqX?n65U5qcM2JM3l-uUL=_cO3~6=0 z7cE19A(zY$V>|6_%fmPFP<%&cJCkIl>Hh@&jC+>L#zfh)bNwVKLh|6o zZoZ*MzpYt`5PQ*9V#bhF2p?U&DOtERVNdwtQ?Y&&@UZ^tgC+W4gw-6lIMF*izu>vi z?P2y6Jln%20E`HW!@T*nw})<;W*zdTyMLZsj|<7tmQGm{eG=Fdx+?Md6~9`(`A9>| zU-gC8wC63k1R1Nr9^vkWHd4muuUobX`xW+joMhptmL4hr1M`HS7HLOi?r1q%tra0R{QTTlpCD`CIt62?Xw8C%xdJRU%nd|2WzL*>e8SaYYQDOWT`3=O@F)Ys|bgkOqg1)-`dX7@zMA*^Ex`3 zkcb^&(o{4#wpoQ%+q~NL#mnC*#7+8Ft2ZQqZ|W~pXz(U7rqaiXXP#k`&we*d?E<5~{PqTBX0VXBX+%Peba)WQsC7*$x_2^Q!6!_!D4nJ zFmD85#;?t9J%NIfC9V-A!TD(=<%vb94CUqJdYO6I#mR{Use1WE>9gP2NC8zld%8G= zRLpsM*WWuNQ0Caj=RK1RDzn-N4Y(Ql_u4az{jk z+EJfZj@qY_UxqKv(wHV^l6bE3^tNh7%{`Nx!e43b_kI~JWcBU6$IJgs7cO!fkYSvm z%uvjd(8q8_u)&haz@6b4#ey-<^8|&z1}PqtbKGp*za>)ou6@jVSqqD)Z-bV;51Nx( zkrv>abtud@_rlKEw^c-ryDt+Bxnn;s?zXMP_4&N7&C{=)+n$=3dq|fE*X=xVpS>e_ zv8~j)l6(6nm#_ZcYv+~t^WM>elVkjhL&`R7J$?E9waGEF|8Oh*ocsRP)z%}|=Q}<1 zI-@^zd)>Bb+1^)tt318F{8PzeMD_R97dsn1I9{54?Tnx1vEy93itenO*YmCD?#%D% zdzVz@ygM5GH0w?Fw>x620`_g&?c=tmm010{`SG9ihl}?Xx;AfZWeL_!TvM`r9{;h{Ln*~( zT{bm53TNFWE{xa}eBS->rC!dXQHSpzj;P)KGbMSo*`9hEgOVRI9%>rT+#dTZHdfU< za@5JE=wV>stoh#dYAmho8`mxGeU!9tQ47DbjNyOFuN`dH_v*gPc{TrMsbAO2`qDd0 z)JqU3iEQT@k@@C{egS&%IfrK0u6$Rn;P1itjpJ9ep8d3{wvee_Uu%upJX?=_=lJDb kzfNQ2=eT>kpah<=+`f6j%q^>3m8(HqPgg&ebxsLQ0Ke0a2LJ#7 literal 0 HcmV?d00001 diff --git a/admin/assets/images/icons/import.png b/admin/assets/images/icons/import.png new file mode 100644 index 0000000000000000000000000000000000000000..122546d8ce70e8350bb2c226860db942407c9973 GIT binary patch literal 3671 zcmai0^ot#p~M!0{~U?{{(arziU7_Egux_7KLRZNIU~wjp460cx_hA^ZM+i(34~~3#N(= zP)1HQeBBgnAPLU;6HBnRwN0auE@dlwsc#tH|I9a)g;5p?Vi4B@v2m>aleg%ua_Zvg z5&CL8PwPw1TuEqX`plHZ)Wt8lhwmJ_cF!&%&pREvqHdXro8qS&iUTW!SCl%I)`G)v zwFr!(&H0Mb*>n5r651mT==Zpbd6K7g2&pP7NXY6NH85@p>KK3mh;C}bA;Yfae=NdX z>)r0UOX&x~Q9|=pwo{!4P;j*?`po>o2!s;Ga3VQIib=D6oFt zX@iq3$PKys(2Xy~vd!Gd{j9m9R%!9KIsZa@3Y!ltD#>d`01Fb_5=2X3H?MvE(&XXU zkcgmr%7oUWJXj3YuUNJ3Q3uHq?rU6yqYQ@LVS1q5s`DqZXrMYLM*^V-Q0ozSsp(J# z2JLYrU{RgcZ8H?8+`P4++W~;4-`6l^LT8l*Lp{K<5|k)2pc;`Vu_^-4=@5C@nbBD$ z;ORK)7g5{T08+kven=2YOm3+?V+t)Qw;o}$mA5D}^VNn+i71c00dDeY-F9{c2{xxn z(2E1LSR$`3D#p_I9yAh!Fr%b(1M9mGB#xQUa+Hd6#bYet>+tf~GW!e`CNxX0ee9Rh z-*M}&#`?2`beB_=V>xp&DcJEhndLlu!6uk>OBQyd<{Rj~rsv)H73L0NIu z3VcUaVg6+Bb?r_&roo1fVusbNwtpboNX?%($=ck<@$uABIx}t&ip0fQ5E=FC^gVA^ zzF1SKGnAuDle%J9R5^_yq>~V?=TXUU7Bzq0M@JH5;yEy3C$&7)DlB zZJ1zDL1Zz6K#7zBKeM9Ydhgi3JDn2G1vp{0?4@wEVjL~uuc(x;Bzh`wWVzpELCzF#QjptW!g(}+yYG5a0yH>c= zu4jqD4S4Bh>?({Q^<9iCD;2b%HTny*%fDOZ%FIC zbO=cl@SAWwOpMbOG}A1IX{e33amXq3pximiivc!d7dR%0qC4ez58={!5&!v=xic*Q zoPg6uf31p;Zre)rEM+~C8e>LNhxC7HjyjLn@l&R;yso*d5a&|!$=Po0a8rQw(j*a= z5(trngG` zS6)wi=0@!uoxP!B!1L;}xSFu$w|gFg{O7O3F!)8tHEEu0>iv`Dm!jmw3;6wi6}mc^ z42N8;hF#4s;>g+YyF0L#91QB3Kku@aL(az>bn|1Z6gm!=pfRaW1i~O-Fgp%KCaH2V z`m>kFeg;7!ms>prco^O!IU>EZHrOYI2Z;Wd(xlGaBn@?kcl}cfKO^}7|ANi_ed$wK zEg1jE6MOa~5jO~TsCa!bIS1P7x)S9#So6!rpiEIA7c5&)a{Zo_7TXudDfZ3IjX7|E5lBi&>B}bTK zWSJlIU@dlQU1L&#;h_;p;fZF=n+ia%FX=Sh%)`2u{y`c(w9>z+0{D)sj7QG?Xpv8t zu+1I+I3Ro(UO8DM02(*aGkk&~Xq{yu@vm|bnL|>uG;t?I?H^zlj~JkH%0~%QkSjI^rfrErz-`X z{FrS!f_rQ*;UJ903@ghPig?AA{4N8%bnav{D)YzREO^9XgLB_~+I>=skq47grPyEJ zR*6ko6kk$P;I^djS}^26u$Yg^g7FZUtP{7^`{_cH`;CdJz@zyTV+VKt#RTHec z6m!gm^G_Rk-eqm|4rn5L*nzdvj`8$;D_h_3SG#ChkUs*!zrmvdQVmo*92GsRb<5Q1 zd{fqI$JM*4jbxmyOHhPvBrFvP-D60-9O= z`}OzP6@{mg|J;upHHdR;H$QmyhAG{RCoZalwL|T4?luzYAEf|p?%ww4ILuA$nwH}N z`2r(dMeeuK8!`dDe{&{k6_@RYOL^tYdUvAxN}hah>sb0)`SPJci?48nPCc|Pt8z5^ z`2B~_NZ@t0so!-G=QYCioe*&M?7)@cO0j{qW<#AcE@^V&7C5udJZSC|)Ye+nzP76@ zD9jTb18{9~BuMXHgl=Vy+&IN&DW(5>ap#s@vsFMBK`7j=TJn^5SFa6cy4RbX7leMq zYVw-7d+$DjmOU$0$1P7q=w&b^-to`r(hWD`TPu-F0vSz`!Ts&PUZ-fw2_Vmgw$SMv zQCe1>?^X1RvN3_w7H~XKHcf~G*cGK39w4Mcm4@YK_O|-JH_q9QjY0bA_XTZ~G|_{s zL;W`AWp}_qpyT4o?le z`?3^3aIy>E8y*!y;?Khaebqix4b0@7XMDN-F5_tK4uv$`CVT23!Z*U16$e^wBg}xa zP8k=AGUl|~S{LhHc&p!c?C4&4Pl{a%?{#DAhONZ^vP4@X$iCy9-GuL?SK0kG zX?KqJ^L-K0f(SrjX)Eo+@y_j$FN=KhX}KR0Do(b&N<9mh-H#HS{uAS+myzgw?=OQC zcqBDEp)2;b_1h_VBYt-ihE5_>(A@Rr|IJ$VY34jcQu~Te0`XOsiKFV66gsJ&*gv+a z{>?K*R`WY;<*m`yA0RAn5#$uuhZ$r22lmx{gr`7dmB*lrgLL_D@M~SVe2r=ahV=S# zV9oSfTZ?kmJM&{~{cnIoQ+4wtWQ4xVq{7v7Kt9&I!Nt2b#gZ@%CLO-_*nv=JJS_i3 z+FIqx7qEORQ+;6I8uy!kkC3t51=rYiZz|dS=Vh^CzZ5l{V!O((avteiSc*PC291~F z(#Ly2=_~{`{LpREIM?S~BhVIwe8f-X@f7k)EVI-+&Mv9uybT#N2aqw0u+6)p9KU^f z>l3zo#?k>Bw3yO;@2LpGwYR3Z3f1C_?YyI8FlrH4q@Q3&2)(S2DsFrdnAWA1Z3bP) zuW_$mv8)fjq4-c!RHMk;U9oDtEawmqiXaPOAkiZ4Bek^jVudjW*JBnZHm@u_p1Az( eF9;1gKfaU|e~rEDV@2_ \ No newline at end of file diff --git a/admin/assets/images/icons/keys.png b/admin/assets/images/icons/keys.png new file mode 100644 index 0000000000000000000000000000000000000000..e0bafff875f2f9d866513352ee2027070d15a264 GIT binary patch literal 2874 zcmV-A3&r$_P)CSw)bf(r zNU60dHX2AdO>7%!ZKtgbwMm;)?0dUb@FkX7+O$bq-;ye*sEyjRyhxF|v{Zp8Z-b%- z2po5}-0eL5V{hdOvwO3<&+P2XJfCDkGBf*|=lFi^nYU-26H-bviZP9$z$bx^0po%5 z2Yki?$AP!|KZpANeG_<&Wa?lgn<}&biZG3ffW`fvD}mY)E42gb`#+mWrZQ?8Xpa`4 z984nuECcQWE_PuLM}Y@{pOH)*ccH!(pkz$rbHFO#(hBVDDDV?tHOW-B%EDTJVlj<{ zzz=~r9_;-Pa5u1)WU5a^K`nq&Oe0G6xA2=@HwE}Ra0AKId*v3=0yx1mh5>&9uJ*bq z1K0;#OET3~PLYVB#snABxER<%b%hVm1pEWjxS^aPT7W{B##Lm)cjF9S1@*wA?Emtr zQVZxqKt4?4GT;?ph}TUih?T}V_&B0=)MH}i&KryUy6yq&FpUYoU-3!!q~Aq$zOh8^ zm|pY=s_p^K#x&~5o@Gz>x+w)25dH^5<5DzESsDgC2*nFjmS$b{GP*(HkUnz9LcN&05FYa;M-m|BQHgU4?xf15pu`0 zL4PJKK$bg!xYv!arIhgdkzEJBH`q2>0Mn=gZm!VYP668iXAg182p@pcN9yHkQ-4%w z6D+e(TM?<1V)*n1(XzSAc6IrC@G+(_7WkCns(rvc zz${1M10+*NNTzNEmSCkjl!XsqofcT>+w-liQL`7{G8c#{uDOC_>fw^UnM^${+h?C7 z-SM2#7WJN3N}hWh;qC!ors8_fmmGdSqGjtw(J=8TRSg1=>5jbP7Q-&UWeRJZ1a4DO zDK`9i>PDYbQVXE(L`=5Lwv}BAppKO!Q~OjD5-pqhM55^?gu-}a<`TQb;T6C%#sFg! zR{XQVT8OFJHV_+eN?AeKH_>i!cm;q4g|+sPOdU~J4*(Gxs-#B&K=w7;Ee^8)DsTO6 zQ&{T^(S*9XvM*`3SPP(re<`eWhG_g*Wwor{v385YEI@US_p1GpO7Wf zqwZaVNX&K=NtgvtcY@`b0A1=XQ$>www>Z24BvXe0Rd3ohV;UnB)%pP0d$LJcF;TPE zZgF@8$g*EyttAR;A$QEUir!=O$_k2_J$8%3F2G)ewSJ0e*j{QCi`;fmRCXVD3~8w- zmqkQ(+AR*d04lHFhXKD~>;kM;TyGJk(OOOc**0r| z?A&vmqV@nF>PG)3-@331um$K)R?SLd9sJ89a@)l(%J#WM-)NGrOw%IeW(w`;MbEWJ) zI8S--IfhI)7@2e6f_$sPJ|qm_NwSxNrM(qYXaJ|Qkf+|70!#r`5Q!oGIjW0HcbrUj zMS5RRJMs?&*N46b05FZ)f%}X8p32d?ZcsbwROE_|f{(q!eGYJj-;kZI)j8E3J|Gek zelX}Zx(DdTGz{REj<)qd_{7H@i(LNBptq-W4{!!&w_W`ZJ|N=bzvXB<-2(uou@Wfw z4jtg1@Dms86SMa>JK0wE009f1*zg|W;~h_98*TxD7Jf7?MZ?6aMayQ#Zl-$@GZw8&@RJ zL`Kwqcr7&zw~LldN(KxDTL3r0kDu2e5>1n^dcGvP-(E@j-QndobPz-oQU9S$)Q-BY zm}`7Tr2~5v;9~fqW%K*FMRLcC+hqFi-E_Y_4ix`i#)hXweB2Af8t)PGv+cN|$zssHxA0+A+JS9q|h>ZTs4iY7{~61n?mIvyts{ z<5=ldtn?LFrzet0C#BUrl1%pyh**ed22qniM0$zTc8Z$Be~AuxiAdcOqGikLPWGXS zUIDoL4GPH zK;^^Fp(Q9lWd!gp{2W;cekVTRWaBX5z5w2bpQ9eh1}(w=3@N;A(SSPMjQOwV%7ohsX&ruIN6LtyI3gDmcbJQck-4m!2z<=Rqku-!^ z0yP5oHT)bo2_6w2cYkNqMfn2wJ^UP!2BJI@HVMiTASmJIkTeisapiaH9i@*42wM1A z?xj^4UhUiqO6vi1_<^B#0d)9*qBsF`_<_T@0R9R8bzq;vZ9^SCI2FJ@;lBYa0Om8e z1fdTfoLmL?Df}81uuNwE2Jkx347>z1*=(!B@27AN;Med0TIF8sdjcK)0P+jqpYV5` z6Ml|!CD7rYh5aGGG*$p>2K{zX;g9VPKT|?8^Rf$z1>7nR72Il_XhyJ<x3*oJ96;Fy8a<1$z+8=L^!lXVMhS;5UMjsolVQtn@oN{NUm2PGB;%TOQlKjJ}hD zpTft_?-2Wse@FSfeThjA$J7?pIDQr&S~kBZhE85YG?8C|_<0>7ap5Fx?*1{L`g(gF zwLHbcBB8i$s441cpqTpV^J3VgpSG3Y>}gqA<-MqvV4;>kEk%z95G`9bipEP86HT1? z+qL=T7y+ZF4jslRFaJL91M?xMrxR>?BbtB@jHwa#lc){`6_(sq7Rn>dpxs(BZ40@{>fB zFTwq^N*%r;yqPAhbP4XKRenWvbHa>wGldo_fnbdeUp?MV73*Ar)hw4^b-OuX#QVvj z2a`Z@AFc9^$~7olcrO6tmq2nKx5%$mzCq!kIs^ba34W!+cOUO&3(rUPW>J$Ky;~Xn Y2k4xDNPH84b^rhX07*qoM6N<$f{UwBqW}N^ literal 0 HcmV?d00001 diff --git a/admin/assets/images/icons/maintenance.png b/admin/assets/images/icons/maintenance.png new file mode 100644 index 0000000000000000000000000000000000000000..bae6aaba060b8a5192f80fa0311c966f0471ef68 GIT binary patch literal 5001 zcmW+)2Rzj8AOAXsI}Vq9<{2S7a$%*Z+CFp8ffJp6B&>eO}M!{eBY7Obi(5x#8L%*A|hD-cCfj>dvMs@KsO*PEKJ7B?_QA0 z-2gWk|3DA?t{OK0u+pOSv@OE(cM2W_@P2>PtJ(7K;hxw+nj3D%;xfH#{JwWTDh}=b zWC98XN%xrr_<%xkM_!p5rg6lani_!=U9p$sQtYF#Zg@qk~FiDdi>GiUsg`vLf3jABUh3D#{8>i!SqkoR(emsYDJBc#7 zL#Z_B9^mM(Q5!~P`9HCvt~MJm52|}OrK!KFI5v3orBE0b;m@f$iJs)`&*@S!FE|X= zN+w^KYHbskUCecADWlCdG4$}HLxJ$vAl~>()YP3!PIvq{dzb3p3IYl_j^*~9jjF6? zDfj_X&esZdutnxYpbVtzffJxkIS&0K-FjDP+PC+jVXchKju;Wm!+yLH((xiE( zEJv6-IeJZ;%1lKUHjC~YqDvLKA%(BR-hcpp;AQ@y{dK1-E9>W&vS8Z8f!DK}q84KR zVF2X|-!+L6A^?cJiKmZZVcIF{UAgC<39F)N=r70>0SOoEx7&p-wz*?=arV!k-XVrC zwn>DQIb!QNzxDS-GO+K)XCxw|=Jom=wy^U*`qj8DscL5T4?5RBiAok|(-sPK5~=$D zb&tR>k=Tp(Xmo%se(`0Riv1?0FT~>x<=T>?2A@B0h6BEGZLluzf{9Il;iFc~gSNlw zDdiXZ{v0hCyf-p>+OeQ1rxMb8pB}rb5TU~1-sdgHj5Nb_P#-b6Yx-uJhVZ=W>pm&W z(bUj*Z#k*hP~nx5vuAp$Z(xaoWP|14^+_D*VrrElH` z<17!1H{#m^t@3t65;iXaAWhKN_^0NN%~i)8C&WGu$+(?7W~cRQYv$NXE&at0KE!sW zSc8lQZ*4Y@kcu^9drf#(et+B*UU^Yj!FoUytm`QMgFNIHSmq#uZ1R!YA}4dS?8~2C zc}*Z2nQ+L#ViJGW49jk7>^r@zH5sOp3x4z^AGk`n_MjkGdbNc|kD;}yXc}W~3E6#g z_xO2&_h9qo4k2`%Vo69EFCkkOa)x5!I+hO?J}|ZvJn7{O6(CbRmJ<*`57>Y@y(q^r zAxS(yLL0pro?J?RV@}B{ybqQt$v~Qjo3!I3fxa=GP0?D(nJ0RIuFh`#y$Nm~3`i33 z@~{{mRLk`L1(Wa4b{bPmp^&#>XA=f+SQ3+GFJ?TAJtC#DA`4G(ppVuVQ|nh&$k7uR z>IEfgYipN6Z72V)^AMz|^^T`65u&(4lzdWBz2&vw#sf4`Tt@?qUY2>X`Um}nj1thY z?CxGAeU4wCSc?WzW%hRP4l)y|%LvWXydb2IvPCtZO;QL5RrFuNevO`jB;9|AnT@BKg3r+N9 zd2T9?e|lORkvJO+!5Zwa9i@{20wHqdEaJ0%Nxt{qUDxi_(cpr6N(BvhCe~;doZoCq zjf;!>_{~+q2-siQtJ?gvLMwIt6>H^=T!EyX700OW6L0%bAE`WFNw8UOsLW(CP7{+V zB({Q%H6r7X1z#MBzASyPB@bh4vXZm1XrdNpCXe2e9n68UnZmn&oTW^o;DQhW8R&N< z{FRm6yKQ~C+^UAF+k`oIC))7n{q{0~yB-KX@B5ibsH%>NTITPf=LTz|G}-7goom9w z9K*KINnyiV3gP@Z$U~OPeZ;|9ACa`T_f8+o%QI^FhsJDvzX0~NKsL?#o<{q$i=ai! z-#(oFRewJ7)Q#u@pS{k{2s0>y!Tye-@s#}%@$atk4R@C|dqzjCMy>$Z4kV9h_U-5`Yu-iRs=mEmlk|IS`eMJ@=m3!u3QTM%;BxK zr+>6p`mlq#kzr05C$3&cfrfq=`W=zcxH|u?RCkZDXXMDSRu536_Y~r+$%(K8HrAFhj~NzHa+ZU3=O}<8S}brr(4cu{+=9 zd}z1XE4(+-3cv83ZF%@ABO0= zHZv#U4{UjLX_Z zo(&}~HqC4ZbnY5n-{9!LU`37YQS}&2v!Ii@Hj>-Rl4##nVujuv0&54mYI6F0;QHK~ zd*+(|QS2k<-{u0W*6Jy(t>wA2JuB$LF3)tH|7LJ}=S*@PA$Ru&rxIjJYpf4r&aXqN zj*Lf=VzeaFl|KqX9@V~J*!A~`tQ5L&OE3GC9_oU`ox$ZVy|`lk{(7Nug@{+W2$?2- zzk{y2LMLI39Vli7SzhZM`R#f^Dw~J-yaJK07N|~xXl4Pt~K|m1k zDtt(izHkL0mol6Z%sHmLomud9;l;z<>bM;L0bNL>;tCwn0juz=nRdxOv7y^W2Y zTV33J2}wVJ(pu8KgHu=t&B&bdd|spDfGKIpqgpI!&0XN@04GVkbxF@l05tvOfEy%zN94-Y z=whB&mb+gId~wm892B8)+#6EAm5i0ZOj=0tR$*HeuQCZIR8KX|b*lzj9Q9ij*3OcoiH^hqVU}VXy&e%4aPhDY8r#h4+#O9db+p?Rg%DYa3BowpG{t0)n~O zPz^Xy2fiU2^grCiw8;Etws*8YCNJv2q*8BY_{o`>{622sH^Uk!;gM6u-9sd=>ecfg zX;lOl#Jv((P)vQoKK3I-w_;$}ym>skSMegh!v;sRi!S?t@h}*Ny1Y@E$JiOQ3}#x85C8{dJJ zk`tEHO6(_D746I-WYGKl#O{7|oP$*QIceR3I942c1UA>6K*)R+v+r2a=W??_gq~s| z*`p8gb>z~GAcxMm+kalJzuvDutkHeMcTxj^cvCMjKQ_)(Nlj#&-V0kSSUDbdXyq3_nV6vtC%*D!A(79P#Mew``tc}s&n1?Y< z|9}}6v3jlZ*Jh^Qx6qv>9l3#%ReWwAx4Sg&+!1fF>%2h=d>C7G-u|d^3@1s2v0`1NT;?8AF zj73f{-DsBBdzgW0Po@V4IZO{~y(u!3el6Oz?A0yzG_w$V4GxynR9dkRhQOAJ0>X!e zr@DMCOz7%#kkmrNtD4|5`_;`N4T*B#Bit!mtR+iJw~gDLs>X?UZaR++;X+93V4%o zzS@5aJ?;lRMsUT5nAfF4cMd@y-GnY{v6F8F8|MA>KF=2ombF%Cv0g+xA)#ol!sUc2R;;9v30Lkhtxwn<(j1)`K{EKq}YY+un46y5C|jM&@Ag zy-5E)k8yd$H9tYQH`xV>9O3-;(B41L!vL{VeXn#FWo9FSKXtCLqD&K%BShsD6O4SXd|?yq(g zz-Rp_<|APZu>ZC zsAgeoN_yVcgnAI2GKV_6Qw%5$V)SngBI#&q_S;NAYA&h)p8 zkJNqrAK2bZw@8=zecg3Y8OcS>Po@1&lY)&cZVawe-)TFEFqQPKEs;%O$HTgq=TM3u z5a5|bE;cL6@U|gk;;kDsiC+2#uS9j;F@(=zzHCYq*$@qcp0Cy1Zf&W4W4tb9r%4ES zbfYO>vc25cDAK`oadWCmyC}g|*Ze?~?-&@CbIfuR&Nceg`!(^|WEZAv1A6OuqyA^< zXT*WmtmIz}#F%dWqQAq>*;!fF_Pq0mb9tbe+h^z3MFMpx&~eOBFEyh@<)`z8UX}mu z`ItvGi=~*(+dlU3f;IoCDB=m*n5G=J?7ey6^z^vo9_O2VgvYf~Jma3*(w3GTEq=eg zX6t=|HPg?C?oHJ87!1VSn%J#LlR^|*99jp81nVAtuZf>bjB^D!V%FCA@=ATBG7Yn{ z40|#Pa-Ni|6S(=UEJG4l=zTY~{6duW$QG7=%KP`S4|+N-aT+rG*u3CsIi8WDf;7FDMbLX5Bp{=PzOz?yN000oHD8pVpXyiY^!+Dtdq|HVj z6qXxQ1&;SH{P7Us4>rEDvY{IQfa&>9Fao{>k3B5Xx+@sC>o{4tdzraf0=&Gu_-!5S z+$_wTE%}{Xtuv1#p8x=lMO0vNaPO?cY$JDLeII03V6Jds8^XxbpSYG`DF|u4Uc^k& zWX>TA1+fVkxP)9xxi?Bua5e;Bw=PzHO95-+ed4DC+UZ(-1jnnXfI`_`nyy-$V6p!rm`g`eU`#5`*`LQfv;ooCdqT6+oH6GI09rqnq3rg^h!0V`CPXKnE znjBKH6eOFY^9VG_g%`g~fi>h-4ZzsT{`49h!E3U_fp~R^={~3LBZ?MJ#tEua7jY;2 z?zBH?L&Uc~S~!0r)yoCTfYzQ&{y{lSAVhGO(xib6pS(Ikg^#Je;lN}tU;4kxJ!vt# zoGVceBX_92io^E6?gu*T9)z?*7T13EuO zVan5~Z@a=zcGy0+17!v>N~W^;YOGmG;KLdickpJE#BZM|oV8Z29Li@le51Lmv1((AM?ncZH_^OxbMSPKWG^R)xsDv%2+k zzwTohb+(!I%T%D(jW#Nrv+D$w5y_4FZa|~0Ksq`7@d8=i;a!|TKE_8}!~_MU*DY* zzN4VBGrW1Gdo2@uEG7>6{CsO@etj{O6x*K~M-DeP zy5ej#-Ggg;gKvMO%$Ki!bDWA5IQhzkK%eAp=J+x=5ZDlSsb}Ya*^4RLC=-fCp77Nl zly_-u&e^yfvUdNtrhg%kh2ukingQK^cLR|jP}f2>P4YFGixsxg8XPIf%H<7NXK!jr zr;mvzY%Pi5q0jQnRq2W~-dE*(t5WcCJXUT@+rS3@-c9%HuhP$#gV$Z#sE0{g9B3Hd zsNcXz7;O!^#>$%M%80=HUEY)lCGmF6wN`#wLL8bJ$madN9J-D{JYs@emZ%HgC+)VX z&`QGw2b-BfleQ{0iGxm@Ph;u^okA=-ZhA#r%L}(^qDL{tNAHKRZGL=StPp*~be^_p znzaCZXP+W-J%vz$W!eQ|{Z$A95+pjVxzv^nchqk-Mv%0^k(aG@eD;b9M7)HeU&jhr z0(W`+ek=Vtj1sL0z}u)*A56wrRB*4fKAbOrm`J6yu<~~=UDVri>eW;bvn;1naSHdx zzc3t+GZ#TF8E2Z8L*kf zkbZd4c+uOaQKDTtR-NLX*Y6yf>TDp&f^ zk8OohO)r?keZC2TGYL~BeQSCOt>5W?G^k+vrlZgy!J6V4P6Z^y+ZJ3kex}Pq{j&39 zhWjWBfNx_@v*hCp`#^G04y&P|rN|-l8gGk>S!#D%;y=XoZtGkQD_9C5wFXPyYm0|X zFg~*s<1KnSysj+dT$-SHGz; zCZ)IXjRIZIRwq;jfr_(z)zAFya@U~uA zTVHH+3x11Y(CKJ- zLO0;qL4JmY_$Ktc1?JF0GL8Z~RLwMf6t&}b^Cgb+&KQI(HvLt%OAX68RJ65__){ps zrb^a=kPj1OFyM{_jm%2s$xczp`|AwGjH{EaeszU$C^z+o836$WAzoRQ2UH<_(e%6e z5hb_5V~E|=_uFhz9D+HFj<_R<(WG!q^GaOx-3!9>FDzmuA1z-XVeT8eCfh-BEXTx^ zD7cj=@!HU0#7swovaOzT;;vbQPlj zuS%-M6+6qO?a!pr53YLcb>tLPX*|MCW$F-p(!?zCH@v==MGv6~c-v+?g`d-!s`cPq zbp^EdTPfr;rN0&DHQ6OwF0L;|e>Lpf+LKHC1vHke?HH`OY|5@(d=2c^S0?MEes!ff z!sYwwKEujzhv%|tr;(znWq7R9ha`U{&lEjofhBAaWcD>UJ=Pa>#F``Z#VHq}ef!Z} zRYMJliYj${QOh)V)ul1*X^jiccF%1=AjhF@T~f5!EDBj(N|Uy#(b{E6AR zAAiYiVL9Wx@?@Cs1lB>G9X&C%u{+#01SpS$&C~s>-^aYKT zIv|QsCS0n4nih;XkKA_uUIX&bl2FtjeG-nzE!e%eY8Hu! zw2)+h9z$P9GPKW&(86Nm%y1_8dH@|PV7*Eh=^%EU=g_a1|3 z{UgY!Y(xf!GRujRhA(K3^rvw>&NAD!8T$>EHtF${(a`j*pd_HDanoO(2g)}Q;x`}H z>|~6=!q=DyyUy)qJ)cq~^mISEGn7uQQq~35+P;&8;-t zB&rmo;yrRObmMuOAuhHI)*vHXhg(oB2ak1`tz&YK$!altWkh4n{J^ZWJ*knVWznX* z<2mcDW{O?N;_Nic^YQ@Pyc-OtsV)y!MBom6sG~Gx|3|DzO#OyuSsFFYk zR5=9NLw_XCLan$tK?oRP>6!CBJ@znWF0$gQP9YN>Wk;J>HHdhR8UN&nU5HvWs5O%5`;C^6rs1 zc26NkvNx6?CP}OvtZ6zSsrFq40Kgf3NSf{G=J#3(NT~zrI^78V*F&`3_bqp6kqD3X ziI^SLdxmW}N(TV@uJt%E0@1L_wC%*81%L)$_n+I4+MsAlhJ1P=-$|Ws`z885qwxP@ z72oXf&EnObG)#fU!|f#h&2y7urNOTBUj%?5;}FBm>-$?3ChfuQ&zPkb$g>!+6144# ziwfi_A;_kb2$t`Lrz(#c5q!l6Oro>%OCt{n0!bfFoFX!LIADI=&T#zsZj25^-mBCRl&|l_S_^sc2Ki})V z6zpKCP&UOX6#!Pt@$}kQ((sKeOQ;A@^kwDzJ(Jbx={zty@_Zy9PcDJsL4O`CcjYrh zZVzGmb5xV2;2V~ZW7aSv$J3|%;(aTI9$C%+J<|zC?p61ZcGkDYJFrI{SYgHu8q;15 ze|u>`g}1pv>&)5mg@@SGMurs1em&x%JI7eDj3*=#U^Rb7QW`la>KpzO&vuhZk%fH# zQ?wb51t^cw%wBmLN-s}DwYQgEZ~q$=N-xCABp>g2jV>4G!qvBSwKEgi^|BnbLh_297rhf3kjVRqV~*K zUAOmnOo$ZosnTC%#~tM#U(;!TH!CO7WwT~(-7zm-i|aw)pxg= zr9ZCf0vL}^d1oN>2Nk2KJ)OyZ9=>%OYdac2>M#GJ>Lmbfh+|{?&I$(jX#O8X@&9j| zUIy!r{u|*FH_)0_J;54sY`u(k=DsKk?H;A(7n29Gx6r^p!OtUUp&rWDdLKIpv%}h? zV1`#TggkB{vx;gfetuYNHu19(5P|yogu3i0f8T9)fh>PAyEu<5Sq^+pTJ_oq>i;94 z<$3(~987W$*^&Wk_jtc03Q*Ko_c3=P8Bh!>f_mvdS@|`N`l^Du5NTu>{9i@q{gEw1 z9{mGDPZp?TM`-h)UfN?`%TA5bsi*ta^2i5Vr6}R*fSqOo6%1@<|ccUok z2Lg&btgSFxIJ4{tH+I7jY+60x z(CQ;Dd=QB8{6)2ZPCx5W^D9$Lv3cX4k&pk;DV*x>Lr(BUmV38AJ-0MG z(2L1-g%18{VI%S(5nnRUQISk4W6d#4&Rr5y1mJ$;j(u676<1=XB+Ot}GWp!OV;<%?+1U;{DNyUO+Z;Iy?F${@5F5ySsqruF36Gd9r4T+N=NB fiTU40caICzjSc5tyWV|hG67T+G-1`yH^Kh}Ztw>U literal 0 HcmV?d00001 diff --git a/admin/assets/images/icons/statistics.png b/admin/assets/images/icons/statistics.png new file mode 100644 index 0000000000000000000000000000000000000000..b3aae40b7b3d9ef4c9281bc3e4a2ec33b8c328de GIT binary patch literal 3347 zcmZu!2QVD!7T&dbh`3r1Eh31r(PDK%xFULqwnVh(Z4qtt-fu)FtG6Y3XQP*3bqQHS zZ>w*#SkHa)-kW)E-kCY)%=gdv=9Ky8%$#T~4HZgqCUO7(K&kqtlFqFU`ddkfZ*7mP z#n)R$=7FF zK5W(FYToyfY@Bzd(^6g&&7N-d#*n`xSm-Msw;!0Cs6rw* zE86TCye&9@Mf&VX;`=gaJhePK_|cm5<_|yg#fVkJ2<6gdR`$VljtBbS;yU5pQ)89v z8$J|)N`-ude7XFMz5&a*g3C=wqe2#;S8KOJLhTf_7k}2n$ls?qe7)C#rR>vjRRo*L zrWw8Pj=L&qZDt=o$Z?@_R34YP#_}}rBpH-q5C^4)wLA-=qp2#zUsWPd%45qWfcoO! zC(V+S=`lKFL8abAVQ6xHdW))F2_ck%ISf%#9U8$lnzXt|H!Sc9gz$vysV&AG0?KJL zQC-*}n)Cc_29rC6Itz$ht<*Wq4^06cNn8;6d`TDo+%a{*H-YX{-Qb{Tde6(DdkFfU zl^5}sBO#QRwm{z@h%{Bu>?47Eb!+Zy71&7nD&aUtJBxKZt6yeO75aV`At9hpz}V`@)B zzxtFlY7Y=OJ?0^%15~mY<$DY`D%Dfp-$>>T)P9`bZqTN1Bl(U`!L`Lb4{r?K&%Ii1HGW#NBwDczCkZQIv;0F0Rc!h zof6@oYN$^CJ!1byC2z%2K4pAh2yhyRE!ww$<)S}0Xg2=TD=(ZxZ^I( zevWSF3A3l$xGWNKLz=X2NE!A^nJkEhYH_MmoEX~^A`3!@S?(*I?!q&M-mD}XpJywx zu+~$5-WCH~#>Z$D94pF=T0z#y(ySG-4l`Ln2=+pZN>CUYF3d!?pS@HnPJ-=G^JN8Y zq@V%#ZgW1PkYiWD0MR{qK#oy{{3%=L(+`xS{!eDQyAc8JUz}01!`}k6exg23!2nm6 zzsc57*EN$Ocm1VaJJWrl1LOo~^KM-SL#Vy-rjv%*1S&FLpWzB{Q)|Kl>-Gp%$WCHc zqV|2W@v#p0T*ET*nR=xvoMxR{*6%$_@&92h~4vYIEF%gFVl}zrz;bL3x+A;{)qHnxYGI%Bxrl#-H?eHPsX>D~B z@*o@{L$LKD#@;n>RTqP8-rla=xV||uZf?|eQ8Wsco0tyMW_I(R6ZDx)==Q@uea(!r!8cD-pxN@(5Y3(P3}xdHkkjEFq~*LDLv z*44Gq2h3gZ5nNnT41z(oYI($-Fd;t}?2C_q&;wzi&7d6GuT1ylpAJs;u!zpd%L2LOD%I54HvN~R(4O%mGXI_$!KlsA}7@c8qRYoGJfl}BqH{!kKGJ8 z>^+3Go{;dvDx)pMLW}o=Cs+t^x>n+c^5e3?WF$O7lklZK#k&W6gjl~;I;mqr&3-zZS-`2$q<7$WHfx7@a)$QF#_Q@cd-Y@bRVK`M zjyZ3BkrS4vN^+O2CS7f275$B_x`+?3J$=&eNoXBE#@*Z0zX3RZZTWtt_+g{sjeiS=Z$klE1o>}P|OVjJHg zZ+Z>?N`JeP+(onfUu#-MKCY2t>as*X+!Tm9WY04q09@fG?R4t;5BK{6d3 z^CvB`o^ORrRRw`;vNS=6qWAaGBYgQ7=P z-fqI=cvExZ2kUSEd@=2l*n#4@vu~Ehot|ncEkoD1_I5C#Q%}gr;$iZPIvPmj<;TI_ z1DD>=$!^xJf*xGu_QJ6r!NufAYvuCu`~oK#5T+ZQMTcEkhc+{kJP z(VkAyl#l+e`#)2iS*~huFspLDvQ@^#O5BX8y!nA^T&n23Dm%w=}Og_s!B zJd6VMMB;Ny+wf8fCP$$_^yLtpXHJSZhAb0D!-0vC7%rXkamTg>lFrANn~v9i9_+8z zla^*%H!&1TK37-ha#K#hB<*hg_F$ZDN5w%~DI%l4I4AxlRJ1!0%`27ZUW8h?cGs9hE0?`)>+O>zqBtQ-z7pAXjq6wnFuO z+21q?De;Op!DXVuvta68C0+sFcNWRJ0H_=Nd zn9^>cgN3MG8;%t51d3nyXu3Zfy) zjB_^?P^c#?id*UCiynxLcB6HZCD;rcnhWGcS(cdA)QFTWaWIhb17tWj+3bN{xxZr$ zfpxK8qW0Lyb zG6j695+Y7d+8ov=Lj7LVb6%=1odI=@Ub@B?Tdyue6u!SM=XQ6~Sal4hC=n}@0)d|O$kiKsx@^)- z;`z$Q=vMRsLoWuP$}p>x0-E~U0|ub_LPM$Q`J0e`04?BPt^fc4 literal 0 HcmV?d00001 diff --git a/admin/assets/images/icons/subscriptions.png b/admin/assets/images/icons/subscriptions.png new file mode 100644 index 0000000000000000000000000000000000000000..1eb620f805b0d4f8f9cf7200bb6b757b68829dd5 GIT binary patch literal 4372 zcmV+v5$o=WP)BVfrg!R(7<~(4f8TQYMKoSz!Xu z*U7eV2kR}HlN^#2_y~B}uzC8-ljP$pxO*4{0c60|Rvj&H2QZm5H@z#3Eo`zZzUIbO zVFkk6woN=&Q6b4pe08EA03X!1>KIk-5#dt6@RE0X3)_+wU-ek!285rM4d-zYu|4KZ zOF;l0s8cO9TA3xnT|lv)GMaX>Ba!4dUk(VXM5rnoF7NotpR&jTuBhLo@WZ|*vSjrqIyh5j z3Ia%js+#q?^$~;zfubPF6rooT_QM1HF!;m! z>vV+Ij0FK4LH!>6z6dj-_QMAQ#pJ~m^?Hn#Oa%dSp`lGrMVTG79~tm<+Ih%JhJpYN zuvWFmws{^XjoO!t5T>pCOfPn8UJ$?mj&0)(;FPHS2m*zN)~;1ogG~hi0QEcdYrx%6 z`w<{R_LuOlZVU?o05JIxil@#~uA}gS6&tnNNvFIQz?OC$0$dZdA6cM8EN<=2DDNkO zwzlg12j=v%g1LJaMqBHl&&^FjyyLIwi4jAyRW-n#{Rsx2J4W_68N{+#d#_vj)@^Ul2bJ9(h$%h zA*7rTDhMD?kR+|CzR%yc0B8)P6cMZeUv^I3w-;mIKF4(H1m}ICcAwzZsHo7C>ic{a zz`7s`(c%)Ce|V2$dY@NcJZw7nNSfyTBnUUUBi>ra##9M;`v?&DBx)Z*z%qElDJ^dW03gcm!>-eg zh2HP}=nbZzb{}*o1Sji)DqI)qInm@6JgW@z_aBe<;rvhdHo0g z7}7^p1FuHyM;1`vXWbd)y#N3VDlDsS6yf&-n(QcZ| za${H!zyTCy0Xw4hBM3yPK4Z9SbYoi(zySvJlV*jf^!!J`ver80xHT>apbLZh$x8^o zjM|S3Xo@Ad!b=F*f&h+SXCLkW-i+E0AH+qu{PfY1ejWM|6a;VtV@2Y{2_~XAuh-5! zY!R+58zoD;W-JIG1;-sHtud4D0BfW6#Raw~)5}K40za9vI{Kw+7WXDmG6G2+pKNMY z_O~ZvNg6nA(*~)i&dLcGL9%I=9^1>{_rSS9l^O6zU|f`I%0|iaehUax0IDj~EE@9W~`mME^=r|b9HV(We=widvXXLv#IL=l@O?|x{Lto~jToFWh0SchO5H34& zguInWA%O@$RfW}WPC?~v(7#D#V>fL3vv%xD6^BnzUb4l$w`kH0D}(SS8*JOH*QmjN zW*SYbX&1>vBI8N88ceP%A12!}EGKgT=*p$1AogQG+K}aTL&Ik6Xg@KX3FxC{qT^1P zJR>KUY}dEy7=?!=gTNBayJ(9ie4iMy6Zmo22>vUmvx73~1nR0KQxN-oy1DrPNVmTn z17v!#sN{WJ_(V=_-5%6W{xZ0qT!3;V@VTEdRPhrdCV?`)molb&q&yPj!BM_j09{pc z3(6d~W!O#I+qJ#TZS&b|8+^NHV&w|I{$(3gjWx6~MTJ>F%Ew+Rwzp89v|R5@-UmEs z8r<)8XxA&g1faDwmB9aa$!|ArSKZs@rM_m<;9Qwlne)rH8+Pk>3$7O7dw^p=CV<8r zY_M#Gr!lb*mZ|cTWwUVnNs@9$Pf&OkfUbUH9JaX()YBO{cJp?0ueV3L!n&A2xlF8d z40M=FsH(AbFQ*z7=OdgWFj_m0jx9N8)3Ad-+xB5Wsq3_JlgfvNutb%&%16rXuzdE^ z8`b5@OR+ni^?LLDB5<;0>(7CzFzU;c1JC9)2mZ~0PEl;%%kd=XV?lpY(4$d=X=aqhF_rZs=fMFQw zD)e~)@3?;rEIQ^|pnvvUNy|iC^u(};>OQLR(g(OIYhHAZ*kVrsewt}Px@K{2tfC8n z3xLysVOYuD)|Sl%!cJ_~De@i$i(~OVH8Od!m1&*1jH^C?u6=C)9Yswbz6Xvn=mQ*N zX>3yE1izVeRZV}CyHKjW5FpA*Vr|)+DqEc(_Zj^+Ki(52ul(TdhQ^D=XL9ocV!SR` zkTT9qcj(HR8K`^)%<>o=Y^b7-CU+10F!9^RkIJ~h?ry4W1QJv+Ly9SGO*eq@1$6x@ z#k#WQ350o|1N>Gj$`}#-qxHM_u2$OiB-{zW2sfs_6UiiBacir~mzUC3`Z~gO0ab6R z%Fhyu9&^3mvM1n90P5{D%B*1;u4d6xRVdm27#iF{j)Mr?YW;40j~JM8gWKnN=%*Z{ z#C91ub=8}*Kmu97kqT9LDDmR_zt2|rVdAm{U}u*7aD{D1OLsQ9`i;RL*&aBG8Me&R z>guRF#u?-C0(5WX?ggOC2O=347v+Oq7f*ZFg=rwiYj{CL zd-T~ut_2`v6`z9t4G5W_=zSuRcGs@1S$qO$zp#8dRIe~9>^g(Q+iOfx?jrms+i#nb zDi?NZrDh~k)uJ35MZH1cNdPkLAL>y4C0ov*69S1}BOam0x&M;0HG7>eo-kHa`M-xz6NI z02|Ci-!U?Ia@=diTp}<5s3H$;zWE!7UaFn@=TGfg{_K|0m0J>0)+I|j@IK1R7%Zav z7lHMrb!E*j5D()O-UXl-TojvF8TxqxL6fln=B}SvoGjLn*bJ=7hQk3tI^3GfUmcJIU8>6 zKYP=Rg{txbE^a=6sg$}begbOLvf|F${rGke8T+jk;`_!UyqrlnT~T4Kndm#^Xu0{| zqXl?y>-5pKWxN86=+?LkkL@aMS=(WHTbnODNont!7+dYTqop-9G5X0ph~5qqr!?xm z1=x=0ZLy1{r9FM4C*r9EcwpnqGi=LPhR2%^V0HRl=-l2SI| zM+jq3o&og|U#Css-%W#2N8`;04=up#x~fsAybbO*XLRMM-Np8*{iff6ryAONe}{@{ zcgw=lplfP|VCiHqE&x3f7>JcjT3a^9=$wGs2)v8%nkg*mDbY#%;%ayB-1Rg2CDncv zpEn;sG8=WQpilceU|I%cbn@_XmSw~m1wIv3Q^jmdCZ*qwb*8Ct-K9x;6SjU1+G0rJ zsNNY2E?a=vn`b-+T%JiqPwy_Kc7KNJHC3)+-2KmZ&UWLCy)21c0aOZ{#}U_U#f9x3 zq}1m=;17l}cTnvwd-Ra66Wt16c3suCP|5gEr9cD#Er>mu@&{avAS#G;$jhTrXt185n1^5|r@ah2!$1)agKI3M$VPA6+=N7=w zi$nCo8_Hl-wjH)1L#wKW8p3aI8tuyt!wf?ftsPq(O5n_F;3xo9$?cdpwsFWmt*$mK zOP?dF&Aub!`j@o0>X_ytDFsli)iH#!RB1Vb=-CJVYc*`I6y)!saz)YGH=Q-ZX{tM? zNhyG1x3mlga=lp_4WKh;7K`mKd~JaAGHm0%h<)%yN&zHoD~R{zY;z)jov^M$T*u$f z8Uho0Vk?kR0EUR~*|!imhMMY_Y5?;C!62q>OpV$JuQ+r9?H)1G_0qB(VDLc{s*aD^2QTz!0rX0< z3OPFLLJJTUqPY>m_6Q<>Bk@8{7l7Xb7_){z=wWIyp=S#qnxWscD3a>~Xou0G1yCS! z3IfP;3IfP;3IfP;3IfP;3IfP;3IfP;LKA?kB*LnzfO|p{K(ce2Re>BM#1=p@-q~9q zctR6^g%$*mgM=o4M4<)9NkSBW%QOE9gi45y0LextFsNWnxp6RN1pGg(Plgr7x%?FX O0000#xAKAL(B)HYWBv zjSOr}G}Xy42>6H6AK={Fz&b$n>tFc#-vAI_sEVgNyia@8)cZZXL{l9OI`lB4{zdqU zU)e00xvE&|KzZPx-^SvRsvyg{J?Exsk6wQkdU>WH#9F;zc&R1yhW~|!s~zDs=kC3D z-xV?Z;?u8mMr2fUOl(|yLSj;K%BfUlT6#uiR(4KqUVcGg(dlAVNoiSmMdg{Q>YB5) zb@k^O8k?G1THD$?I?rF|x_Id_ySt~ium8%`fx({yAwoDnoClkOg#(jQfT&<51VBel5~}{90l-wHdm-V1Kl5<@p=%_Y3~I z!P-pEt-Sg887xWk=`N4Ra*1!n7=N(v=G_~xY<)MU!7_kllQ0bjiwc$&ENW1CZ5Q|t z7gtnwM1uwMORTLuKLplntxjKS@!$P}{K97bzb@ak>q9qg4v&nE-5Q^`JsFOd!XOaq zxY`9FXdwW21i-{wxRH-vybwY|o3V>8R{`|X2C?^xOUjvAcwc}LzVu-92%^N99mO*o z9CBcGe@`6N#ey#Ka<&s9xXG~Z@vIORLUsc*2v9LH%z8;?=Tms3FCm(EjED4?J)~L~ z&OZABlXV-;tAnx}k!LBjov|&mEV68^nK~sHG>)A;YgWqc}`$O%)!vDt0=WoEu zwe*aGAOH&w77{ET$U?A?*8Z7b;X%%UI4G>|^N1)1@d45lEHj9J%$mNhTZD+2xlK4FYplUd|xS-F)|C^?p74eY2u^t_ttZ~u;#0Li{{E31^P#x zOb<4nAq^K(qR{IsTdGFO%rlhtU2Cbn^+WA<|51DGA8OCxJy^)vOG6y?g9QUY09F_5 zw_pPRJ21$8upfYJ0IVno3lIljhv4AAIiehF;9#+b#-_o-FD$NrjUDU?An!pCaFD@i z4dsqCt3ps48z80^tS12y7=HTtL8qo&TS}1^I=N|36*(1R_Zgvkro+ z*`|a6q=9xo2!(C7km~3--@8uZvZ+$<`M!48fJxhp>%F`qhGI8{dfG9Q+W5}tyS?N| zk)ee-Kzb5i zvgEtoc-Gfj!v|(NQxNZ+aM)rsIK{y8nzXr$vvi46dz-}WAo{WEJ=a7GC!WzqR9Ahk%kUL81>y-t&cFVKd2Fs-T9oBbr z*VSp?*rm$8JX(U8*r|Q(PP@we9q8_Xu{w^NC{e10T2dOWp>Xckw0VX>duow6fs44Auwvb#MK*XHk$2~`#B3uZjRPNeSBP}u}L zh}}ot&BRMbM$>E9Q5Y$ku8x=UCnJB?G_4;H2WlGF+Cap8Z93mv4Tw0f-Ego5q6}zq2f~emGO$U3-R)oG?(PDJG7xJZDnX!uJ&}Vl5MmtV z1RE&WG(n(&aQo*P2x?m1|1IK-VNxg*Mk&!Bh6ey*4!{jd_fgKy&C4YU7af!<&8s0Q zS9%C?v9ix*^UBw|Nw<`h5~Vs^Nlhhv&FXAwV@XRX@4$Yc-s&!3*hRB?DCg#FXO-*y z)zJISE%#D+C00v;q3Q zVpSqaif~ooC&rcn&Ctq=#v^i3evehVDIc;`Hv>nuUy6U4g|I{nWgpNi;4j#R?~ACg zE@>k`P%0a=+k)nVt@z=$UCvWy4qz})4{oosNwetO%5wrBS` zjy!#E<3jt3haAMc@Bh8Q>HRg_K&*jE3i>Bbg98xr*-$M(*mLWTZ0SC1VP6^AK1}A+;PMdL^Oyy5QBfC zry$(^4;tLBr#HN(8!CzM0sya|HY0=tjRoYGOfjLPAR&BOery_m*UAnQOHVJ#$CS{d zD^tshVAcM_3T8n8vGI^{VO>$G9K6%7zE7^Q$CuF3*2O#Elh@a;GwjV*JC>$9;iYo@ zp`7Nlmu~qTzIiV)rl3!3*+=ABo1Dn1ucka&M+Yr}_!)|pFmGUWM{x-#I}bFJ^v2<3 zEero0+-{(e>in6;uOfTb3&~S!1tZ-A)r>UkvyDd?BJbKz;+FYd?a6R>p3FXSFLJqm zK=@L_AvcXC(+d?$a`JR=h7z}+%%+C)Js72dlEnt3z=yfrx<*UJ4n%VHZneX*+wlh+ z+#X>(E81>Ya=xd)1<TD}j4I-4I4vy74?x1DcU=D;oV z07Hb2$Ae1lWJ8Mq9|C1FRm90zUY^K)c1_a4%==loep>sK-i)+t_;utR**$aaQL*+k z0SS69(QG2C&Cr8;c;lHOded{u=X!Km*l%+BmCt#7=q)olyiSIBIj+oT_& zc4O3m@w-uYQ_^ho0XXD0sown8NdD0fa=;A280?pzV1u~jw9oIs3?lif@%vXWfA0@D zkOo^Xi07{^@!D@Lk+U55Hx}_5wn6b;Lp4XXIi2-;V1rV9`kyvVn)^V|=*XUz;lZsC z7CEy{|K8dt^LnJpNF)M>c|2`aTwb)|jhHAkTM@<| znKo?>DdSH>+~r^I{c^4!H~1Gw{=3GhpN3ziJSLBhZI4E$Zj7V%3$Wvn1-rGspv@`N z!3{psxGa&^Fu(m;eL`4F;q{-ylN>7cY=w$$--dMf<0k4)Jy}L3qg21msEuK9qm6F z(>0s$ecuLg&RKqdeVgM-IYSWGxDXX{{jq6t>?w$Fuyun72m3Y%^0hgLgK*9S z1omxkPy%hjZoUZQJ1SCp%6%J zDh?=bEytW^sTB0ZD|fTx^J`<*4-}I`+AqO|i+RfmVwG;Q)(^?iRJ8812qNjRxJT6_ zd8j6wtSz+i?&tR(K7RWA%a>pOKzo5IEsfnRDJrZWht;B(oZF&IvVNm!cvsVipzH5C zOJ3LLBhyGRzlsHtGk4pzTobl{4JpeykCr>lDf}XYoejUg=kZQ-+upl9qa@{uSex<$ zXL3ptySplz_GDX(q5IvsoqJ1I#E~&Px3Fs&iNsxRvPVf3t}1K5Uc9FyNF1OLWuvZK zpu;ed)*TJAz3~DnE<+uS^H-UF-M2pocEvZG>BVHGibyO4FH!3IHP8m~(tz>2U`xP; zz2S57HdFDhTk?-mU&baW{ET;0J+ydh%lJ>4Q5BA+jc%I4Mq}y;1#YoW{+W=hy5Ar+t6lxItgSaUmeq|J1uVT^lqdAin=e@BTX8$Ch(?H^+;B_y#TU z_l=taZV=E#f78BwEp*7y!(KJl_#lNq+D*A_=cG(tw2K2BwJY?Xb>YXuLJ5q7{VHMM zDaRGbi5Yvuqf>JLXlCIq^%z#JYEii@=~QTSjv}I>-Wpqydp!PJ+p(IaFwORh#)y=> z*l_hrSCCC5iX!?~uE?Fu^%uH%MU^imFF+SRafJ*Y4PEc~;EE22kJ5Hyf|WUu{t z9rcCr?D?7;i9I-m<&%l)fFIB6an(-FiMvpUAkEO>!d_H+bS)b|bG5kNfn^iS3~`wWXRc#9OdPtyVgigQv+n&CTGm+R z4ca)lS0^^g;l{JdeePuI-8;~yM0ed&zNn*X5W07?Y7Xu}-&p!U! zt^(8pPz=C%6I2C|G91SPGKFL1*G$ik#97mZzp?Ta5&Gn7h$|Yw!(Qs7U`WS9Sw7OX zb4CUge3apnvV^0INK+aA(6HEerKn_vLOkRp(-tBg^)4r z$mH~(rEc!{bIvWA=00%lRkJTK?0T!^MJ>&)XdIm2N2B2HI|w7KwRf7mZq@9& z-d1<7oi=~1$f~{m{)O)tbY_cdPoG`4jSqzmNkGEu<5R62&v?UAEYng16e8VJ2>_%(XrM1YRDgzpprt?hv3ir7GG!=g<+Z)~-nM&uo7dgJeU|X@He8zbH z)cQ8N&xF14hg}kD1f2M86A`exNn|q7ke&!bdPeUbzI5@^&#Uv-i??6C^vkFBub(~~ zzI+*sxsV(N{UHg|U?IXP6wXa)b3x3>y`rF>(CS2@%$t*0>`jezGsEh@<_1cz){wA( zT3>%GL`$7_`8pnNg<9avxB8CPLSE2pNL!YE@Gh2j zpd}5ut3MW+SXYaeM1S!*a3BQ3rr4oW&L4zwt54B;P>O4=O8*~-5J(=-Oo6lkjTJ~I zkVR`7zprD2WjII}kV_ziK)8QDJ#Z`)$RE!B1>_PaJmB2$)mZ)O=m0WgZF&Ih704yd zA_n9YhfAF8^51(aP=pHqRfJkPo(y;-RfAcepO804wbUZ!1I?UXZD%#r-f zZQY8w|0~^2EFd~80N`aLCYht-Vq*M7RFbojq}}4eX#Sen#V28*@~Ma2C0W(Jaut=8 zqBV^!>V*~g{#8xqcdDedrzu?MvD0AsUy&Ek=@k!#;c z@3>@yR&ek&ve}=2(b3jCLb&0knReZV5Ein8yynUyur=uCz}@upddrcjPT%e?mM3WN zRl!9rQuRT9FP^N{(ioMU;@RlEBZi*p)fXHZ^h2vmAhkmd16;&aiK)M%kSV$lf5ZTY zEuy!xNJt!jsz@m5VA5A^AcWFN2#yEshhu1Y1rh@vR*k(BaB>vC;8#KX4kE81T#I4} zSk?&9r0&GRJInpR(bwwfQ~{b^OPqi=Jw+a9z82?44h}aBl0_E!yxIlr50-^#K6?J< zmv|&}imQWETuzCtfeRM8{#O)Qb5&D)C=z={3I}1(DCVxYkK&-HloV;e?QID@^@#i% zJ1HEz;k~uYn^x%pHK`FZi1#>x?Sjy3)x2>Gquo{{snl?=+h-g?T^FYErSj^ohZje# zrR;BvgKl89;Q3GNopngGdaFaEXMS2xjM`a7W3nT(2O1cFs05`lCSHSz=h_Ib2&(>HBNDca?t0k4dveki#KaSIcV&_B@-x4Ag4IHbdXtV zwEF4-f2S2Ex8ZOKq#;Ns&bA4p6^BzG2fr7mf9vW%aRO!OPn>!aPnQRn29>&B^Yz6f z7&&WL-8kfi|0cw4wv)fE(DeK80%5)4Ula&pq0upRsKAqnfl9HbQq6>egAy`4*E91{ z^)u;N2Q~9cqeV|Ap4KX>@z*?(k)%+4wwYSkP;{WA<-$%Jp+4zgZdbFKC<+B3*`TsB zk6`-FLa}Fru3Ks2JI-j2l_?HKbXuzs=FchcJ}i}GCq1ysoW8QfqzB8ok?wOafZK0I_o|H!eeo7p5 zY!V-R;jlSxOl*qwjvM#_EBv5MS@m$^kL9Jj<|dMEN?j^nQEec6jDu6Eco9|XS=`yl zbJX6%byjVE$kN_jGLl33Oh{aO`zd3O)zxbzvB;3O+U5pttdlP=?oFF_9~ztO^XobM z+1*fNVZEMnyu@SPmygufp)b@#%{S*@+aGMu-eU5~%D(nYi)EcxupdcG;fUYiy@@F~ z!~D}r>r4=4R#^#Ft}n5UvX8D8fqMecje-!3&3A0w?_+QhiXBI#wqNS{@OtUi*}s&f zo09-~-<@_ScYaC`p!=$tHYRa^iAVCt5e4V~W6LTJQT2I9Nl!qcJ$vt7DcfDSQhEm6 z)0-9=V$99!YXuQLn?Eoh$Gn{=6N;6&baw$MuW|cz>Oi&wX7fe4Bmu2A?@t{#*m>mI zVIsuD9M!cPbq0i)`L(xFbcVH|e$6oIP76C^K-pP=ekz0dPs|Vv_{d7rx#D zRhcpA#S*6)x+AHoI#m)v1oA_M(FX%McH+hv1B}{~*i1v8u!Fz3SJZ$$C?RRbu8AbY=3^y@hFM-27{dpR-K4=Xj0=NxGSeI-a> zkdUA*f)wTCVShH5V50wf81|LDoEYR=9`-$f|H@kaA&MU93K<)AT;m+b>=ebabs&Do zTJS(-zi6a7`?^E?&}zqYs8QWSNPqb73-{+PbRX9fF|m$VHRlk9A3TnkW{HdR3-J|8 z&o6*P9?JqW3(A^)Pc)&_{J`vup(k3!Rs<3eX4R zvImT|DH{+or%49SGT>O4lg4WK^9-Dlo<4PnE^XT9ppCt@*S)m=YKAGHu+)pM;<~gh zO;jswwc>hM`?z+)a+B#7qwU4QtKp(XmFF?NvFDxbG;H-MbOk)B-qD5aPb-TRvdQcx z6W=0xEI>o$Pj!zM)*y7D5Q2QrYln3&L@bOG}4zQST5iV z74>1Jw?f7xM3??J zs=J`P{C=)&C7y=SFY^MqNoNOh==v&+v-A^azuR08=;-dK#9t|^kO*Zisyjw=?jVqI ztc7Nn{%L|7c>ocsw3zFW?fs|8>~UexZSTj0+Du}SnX8X$4mTH-?tVhX94QEp$LYl$ zrZOA7!c|tT#B$3|T`kverr_^~i)mYqZM@Te5D1KcP^QCqRvZRnUZ;(a)UyKb5&}Ps zm=z@WIyWqbmu;N)B{tsPy`bx>y6P26eiHnL+N9gzlDg##7Fp=?xsr?UKjHmzn zipwYKx`zelz|9K}T$&k*X;A|Y+~lE1O6x;^XO(T|?o}raOyV9+1|DNX1}g`gh$SgS zB_%tePlTuKC(~21b4-+|>HbBB(77exVHwe%h?TNbKYcnj08!dxkCO~Lfz>nDI#*w( z-P~;^C(|CMf8JE2Jv=^aQ_rYTTU;I1Y}3VwL%6X~iJOJ{M@%;O)iZ&~2R-9AW&v?C zMBQDI#!8RQ@)C*99F;#|#HX^q0Y0>H!opL|1bLVnNf7kBHdUf`pE*S^pl?T_u!`5x zXEBn=$~c!PywbJ|{r$3z*EWyI^!6L^AABP5iZb)%tgiY|w2)?GvAAef?DO%}5ds=z zS=MGQ4WJle8y!r)T}XZ9?;*inMzlUQ@YI1Ecc(3YvSB?=x6DHkdSnrK#Je$X^Tj6Y zlEownO0^5$|NKMP`}CYcS2X1g4OR7S>L<1A@QjWs)&lbNcz3*P%l8;f_VPPFWdZn@ z3g>S$F@FA*H~|aLXxma%C8w{i`N}28pzTaYubOCwt*il30yVBsY;*Da+ow~_z7KC+ zyzukC^7o-R1Sh6o*!+`W*=q%e#G<&j?UEaE-A7!Skwzi}pwc0Jx2|D6>d^5X0jjKr z+&?AULT+8@oG|qcf7U>jQlXL-kmQv+ZaG?rIx}9^OVkb*%;jBN00ff1lqaqL4}n3N zkI(I)Z=Pb$9|Q)7g{LInr*kn)r)=pc>=FPGwRZ4+l;!AI{V3b{=<=f+>c8QymGvqF zuHt18i98t0&p;j5@Mc4|p5TK(t=j-Y!=m>Y`|0A8DI_+qq8*wP@63{lmtKsGig1xb zr;3|ZP8y`17>s-1B;4)(h4l=?Y9HfO&%t;bQ&SJq4cQIArrHaRVr^AAaxHEh=cwzu1}$M{65M#qDo8nf zH1xG8Q|0k&lBlpL;by=&?mLeS)#fV`O*fFA7KbYuoa_RGy;qrjzdZUqA)mU+wwk;dK6RcuivV}5uBn&M>F>{D^oZEvm^O2H4Y z(IDDPPgEjV408*~WgkYUJs?gtOUaJgF%s<6<8B~m`?K5L)hA4;i;>Mb)Z^PJbAB41 zl?$_%8#Q6E^Sjy#ReP0s{cZhbYLgjK*{+7^i>>qXXRwaDo89nfsRbrY$L}*W?%-Oc z2mP^!62&_-c_i&;*wQmKFCUMTS^dagy?W}{d!2o*YNUv*R+#P6KJkZ6_8VGE5?#j5 zY0AziihfelVB5ql^Aq{i>Bva~l%%x5p@{C089r`O9}oJ-HWFO${WEhUGuCLp*6}C6 zoNEpb5sXemDe{c=m`J4^u}{+WIEqeES}h%Jmq+zVfj zW^1bSDAWG$_-l%zvbqt7A<`q9NC*nukR}Ak?vm4i&>g``TM-qFsqSdeza4mU+s~-nI96psE zpAzQ9X345^^(0UfOIn~Op1M-`V?6;|iXRe)wc^$XaBP z60V~rL*MH8`BFBpwL&xU&k{OnKf9rO^A=(6g*0=8 z>V5y zu~P<~8T!^%ZCBb|!3-bfbR46v4#wlgCkFhi@; zw?@Q&(YO<9Olly@!+TFO(V-49v~Diha&lD;8>zDL*8?zfZ7KX8s=u0f?ixO=u(Ped z@z;44n4v9pfA!BkXu4bbL%>va+qs;d@_R|Nrlu)6ohExUCe{Kb6X~aqx6~t24>1@d zSX^%0$*goh(n7m1i7uX38Kfv$mT^eT9804W`ENMW=FH4&^cOJOUt81!=(hJemay5b z>X({sMrvFcvl8!a4j{qJpsvH+M{doUt~+0rK4T2&xo}8-?u9Y=c43CSiNMNjlJ@E^ z`VUr^A-0u%2A3n$g&~=Yaz?TTLx!Nw6+p=ij6}7tMi`(4IAUDTjiwGZTn-?#Os+STrVr|N0dyWoPcX?0)| z@|IdbFyHq6T`<5!UN~?UBJKi&pL*xeRceOZmgj!W{lkHWwx&s*WuM&&L_1tKG%K?x zM-V$OdB|Ws5+xQH{M!xa8h@qTH?Pu&Ky>~{iqed*3iR?y8{QZ{5i9!MT0#cDQvR_^ z+5~NOk+y$8##X;#Y4R}DK?EK2vB)mh(+VL%73J-0OeDTu7lMRYZHdZ`@~kyT^$fKO ziN)a;>c&pTI*%VMAu2KkVvORt(+Uy}XtRs9K5Ps@!gcy~blYl{y7ppJ3AyvB6r|Du z)A--FdK2yLHqiBSre1qs7F6R6QS!Rn`@MR|3vx(V^>oJ!ybIyF z^S1NCyA{d$8?3@>^{Y>BDD;@|z-6fIh{}-nKqT|n1e&|ZA5yP2uum$-KSG2+s4kg^ z4qcmsdX~I-C z=-J#DJ$*B5Ny}NibC*0Nb4$-ECYWaTo)5xZzTrq5WX8#w;cJ4f4WR#E9jMR{Y zCW%?9w-g+68{^{%83zM) zl}A6m*eHp&l66CF^L@tZtVk{Y+JGT+Jj*}yP6HU!IlD1wQMzY1xL zNzY3N(QfEIpvdf-lOYYvnW1jo=!nN{Twp?BJPqaZ|DLv>Z9c3*m|;W2h_=&Nrc<v6Ja6%ytF!v9+B&C08+&5VVHG7}H7;{vr0=hU){>;} z>AV&ufpq@3c@6g-PEpqW^71`#qhH%V~UlrYUylgqL=|eM1MtHmyI&QoW$M z#t6waWk?QZS8X#J^n2XpQvC6wT?90P|OH@LABkTKP- zzFNaKbZ5KbKnEaUme+HLe0R}eec9apF;nuB+knvHdNpO`5DT@D?hz$3$tUr!%qSN{ zaS?tAF_!Eg*6FrwgC>8?y+#63yvTL{IT~qVLbksS=aNXLcghJ3b)b5*j8!Q7F9Efk z(R<9rXv{}(urEB>p+3g8aZAwj4LX;Nb91AOPvwxbf%^hv{+|HRSE8f%Spr%F$r>Nf zn7@*O5Nol~i86BSvg-?!F_-Rv;R=17t~8)E_B^<3jmIu*0vcT9pm3L8++H=fOMlvE z__q1g%Xv}Kj#FapA2cxior}S3L(-2l)XJnX2&=C!iLupc^Bqi4J49upT02YHtv^z* z@W^i}eAh2mpG=R86Bxju2zOsvA;w3YbXlvYJxJ^wrw32W6k+dH?*^GaQ4qV>hDe)% z;AghpJ9la1=)`_u!!K1mpWc@CxDB+3P*rqDNIQ2_n;|7!wo!UlRQ(&*Q7KouDND?Y zFUH1Z2gD*|*fFm@3zn*^CzK6}V6vb1KvV67J?_{(zx>!9ao8K+{S%qmDOSGO- zR@saw=M?7+;QHjwsEi2CHBlrHzvhwUDd~xehtYXgd5q4BqX{6nm zOQs&4IGMgnAQw2xw_cyv6JYz`uT=Q2=)92a`@EBTsq*ae^DDyQ zzbp0MB*cFTn7}~y`-GTt(fTJ|gBlHr?ArO#8nr=L=Y+(d=&w z>;s&H7)-MMir4=(WBNAEdZXur$nT|)CI$ewkjxP&VHrK`nG0HTG>%)ce zg3A2Xo2k{5XF$(gJV4HmE7LNQN{_p$cImpE*wvOueN&aI6Y-iuceZJD-u4|c;lAG) zEO@t`TLHUUPz^ln$Qy3zGturj?+bf;N{;vJS)OF2F`^OK$=7B|bI$N|mGiatFFQ+Q z2r_8gsc0xk`4&`8pJ(Id!v-?Nfb79m%}7h(9szCUIDOSm81PegJ3=1NAbeRbD}r6z zG;5H+$jqQFjtj74TP&nf{itOA$O?J0*G~It;(RVS;WHZuBlt+P@OC(@D%PDisj2RH z!#8782!|uta?w5sKkp6?|Ae-g3sx_}MhUFfc)9B|S3fhAKC{$hGdPeNN3E(=_E{Xp zC~ud+zi*yH76hx24vD>r71p&WB+`fmPw#aIT@Im?*u)`zI%0N-0TBa)YGc-?G$sl& zgs!2=DB}V^+|mh5xmZp$>0QMwoYna+TOI=*OV6fB0Zp(Sdo=F6Zk86=Qt1RZA)`WBYpVTXupgDnQxc0ihr#)w7w z4ngXWtrP_%FDN8hIh?gUnkc!BCvdPY=0)plUamzrnYGiegTBa*{<7;vfn~W&zHh&n>zSDQZG1IbX(fNg8>Vpj9(5QtEO zZamp2u(-13Nlk6rlP6~z{;9$Kf!db8AA0=F1@E6|%t>uIZkuzD&Ut+SQkO&KKiwaK z9RB{rBs@)w@o_rxx!{T_IPqJ1=hqQ_!N>~MJiEF9d zr)VzFQu7p80vC7tcSV#d!MyNT(J3>GYT?a`CTx3A@Bih26{w5fV3IsXo^jxR@8FL^q5 zHa!cF$L+nT$=H72OCBVQ6gqg=xE~w653Xw5Gkcc|NQCDnyd3c`lhaex_b`IE2qNJ= zJ8Gm`v1}V?iE?M!ym6F3Tg?bp!R_eFO&(R~GV^DGIR35`dG=n@s0%C?;h8b|JOd{v zF~_ei0IAl7Ybg4*?RX_89$a9w-8+<@f*h@?n4Ve9_~NfAn`oAu0FT)3EZr#3QfnW8$%w%P`Z;9eGpI#ZJC z*N65{(%PQCKD`=w#tkxEn>yL>#^KFN#jMcxo%3n*9fSC7iZR5cjFTUZgP*?$acZC* zy=O{fW7=SgbOLS(%X-; zC3u?!jqRi5WE?PGeJG~(#<+r@7sR@?73a6X)FK-PVCFux%wSZxOL3|7#(x+ ziMX>0npWaxa}V)N+zgh{s5_xMZz4C=7k0x!{`p-%ZXtMdrWa$TKb6Rn=fzr}w4DI_ zW~|Dj?`;BJ#!piPR;fS9Yiq6xGNP4;o@kMPPSMIKjTx1ht5Bo$qk^diG*80`kU_|v z>(&x`-DK^)^dERi_2jOw6Qi??ZKwlz6p9dG!_S@gJ)ZLHv+GhFxeSPWNG|h?&E%If zM^w#T0^IqNr*alj&BdkD6;?vR9vShpd+UEFey3RWd8?M4>$8OjY|-G*8WVWiAx*-A zIY6;ZSGBOtu_4ASIZBkwl=@K=wmHp^}0B1ZZyO~rMEs5b4MT4_~cT{jm0d;~9E;YxIu>`czC z3vWMpAM(n;|Mh+&N(leSFN2~DEm&RN&!~K4d81A;1R1Gd;8cnoA$t89ZJf-lw9SjK z-{ec4Ei0Ba$Rud+8N=s1go8$IiNKkw_Y*QSxV~5C%|4D%FzF>>BV@)K^HW&A1~`D! zYB=a2k4!;Q&`w*F%#r;bN9b8mwXC=UdE_ex)U zoD*NO6NPrOtvFd0tv@T-{60d+?wJb4s)((BF2-29n-KG-bj zbjT_zVXR+@)>3pu=1c79pyon3qcTeo^OZsA(x71ST66 zIQei1J~oX?7vk~n;-eCitd9)>5+;c;u~$kJeZ0mp{2nGmIG>v_K5kx7iQI3~1_QeH z+*Mg@>9@e!3)h`f44huqI{@*8WU+t(aDzzd^T79 zTuRx)|ByLKPKHAgVInfquO`_>u{UKs@Aajrs1Ued$n{Qqu6^+^vAzd~Zk^eXD{?-@ zYP;L)tB3k~&-m(T%ARc#xz{Tq+TErF?3(Kj^LhRYyyeZT%KJ)gqWdrdR(w=HS@+7;UAU_IK>LPCGb}nb&Q9Nd8rT!v(@C?{MEw#JT@r#M(|U0l3+$;l zQf1X#;y2rE#>O*HDEKN{lN?yV4n0Gm*q*{m1Kr`mkZK;(ZSDm|+GISX_{a;5JLbBR zc}6I2@p_Yj{9c*Y`&e12!XIQbyPpZnZeV$ZT z{WlU@b8X@MqyPS!4H@UknzJ4QM{3T_du`$Ur)@VV)SOp9-wXA>_1}NB8{?=nI7$DL zZTG+U?{a>p>*bzlCPu&K%=YpCRe)lR{>En|Xln1Q(*aPZ&(^d%eny^oPG^{^fHAWo zUe7^C{X|%(yL@EEQK5CI?ovFvz()z;;jqjy@AXnNrd*<;2plHprGQ27UtDbQdPU)~_k(k@X+Q%xLAt?mFa;tY z5qpA>}5Pe@_OSU?8DgflCG%lJ0!)2N(q%zbLdBd?A3 z@Oo*p2NocBbs+y}^3zj{8k6H8o~{kN-q^H(w6COLGu@z+*BUFNNzTKYd7>yvBxpw~l2%o=h$k3b(S(~`|? z6}Fvo-;x(avh7hO1{{i1z{8Hl#hbvS88ls>jjcP~!hlwH?ADR0Xbw;iu<|Qwt;2T?So8G_QB|$*{WtsvC(OmlhZGdx zR#J7h0tmMkjQvh$E(PE$Ntw%3tw*ob?~DN+7T2jW&5+lR{NBhS4qCuO?&TwT$Ox8> z@T$*6Mz+afo-mXwna z^+HxU3(J|;3f(q`N7G1!UXK10!>|BzqQI>3zMFE&dx#{KT^z8#o=ls7%bYbvrc_ro z3|S-V1!jF*fqW-!Xu{nGbW0OQ*NrU|Y4OAl5WH!^5M<1AY z0SdeHcG4e3y_Z?v)4Z}nVEs-d+nftaL2^9$Vf?499QsVJUKZFu&MC$$SJ{snJzLKE zD68k#qW2PFtb^d%X7u2Mc?*T|(>m+eWb{Y7bif?0oMs4L2PeY9Z&%?BfOTJtfxXeh zSkhd)+!I?0dL`3*2Ef{mT)~Q#SGoDgwm6^xxV?_Mr);qcIZ$umSLg)@DGgYq<`cG3esMMTKIDk}MQ)&*OIgbs$y)^v#Ij6tjH0M>+kGb^PGeb@;4Q|T5 zKH2^Du}jW84bJOp?{)wAtEixB<+wAzs|t^`>* zI%QSU`ROGqXNN z>veRuNhXvf9XnOs*;JoYAkVvvNH0i`Jb%@)r@p1_jFobES(*C4gvm%<>oFbc^}W7z z@|w37HV8lLI|{c(&OY>kKDw650ELftPKOB4tag}L3#lw>4k(%@7Czp$vD0d3i%;tPdK z!o|j-dHB=~SuV}h*BMksCPX~&!IX@)W%P-1`Z|q<$c##HY{$q5wWkkNv#VsvoWi%R zT@EXmOIlT`>){>C{&?7@{Y9F!XzTrinRap--x6D1)3t+}2My`pSCwfw*Yr~_m!3@% z+e90{Ywr(b>eINL3sPv*uvKVHQl(Ox`3d8gOMMYEmS)G$ZR%Z9G5Bp$Vs-^u$MT*9 zw{tx1zzdn>OU6-@7dq<3+KQ2p@{P!9!t{s}H7rfAv3J8rKZ}hc={9KVeH>zuvDuDg$cxFu^ParzeU`EXr6udEwJ#)jg!PXZi#4B}R8X@<-exCX zymd>za4@;tN^#&oliqBAGKr*Ptvvk5qt$W(OsbzfclL3lkiCIZ@)-NzBcsNUwqQIC zDBKZA?9;*+%2_ZO0viB)K6!xnw1itopo~Jgqa0%@r}?pU&tiG6nVA&PHoRhR)7n#Rt#Zq zva3&z?(yyy((&Hn{;H1MB}Q92ttBp9)C1FL`A%z@d0K}?)+Y2h(O~Pm=%Q^;zU=TH z>TzjuEigK=xzXt&Q`>!RW_d(c)!F|o<>}KAWe>V}<4pA8J$BUfM%qkwB$C7q zr_NHY1kCO;oKlTC7z(1wVdwMd-j-s_v&PjfQ&Yyf!$(PRRorhEPwVgj*Jk7;kyE1ht8__@#lyYkx<( z+}{461bdbrN%Fk0&zeG!2~G|PbmP~b)|&FL(DxWi)KaiBFq3tezss*oS>*FjD4dYx zcb|$!8QD|`-%(c3ZY}5ICU(Rd+mPr)GTh}jTrU_xl@ZjV#x9^gCU{3f?=?QJeMSsqGRSI_EPEW z67Xhw%I@BcRLm;)QE=ShfEa(x$iyU1RX*DO!zbX$S=p9|SOr=>Rq3Fr2vSQ3rv}Qi zURq&#VS*FSzO=(wRCR~Ry87@sK+FlHbupX|);S>3$IM4KLA&z#+6SC4GFWavYhAJZ zhOW36vZGktV=8)Lw=8Zb4Y|XT=kBFgs+JRc?hV*;=wNc_hIlI73M&y0q ztEATy%-Ni@%yr(8^m`*!!TJM3+q;*?t3_%X(z0Oju?5FywoiiRGllmMZJDhMhdC@5;E0-}Ny z>wqF+9jv1>KF+yA#te$%_^kIm=dAM{^a~5WxYmE~d+&YS`@Vi#`>wd}{m|)P@UY1I zr+waf&v?Y}`#%0%K+P>KyocRIU zLp>J$=NXfm=uynW9n(W{nHLADe6~8iPUNAvIG~-?T}@P3hu>uaV4meU23or zyf&4Uf2~*2Zb~trO6^zIUHus18OXLK|y*#|Gcgq5RlY{oFOp)-4L8 z%=k1j%ZglqYhh3_?kJjXW1ei-gUd{beiIqAq=+8#s=WQdW^(5oj>Q+UB|Y+fEx87Z zy!bAwXN&S`j8p^<-XWh#2OF?bx34x0n%|=($?w6wG;`Ll zq_wPw4_D7MqRcG=^|gU9nZTN#wq#7}js<9~cP%oNEu7BxRZQ7|#_6(3A6VBjAQrTI z_Xq3j*20neJyQzf7}2ES?Y281bUq~0@%_5XuvC<^q!PdBZ@CAk$FhBhCMU*{c&~Y4 zT9DH4-8J~beG^2g)Oi7w(wBt_R!9oPwMi z?xUhU&V*57yJKg4c$jZayLP<2bKBVF%n`Ga--m@AD5=pxOmp`SC)RG}3-6colySJh zG+(x_-y&LgB(V>3@pqFJj}$ivaso=|J>q`aUkdkEizN$o#zrhHA+S+WBHU!J$FwNe zCzaKbVa_Rui59~Lc_TMubW5MjElG zov!wX(Cx8sL`c?4bDJ6!T}Vpn4{UBPS}d8rx(YwWy{}qalfR>HgH+~7+?;~Uk-@n7 zHm4?o+KAYFF_3h#>_3d3^+IwSDRg@DlGClPuDL~QAD)2_i>qE3LsyjSLdvfCZBm@c@ zToLf_6r7$1hE}u3rSXKdhRm@&n(l3f<0xdQ⩔~=H6=m7FYh!npll+IG}^SCgmBL&iIsRn&)OjFj(NYOmnWrod2qn{cQcR)G3l_K z9nhZpqwo|+dK>SoH43jEgdvZ^o)t@NHiCUbBL^~rRS?vs*Hr5y1L1qalKQkn#8;?Q^cIP?twH0H@k8qxA zvh(_p_{ioxpsh6$4iC1TyZbKmv4^Hod@HYah>ql zB0ql1?yg2iBn*)veN|JBZjjqt|MO~7q+$_DM|$Wo>RiVo!aBXoj`cjIc1SS+&6f?l z2q>OjFCX+FEBME<`CIQWzWEN)G%&*RIwe6V1K>8#eo3mC`x9e1SkOpBRYW6L0c_9w4}T+bggJduh&p?fode6#a)0GygWI2LGc zEiRyjSk+}AZ)BcuC|_zUg@%%FYGkc_(nYB#Fim1Xphgr2v-LsWG7<@D(kkZW@c+=a zRG(oxhm_1=nU-tK!JJH1&VZN|oKJ8EWh&LpR^Jn z?5t!GB^|U{=qGqijc68}*kR&e#5#Imc;sYc;lV_s4W<`+lJ#z!RKI!^LfxQu{5Vr~ zx>^0~1!m|5-rMJbp1drPttgAka8hKR68PliOG!PHV@Q-%nAL|ru0Gg9NzgyW_tg}AA?YPnDLn+)`5BIC~=T@ur%zSHSo zSuJN}ZGVIz?UhjZ9!l}gs`cuV9d7NJEJm&xNVkv7^jX~zs^uaYJp=P@Rbh1{ zMjAh(rMz%pq>URlQIE&|BKpZQKD@6zLwL7g#-ZSVd?d_Bis!i=9U(aZ9cr0OxTp;@ zGR?^kC>S22X!cysjCgv(125y7k>NS#v_Fhhu_C>q@~|k_9w)npyw5iBN#eISrPmz3 zvl&fd#apHbYF{a8XSTdlc|SMeaBt^3sngm5SFp#~KY9t>^Bc?K<%zZM z<7#~Afv|ABn;J<3IzdB>%EBs35Z*IY;G>2#HTvOCRX<|i9glM+7 zEZz}Yyv6-DZz2E2ivYMh&U=Zd78>&qEO;Q3B=`=eGuX>15NUEQz1)Kjf)vwxoNhzE zO3KJrdhc{k`ngt2LcIDYU=B?eDv86gsdj7AID{EAE>&Q&tBt_-Pl z=#X4R*=YqAqxjnLXxT1?R_&1tO&3I6`#84hT#C?mN4uPhc6)!h^tBYD)3Nxtp-FVuUAy}^iL0TX1`RNj0fX%idXB8X zT1_PR0qGl_Ge{w`u*pa%%PbM*Aq*tR!*ka(t2KTACN0$`)GB{TP7)LPK*f}FRG6n~ z`|Br=m)nYUPshNmMiG;|ligBiTdVwc6c7F+Q8?|1NfY`6Zke0xh`Qck49Pg-%}sa5 z65i^*Hdc|fJ?pBdyP*CD@pof*ow+IsdE>T>@N#VTi=Pb4(|Hx7smcqVF)y$Yv01;8 ziGymqH|4dR_ECb3#^~z8ik*4Pdw$o{5c_m$$CEP!QcdGE_Q9XbeW`0<)n=CQ&Pp?4 zsxT69z|pk;7N6v3HkKEF|J|b1Cs|_G`~JXBIeLk-^JSe%?oqSN9leGTT zdU~(2v+S){d25KzhWv#MGlec7)C5qx7@YScM1Yt0Pc{T>gVrDqQ3kaS%IBR^TcpIp zluIfJxd{nKtRnSL+>XGN;^Ms0;;AvrDThLsbULBR){Qi=lITr+h|a|wgBGG!POMFG zKSC9#hkV@rP(U)aa7~6g%zex>+S4;}-$^L7uMp=Qdk$mh^|a7i~}Faw84 zWQ4@jMM&$zaJ0l^A(kwD^w#z#uU2arNH_E_`yoV?_sM8PP~z!r!kqG?-H^ZGCvW+23+d1;#GPBWTk>VDCR$JWDzeH?7rzQMg44}~KZ(F~yk=iAvnnNo z{y;-QUDr_i!JLXWM{nJ-qd_Y@gmVA9%}SeV`~*w*C^KV% z#xih7A8SY@GoIM4esW{wZacw#h}yHOS}R!&?c|5sCeS{BYI6#JZMct`ziBG+wW-%J z4Kh^TZ?EbxBsG0f{@TBlD9@CJP=Cnyn)BTr%7w1w71D)y@5fGVPSiCx=Dtw?!LmsR zdj0+inb)Hr$SiRsw$Eh^My$bkjTqEyeSFFD0AE3+8D3vvoS}(*NXFz)i z*btw2xZ8sa|EPo>9x~n9j(=ah(qczmS`T960F4U)mSh8s)s-LoC?8} zssD$z(@qmi6eYLh(E@7%vIw53M`2q7g^WP@@Bbj^s!T0 zS9Bhyp}Lz^Xu}K*1zk3p^3><=Ti#cNHuTSj$!~6#LI~74< zmD;Sl5Fs>vu#y;xeCTVo+00o+|1f12diBW?`M`Hj%K}yH2+8BGur0?m(XIx1EtWW! zBOSTBp3?M~{uKvw$uPM9 zKOMY8=eeUd;Q}5T-)?Qz9S{{;RkBPsKBVJ7kn?Cq?#qM8fpx>vc~no2-TXm(3ef{l zlQbSGOfb}uYK%oW`-%xXv7$(jE{8EzdmS8tX4AZ|uGK4up}C3s*Hw-6tk4SgP2`IW zbhF%SSUqk={poM53BEZJFS6!S){%CwDZN`-fwUVasUbw2ski3iDb&k+=)*X(S z5Gz7Oo`8w4yy{BzMyfj=Mh(u_9b!5LMXv7ZL!7RnT0-_-91i-t)3td2`a60SN6Z2; z7U#XxS9eT{_cb}jxC{3+LC4L#HU^zF_nK*;q_yyE1-M!Utu*MV7hW^{ousw!MRS+1 zhhS=18fT|Vv>ggre@}R-dVZ%1E&M6=bXWV{^#>JhMYODP{^j>a3Oq64v~u-$M-_6X8fttaeNbM0eC zQhAXkYi+phRt4TSyh`2E)(s3*@|{)pe`lV-y9AFW&eTuO$^St$-d<6g)bMjSX{AnY zWY-Cp-3ya$H@_9Rh=!;O>W8PVyiMIK!jSXx?Os`BM_#VXv;7R#dg5^3Z6S@!La&%l zs2+$EG{y0NsHBVGIvyn@8!P_7#$S2&xPC(B^k9~?^cE&-VkdJIjh_X`%Lp@~%cYF3 zcKFE{(OD_WO*2jWWe`JS7AWN$!h`H@`ilHm^3MBemd~3_@2DrhQLx!rWw_X^5CS2C z8LopRB*DyhmhVNuG@%tF=b~k_-FNwoD2<-6NiFSx!E=v~l@yMcs-H)x`+SHuo{KTR zTQFeQu;!qb(Q!{j9ZK=-jC*emDhs|q?YPkFQBP5ne?n=f6FgrpvayJS$q|CW6Hsup zSv6Ina^9d|{=F2>tPk$|)A7PSu1XDVp1#WJ zZ(ck7&6qsVjB8~=6*BWz1KW|@qfqiTzcm4mIzJw=f!vBVnl$+3_hJQ}tx>VtqQv*7 zSs~?pw0->pRAZA0wi%nG??;5vH&eXil44R~)Jx(tq-&a0WIPFl^g4)U!*-MMl;$X= z8}e9B6#PV_UfZb@oPayB_Ha%AK#))OC6$A2hP~}k$FBs4T_1wTdl=uLFPjV?4!0Fx zJS1)$)-HbLDS2ru>Xo}dcz1q>_GU4uo|7HAsSj842%~R|l5A3-|MJ>~4sNWPDbY#y z$Uc33hbnB9mZ=faKt4VRniD{==`;!D87;LBMcyBnkpgagYuzyTkOUrwM3{}V$t06E zUrb&89@XIiaRpQe4J8v;hujs;4HXZ~U3;3iuWnUga!Aj5Nl^c>TVhmveCUQeMUJDkjLxUz&)lCDRS!SLjECF^>ezKJyK=T$0{%R6*aKO%j{ z$jd7c;%^mN!pkoWV)~;=qb=rAL0Qu3C*R4=6C)Rt&6PKS1?!3`29p3;hc2 z*@?*gRB`n|e$1Aeq8PcChgnRE_vI&8Cbf5{NxVuVRwX}Ngf4@`GdyfFEKTOHI>toi zUAWS)MIlaAgtmsq%&jPq|KGdZlF)lS?^`HM+;m_$L}ASmysal88{@=d@S9QTxziR@ z8BE?S;<2tF=YK&4f34ocI}>vcQ)+JMyH^ZdbFLJ+ywF3H z{l;2f7&a$VXE~YE12JwnXF+HFC$B2{xVqN=&>s2=T}zfvKREF0gP!)(*9rYP( z`BNbf^$6phQ{z(CT?CuEgAF3Yrw$W!rmc(x?BtuCXW`z23yr_$d+8BI%TcjNma#QJ z!joaWrHBgc=ZAa`CsVhL7;KrpoGnB=Yyo^^8edoJeR`17K*=x#Ek7wyxpsB zlDVxVB(3LDo{U;hrj2g?vQcaGOYiO8n%}k;S^WU@l8S3=gLPUnZ(p~eyU6S6wPzq} z?A7tvVwXeS6{PmkdYO_BiVr`>?q1m{d8+7YzRoEN0?Wk>UeH0OG*u6wqLB_&YMA`j zq{5=?K}KrQZ0V@=%%$addavd_><~oUdXlNJ>9>T50HStF#WqEtn3;%q6Ak_XZvNOL z{7@I$owHXKowYiiTuZvWZ*4~TBlHdT%Lu*&~% z4NED{fE-B&mOwy}K6uoT2Vxe8Z*5{5)Boh{Qp7*x4UFkO#(Lbow&@ZurnkHv9h%_1 zA}%G>&r~Ry8WLhi%gwWeXJ^KTY{)Mym1HC`AqHi&xq^w=QQm5Ghn?hA#3c>19Mr<9 z4oM#AUZWwkdlSZJt!hJO77=-R)WSewC>o_>BY=5C|5GPPPClIteP%bJ%VmY>k#28Azg72%w#$}NLg z%d=r2yB%LxzmYyWTw44*nop3L zFm^#w^X=X=$dm92g(lyZCp`tBXuhh$sRe;I4Owb3#d zZRlZ{zkW)SG@+)fWrfVqG-<3xm0rl3R2QlgeQM=LQ%y1bO^OdbRkXd1e^s%c(oVe+ zzZ{EPqs&;{9-k1Dc^s?fW$uvztMjeTPfq(}V<<=15E-2eH{O(z;H%){OLZZ74-QDF zWxuolR^bYXD|OR#si{RImTR{Eog5e@f!V@xG!uK@%a2h0ma*nPTqZr@G|=<3mTSp` z>kLyyi;puz30AGAuZwZ|b5I`w?T4 z(Vf#L?t({*JLR<^Qesu`>?MyF-`JEKqb#tprJ0+PM>Z&`bc6HBL@pzE5cj0|Z6h&{ zuE9u`_$@PZYiuu5fw!EokeAC%H9E15FZIx#inA@ph9j1_BKC~r@?2<^=;$XKU1lHs+o*+Gl|HdU`JnTn@`oApAbT6{gze?B{7wqez}wE zOIe+qj6vZ7-p^pKW`wtU?Ue4CPC6MHYm?(0V>OWMni|_$X4>DQ5=HhHyQ7xW|LF)x zXZmGr+EjuCNgzetHPV7A0Mn-!xY%|ay4K`zER<_j&!NRo@%;`EXr=(a=$MZr;YAQX zYP(cHbmRIBUENggcGm_w74nK@+0{Ix2Q4z9#E3KFlZ^Fgkth4h8k!M_sNM$z<%gMr zD($5zlpO1P#W%x7x^9)e5c#|JNzQ>=Zl*emuDBcIf+f&~c<1y{h+nqv`k2WNRYf_3 zYbTit@(cLYvJ|G`1$d0~ChLwou%^cQVLvJ#txHK5AoDl`9C(;Q%7O5Vo%3^gHU9Q@ z`AEx6zY?z|I6!5g76GU5g-je?WWpWa`cpKIR1-BQK6%ZPXrs|>5SJl&9I3kyvqMo( zmuVGFlXpsA-A}$GtI%s=gBrMaG-%v0l386}+_UyF%j@flk zF)9ZO#mnSOGU*t?l^almnr9;EAc_z`FYZR9+1l2Ve1jwEP55c0(#&ZDZrl%MWO=54 zGS$}h)~xIYObFZt?Wi}c9~W73B}bC5+>@UAsezBr>0Zt{V;I3?TEK)F1Z#4;Ozp#H z5lC=8RGHe}ndNT$Kci?Zeu4adOwk$+Th8|Ao8OuabBfH(buu{|ZRh3_Onrlz8%a7p z2iUMAR-}v}`7~d@M`XOc+A>@mPDa>2CT{CqRaJgW0)35Qb}R(1;Uykd$%Kd%)LLXR z7!V0h{>Fwlyh0saqw2sORg$~#-pbRAvSabmu_r3w?iEL~VoiG^wU3_PN_EvcGm)iy zi9!syc|w)f4cl0JLgr4Oz?FeLO|FImmqQ;s@h^HYvsKSs_cYVtW~XsI^>9R^w|ClL z*yiO4ewcR4uIxIlPW`sJKVgfgfnw1hV2~23lw>8!lz7^$O^=SuGDArTdYw zkl{hWPZHw9?My61_z^>LnVzg!NV}Q{uc_@FVytvAyg0UN|k^ZER8Zo3R>H^XKJl zVSB|AF~)@FkLNl~jeD+tlQi*d0^w_IY0ys^NRv10-*$7mW!P^$bLMM9$deoQDOjB- zR>arlR2r8&F1EDXw<1kU5CXe%5Mq)4lY%9x@gq;{OoJUwc_q!N)8_{Jg5M5(Ao zQux*lNESnh>=>t!T)&3&9!0oAgS4|MA##jkd^y~P4f zBsjz~u-HUDl;IoW0Dqm}hDu&VKOyt-_vH)$ecfqe%N3*aU^&54X|xO3Z+a-=ChB*h zZY2-w4VKfvmN-AI)|YXyi+rnlSzdqp-GikWp<&EQ^Fi_}nDhJLF+4;%skjoMo|I_e z__4JGhL#|v&n!Yc6^ebi)$SKmMwv9 z1s*zptOdIr3k&bp_IvXTQLt1A6lk#30UkYZt1eKfIJ+I(H;F%J!pF(o=H2J(VwdZU ztiEmv=%L}kY!%HN=eW~r)bGCkP<(F6%y zEKQtGw%K)_BN{Izp0`)!q_a`o8QQ^JDrZ{4P0l&WP|D6!guu%B_vl~UAaV4(vcxX< z_zjxg)CP^ZI;C9?#zvHpFYGaLHD!7`W?2r8e!(K*eqY`Xr$8LaA^cU)7U~!(OK=#1 zH6g3yq*)5pNF3|$wMY=qvmqlr#)mQ;r{6UMi&UGJ8i)IghIuAIKk-Dolt%lJ2mnJW z6D@{oZFWzq%|zeT-|5hpBUwFiI>s>(XMC`RP}`y&0E-Wf6B`Q_vE0-xq&QqpUGvQR zhBd#^Z*US-6DKKVgCY3J7uAW7grA1Y+}Ol>LSDjH(AZAnpjiIfs@Zym-wXwj`9!Pr z-HuMNdwb0d?Xw0mu7(HPAwSPXgziyFx-59`e$UOHba|frh;DVFM8Wm(OXqL^iyKaX z-<>VC8)|4w%Cdb~EE0Vo=2FJ%5Au;#?GfHw_ExNiq*QMZ&9kB90}f&+2JQh5)oPJZG60etZFPa z)f-XkK(ffOCXuOREol;Lug!M~?_$c+8P9kq$%F}^+qf|u6tK+{ce?)c!>h1IgvY)1 zBM^xBD&y-8^5$_3oeBuAf?j4S-0OI1lEy0`XSqE4_jr#dW-SVEgI*_#6b);t*m^?M zn4(dHOqZWPhgI!4*s;3}7BwxM*gCZFO@9_0Ib=4NY8C{t@C=x-(Fs7fa6zZ<9E+w|(f)d%QOSga~Qmnr~ssR}@s zN-ac}Y65gA@lSNADjQuY{ux~=b9@hO9$l(o=%_Fhj$@-sQ5|rYgFf!K7C@I8s&`yc zm2ePPE}^h|6rf9Ucv17{(hdC_bSXjspi43H=+d827!xkK)CHhRZTJ?VOV>{{bgi35 zmm(IRO9$=)bg4`s+IemsUFy+p5&247-zA~b6Va9&6+aw@Nf%;jw)t&x4LBV?qK;KLzV4qJe0TklIN>S3-?Du?N)>;ODXh;^zgDT$e!?nQH}1>%q}?5a_j>I$PODD*7EjLL6r;mAU`0uPCtwf{-}`XuD^2KwH@viOp$ z_~eSz_RihK!ySqO(JwQ1TgZ9dGNV>sQc4JvD!6GeQ6Pjwu7$XJ5u7zf9UCD@VlYZy zfuWL3S*E+ntt#0jd$m6Q!)4Ucg5DndIsUVox?CG6o%BFBy!+&aB7GCX5kBPJkUoqI zrlkkkdpQJP7XPxlHsW3(eWw(Ef?#U7 z1nTy}hYp;JVUY9-OY!0)1_SW?47@~4T_UC~5mR@Ufp!`okH1=#2HDF_(IsN)5;1j& zn7Twvokzzke(La*OAlu>{=s${>~K_2Y&0bg4Am|XQHL&+;MwJ9iI}=XOkES|Vd@m7G zmx!rL#MC8X>Q|NnOT^SAV(Jnxwfo%<8qn{4I?rYU+V>JMb%~g|L`+>Grn0w6I3qI{ zlez09zGrhZnq zmx!srhv2J`d9jfQM+W=qz6vDw;x#d@2FA%|ke~}Su!Z{eS4JWniTg(`tG?90mWZiK z#8i&HJ^SB6TU;WhvayTYG+roW|8Mgm_-s$VL`+>Gre^$oMgC#x@gl?%n-cSXnV9-b zA1~10{zXj9Yu0p10<{-hM1k&>;{*gO`9S>y{WQ?6|Lmj_YC$Ir(vo`x#c|aH#Tis- zPCpH@7c8HE4G?h243c(XOU%jmtpK1{O^gWb|wrC zF;#ZjM+6Rtsie=uRJCsqQ!7Y+5>w${5mWOP5K|QZF?EzfOzlU0i8S|yS!R-0=xh~D<}N-6Gsi^M(HyzUB4om8Q+#_MH9$f*?*(dBl(<0{YUBM1TItxy=AC zdcP;)eHMX@7p?vo|03NRCb3U9Oi>hi{ASb?n=dLScx>CdkxlR7AmaK#W0cLXx|cip z;*99I{?703l2Q*eL+5hiB%WOzMNLoKfItsBBu>PsxA)zYFrd>a(WgFC#}|IMNrgd4 z@n*jJHToy>;{lfVACv5p#Aj3JAE7aDmncAHaVi)nO58hFt`-Cmjgws5BDOHUxXmZK zD9snKg)`(=q7ZlCk^O!!oDRy}KW5gK1EkXg4HT(k@DU8oipXZC3o}cH7 zTCw?}huD14ZS#ClV^KC=l=vlI)P&6!B>}#uE}JiE3izV@U-LyxIebx5Ef@v^_@c&a zzNiV{i(tFM91uzNjjPFM3p@bn5VDzG$||^9@OlggJcCjcmSX56jnb zl>eK2(Z7S4{XlXBPsSrv8YUAkMzfQNSZm@`l89UIR5D&+suzlhGMh_)5DC1Sp=#cU z9?Z*eNP@rL*z`St>)t6y#EV^S*Of#&qipPs6IikWH=cyhTB7gse-9)G!E?ZnllVy<%8>)hke#R zekHKy^kEL{b8@9D8}=#36HeY1O=817Rl>89IIvHV=%QGNK7f7dAIMHLD6ch=Q&Hq2 zs9-{*lbJ0L)jGNXoAtSjAh$&)wue#Ek**47#OSmK605Q{>kQ;6Gg~#zcqz5^k^$=z zf9Ze%A&A%;k)(cja=*mmVu*UbCosV;+N=P3u^%?p+z~>a=||)43f0d2$_J~rXc;Z8 z(uH|2_zAakpXpoNCD=($vxsApW7bxJ{=>iN-LGAz%)e^;>x9X=3yPHisC5p%)PK4p z`CF&?+}fQq#DO+jkxjOimM3&{+qAU29xzMARq8`Ud#eX&l1j(ZIb4(AD~C3nKYsYO zDZ)mCQ1x(?FP*mMAJpHQC{N-^x_(iRVlFI3oV!8%DGD zLB$7giD=4;2WHzFB5EG-FeG*cKQZOKAICHKewQR7Y~_fs`K5Pdy(xTE@3LliQuo5P zyiwYRK*S#`Vdxe)%Oj+wwF)Kd(R2{o%imNkI)$DbmurXQxsLDa89U zrNZF*X~e&zm{9!Z!tr!|BJsIxLubK=}YI+59;2jJv7BjdjqB zfs(;#*tl~KM{@)<2vh-(0N=dV=MFs}c|dT3W(?#QND}VQ^G%4r=V*s44EKejPqS_< z1tQxjG*7`i3?wx-tEAy6n@CSi;ybwAu&ga6s?trqDYhAYJd%`ujJ}ND{Ve^| zrM~_k9~LXR-c7&t9KGXmfXKB=Npc=)r^=(0q<#)iIe9Kw*F*ez6J3+>+!J5>F6#9W zO%w6vyyd%pZyTGKvD;v(sU@3P(=RVyjQ=EgT$dSWePqvRO+Q`4FE`yupG=@p(DAIN9^-nSprim)%x>Qk=ghjqOt|gBrO+-3~SpGK{pU>fbg7RRb zeBFs7cTYvXescHxiK9QCrNa4T?K)eg`e?!$KBJwj56)36oAFduJWP}*wT3}%ae_j# zc~`dF80_UAB=Te_2FUl`hc`(DIbbYckNvfTjGqqQ_8jUR3}2Rz^WLH_A%-e>kCkOC zzl!GApUu0x6&88jNi&;luDEMZYRs^E@;xvAWofw@lEa(zP^1CjeC39-Xa4>B``1r@ zzI68NAD_FjzbhQ`;SLw$MzzTgAL3x9=k!7g4fa8(bJ`(JFSIbQ7cYx*NBM<~&_Xf& z3wQnSc~*0J^N%z_+(W$V^C7+8$YZZ;Ahr-HpBq3$lH>URDpHJ%io9UP zMnxLTqax*{*r-T2Kt+;1qatI01=&Wid&8MuE4`V|Ppgd_MiOPV3g(w8UH;Q%E!Q6^3BPSYKx=(;3zd5xv@^>Ri$i z+tGC6D0!aP+uoZ^R;gtj%;#Q2!%zKu!#(!m>YiWTuS~pH^zaX=uddMEouqhLnk_7O?}qf89I>pO8o5O@ z3HlBNMn#N5X^2>5U4&kBFtWZmKyYIBSpxXpVF zokZD@aa&;x>~#&{%eQXdxqI(t7d%0MPort_mWKKcRM%{eDk%#t_{&LIfl~2z9neOe zj~=IO0~MHR=*G^KdbHuq7F3LG`C1$G`*q7uPCbeASV z{_Jx#zZFD4k>P4FAe2DJfCvK7!R|xn#ZM3;Agn-R3rqr-*vC8u~sC2W&V&B|Uw zNDq%ZP>>~)x6P%nY#*c87gk=g3&HfI*B%N^WAZh%Y(d5|lUh%B@gCXJdD>kiZ&~A+ zep|WrZEAf3qibZPgdJ3cMJ`;wF>#XQ-S1VtEx7kd|UoZYxF4J|IqYHAP2 zw{|)pat<`@mg?%U6Dn!X-df)~VxxJkI!gEa<<%ODK?v*0EepQHGl?qYw{Oem9ERY> zA3Rv5R-_;-miqLKhN7M#{PmkJV@O!td#I(VFBPk3TV~T*J&=Ld54|(gT669I!7^QX zZCmYdKGFG*_i$U?`6814NZHzB^%qJ_c09Q=eC%M&rAo6zw2bYy4>hPc36qg_Xo&rdk!DmZ$^j(qUCq!?*a+0%k8)bkRe&t6>^bXtAuWP(knX3vdr zMT~Zu;i~4&hnT4A_wL3#Y`dtx8KT{t90C_>;&0>>3NUFL_Bc2ML%z=5L(#2;95 z0zn2s1e|l=3fDnsLw1;BjAaUvVq^vbiTKYbke0HHC zEUhHJOq{PM3tpO+2N7W=B1`IMX>rJgD1m+D9ccos+jB~f)1Y0E_@=%-@w2Q5zV_xC zNaavCVQ)unK4C0^zk47}bRq)9+g}FZMg1JX!&{q+mYSsq%8UvC;(*T$&KD2{oQ4UUV&G%}^Dl@cP^>^-1wsqN%nu$V-~Zy@{MY{|3_3vt z1Da>|zDvLS{s%QaAu%aAC6$(zo{^cAeIO?{FP~meSi~qUDJ?56UnUEc!Qv4WUP35{ zcoPICQ-9D!OcHXu`LH-Iq0L#oi`B&2dV0NFXJ?PNz`zEP!#!i--51tjPK>cSq1V<4 z4~;iB^WC*Qck}-2^g|outqB(PnawiX+~knlYa4<0vk;WkvlR-2@^~7d~nW5z>cGP(;$36T!2slCj|KHphAJ~|6goV zzWL=B{t`q%&JQ03|3MT~R5GioYijH24>mL&YHB`w3`IxD@8@!#iTK?IJT`j~+?4Au-RN zKYVjXq?PZ*ug`c}gk~ZiJUVha!JQu|*nQ(-j|Lob>TONbPGU6J+h=Q6nyl+l literal 0 HcmV?d00001 diff --git a/admin/assets/images/index.html b/admin/assets/images/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/assets/images/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/assets/images/vdm-component.png b/admin/assets/images/vdm-component.png new file mode 100644 index 0000000000000000000000000000000000000000..fdf41d78e35d7209464f3b409002e5c12cb1f60b GIT binary patch literal 14590 zcmZ{L1yqzz^yn%`Ng8wtNOyO)q|zNrmvn~`3QM|xARPkI-7UE^NT;xLD2w1qy>I!w z|9S7cbKZ9jhn+if@7$TWbLYm)M=cE{JREWy5D0{)qAafi0-?b`pa=9=7y#sv|0f6F zAGW))p(hCR_zCLo!Srg#H3&otQjwQ=lhY${;m^4&MQ#^HE?n?B}I2TQE zh%7LsMB?7F50&euOWZ+*W+ZVu#qX{a9-iM!WT$uC9@~}XM17SI7+B#MSgE@c2e72n z59cvKAZbS|m|SZ}ms8*G=hmYH??r!oAWSvZ$JjM4UPHqBzE3;?NWV}ppq~75WlE9g zMryr=^)rlK(cof4=_2s$L6-TN+KoKWhL$q|Zf(-F-oQB51mSTD2+qZ(V-MU%vmO=L z5kv#(i6h{0Lzy;W$=CE;1l*PpU^j^r?H%LN94r7kBB446WVrZGuwXdk1$*E`Jg>PW z5jU#h2wmSz%CP&8T84}?-SqlobLR5)CuK1qP?z_DU4Q6>8HF?2qgh*$-Zmu*V2Pzm zuwb(9&H~LSaS5YequZir#8ki1!Z3NkjbcN2I2vp?8k>vT{2F6tUU(3w$5NQKG;EA+ z-R;Q()rj@`;sG!^m0%kreysn4IDf=`VCdWTGXOMuVZ=f9xG-g3!Bhns# zK!ooY`f8!(e7%qAw*+^o0Q62ZZyO{rd|ukgS>)omh7~1F_FmrE1?12j@-E2swqGA= z49EZoi^7tO_2{SR-Tq|`9~WwX($BD9C1ac$;PrYiaukXe7L1h46N(!5_n2x((!!&TqsKr2WF>DfpnQ8kz`loKfz~Vn zj5}5tGG;b%U#Vy?FnPNwr4xb@Gg?=m5;PJr|)DWZWN%^zZ$=MYGznG>Wm#|JctywhmmUx*ktI zBv37$N!OFyNugU`@ApR>3(Sh_i%F@32a*r(9Ag(=`+g>1XoNBdElGdS-f1Go- z!J(I^db{0uVZpg8BdyR9nRZKM0>g(^oIOd^%2hg~1#{@-vA(dSbfztYXE~;8u^xtr zMfPX%Xf>NHMiz02nXz#mNF1wCJd@%WF8EH@!ASATyewJLd0d2zF;Vqb6FjYG3H(5H z8isSzSKn6YfTa%=8{qe)RpiA~3#5C>)G=|ETWa{C`FpQ8)`HaxxRWla6x}yPZ2sVL zDv`=5EHl_rX&cPoUy&CRru|A!))VJB`f$A%N}v!=sSt;!grCDnRYV$X?MEpk2*=st z@muy7KpBOr{?8tzPyCZ6{&hB$kItr;oxanMmrXDK>;y?=z|9dcC8{^~h` z+4IRUJ|Mt#h}6667ML`&kkU9Xk8LcPYHaNvW229ZycNwb`3Gp!zc)S-SjL@)hgg1F zg@_GpP!`qTunkmXXFPFlnF`p2XNY-o`1c1%tT9%)7tq=KTG9_^`DXsw&&osMK6_!C zw8=gmQM|A*`?Dcz#B_XZWYaZg1n$~dkY7Im_DOOvljdi0Na&|enBUbpDBH%(Q>R{x zA+`Bpq#pl@JXG-Y$f>GFub5P%7mCeq-tKu@uTg1Up64O?(|=UE37AUA5lL~EiijW~ zOZuQ#@!#birblX?Fp`lfqB5IHv(%Qnyql9osu#?IG*Gp=B#fOB6w{7Kn)RDhSzU}1mfHl0D)XzZEOcUL{)UN!YZne;~(pRh-Y7L}XuVjEZK;JL(YDYFAz zx2edjVSJ`1A7(N=uxFVGq@FT5LqFZO_1Ma;@PwV!hYvcVkUsRb7A*!I4)SsS(h3 zq$;^IB*vXODE)d(UaDe6@Wz3OUnvC^<}5a!ivJHyaBn0uC+Sx^_r|*q%joc6{`q`i z)x~`$^J0R1kccUgPaX0%L1{^w(6kNE;N%paLvQ~zjSl#-z!Yl(Fjpfe0teVEU{dTsi?wQnN*?%4ED9aif?47cNO_4ZGL!6lK!lh^5 zy0Z@*j#EnRVH)FH1vdwSrN9Bt6eoZSe2sTlM220u?oVYbymqkb$aygZj?1m#oKbiDl<2*tr}!ph4p+vXX6#$uH}z8d&od_C>L+x&1bgUH+?5MF zN1yDMsGi_vUj6aythuV~yj$c${39{{qM!48=PEQnaN2me%PABiY-ceVW1nZvPkQw@ z_#{+0AJdLYvVd3q3^80TM4~co@jQL4m?5|Ml13) zFPAJ)^{0lTS`nLYsu-M8so7LZfhwLwzMk2hR%0*+wm8X+4|WB+wYl|s79(E zfaH=u-+macbS=!6PnQV5=et8Pu2QZFKmLH`NsG!g^m$mzv@jGDWr)d>l9|T8{qt`F zwWBsZj-PgNGlewNT?N2TE@J1EG%s&&?ruZdLNR`Yqia`aFRua{Oh%@xDf`UHqO0=N z+iqC?x)to{%t#i+7nh0!uG2EpmSMs7KjcNY&^kkw|>ZyGOx=w}8 zu0xTT_efQEUyG4RsX95q29N9d)^m|Aqvas7{bw%ToaOCZw#^OC^)hycNMul2C|9Nt z8X&}QS4Cyr`39@moVZO2cKb?97LQ+j#}&yRqlOf4uSX714>dfTF@0F~I)ibbg{zDm z_SQb?!r)SY08lRMXzG9S7qUWwtPnX~vL0Prj}ap`#chZ$WMIX|LD(ga0S)6}lyj<0 zvike2u5pERtqf9o{oVV?0?}_ri{4Y>)D+NcYS-Sc6s`TAm@vI zz;fFuHot=E)Dma#8pMyrFt@(49_Q3XK2NLd{V1ofvi)4~^M_5rU(C&(^?#f80@1Il zuj~0~wxamLUz~JQf@VQp(&PteKIF?_kZG9zsgmFls=C4bI0oTmz2GyfAfWOxuVv3m zlr2kxX>NK!Q5jh=XE!z!@{8t(~eC>0kR}sTC4u zqpUSqvQ4UQ7TNh6*p8GIO(n)>HD{)6{ZGGVg4$A5Qr=*M^yEE)q1cJv|~i~4nipIa(u(fK5EmtMqI zebPjD3|3vf;;;pm^(<`!maaQo|8^%~;7Hu*W_D@ncvYc4EK1qk#Y2(|KChfIXBRrF z;2ShOKsl#iD0B4@-A->>GIOqF@jR%?tOAEAM@nrNrCOxZS9=|x9iL2VmUX>DE+3_l zsZVDtdr<0KP7S1XlG zTk)K0F%kThd}Qlnw8LK);3LtQX6b~p8TeWLe%M~l*p0hKKdq&o78Wmn-F}mkJXt7c z){uU=O#evzA%byY#Aguv$N3>&exm9;F)woTT~Njb<`zm(x{-X-aSmM;#;K;y`VIX{E*T>VXMOrxRlT|xP8DiU%7KITWxfn8Jmr%CZGKc_Ys0Lx*F>aqjZ`0(nWy{;oHejACSTYMBC@LEu3_nA$i{Bh*@*Bk zF|w2#;;6N9@x_9Ha*OShSdIJ|T9=&E;PW@N7FF z*#1R$FLR7-;dt0A$}gdG7lN@17GO&G>SbbIL}AjtPmKwn!>YkDg=3E`z&rhkL&=+YK(IP2QShCz(6>;GpmdWk7?t`3*~T4j)*m5bI9wedOf$Y9xq5 zDb2Q^An{NX7aPsUMGxn-5`SNWDbK!nt9zTKe2bn zk%|84lS2O5puzXnpfFPVRCBKLv5D#S8lTrD{UpL4JnWvCeR#RTFWvUtucE@k8LqK4 z-5QNZar&6Is=~s)ZsA1+I?(3qI#FTS@>I|+U&h4fhe3~6JG&o{n$8vL3R37Qi$>Tk zyB5y)s3Yz09s6bct1pnh6()~0crB4Tk!#jKJc$7R5$N4^F4IjlMafR~*DVgoR}akI zPLjbJ8ev}1M9Uw3%GeN?9Vhu2%gpXUDg3356^aFm-bje%HHoQk{PIS~o;LFAP@%}k z78-_huo7r+CHZTp#q8Q8dpK{1jEyt9sXQNjkMRzwNal`HFv2+2*ulTN?{y;jx8-U9 z-`bysq>V$q2Iqq4or`a>F`n^5M3>M&(00!z5Sy@xP-JtbZo@1X^_(_&?!{B6{j&Qx zW|sC^Qm#opV5)$cd5ZaD(_C!Fqx&g>r5-QV5zt?ag?ssVT=R z$H(WXDf=xnoNqtztndBxlr|?0&0V9%$|xGWi4CB#)u7g>U%=c!Oqmc_$T#wRo7sOg z*!=vBxfbWx$kHP|{E;jHMY7k%49>&ad!vOJ7TzDOMw4v*P30F%ZR^Zt=@)4&XEF47 zsO<2pyKZ#=F`_jrX7=^e^~Lp1jeujT*&#WwU}`-$ZqYwXW=Y z%~6eBag3>A4ky1tk_Bv=DV7vdEKDGaB01?vOxiw~kO_h;+(+Q^wh_8K4NY0OloWn7 z3p)G+`-AB4>1Jo4dc7v9#LF4hQw`gxhM1Q-)#GZIAA`qWhutDOyfiJ>e3}fC0yT5} zCTORdafJ7KbFaqpYfP&7Qfww5-BtK)OU2MBhXnLGQ6EyPYf0qGnEp--+!5UwoyxYC z;Jm)h^`D8SzR)pkMM=5&UzBsNR7;N!hh^5iuTI%Qzm|4GX6|nU(=;K-9Sx=K$cuH?? zzm#fV<5ZwbBaqq8-0OhS9V@n_B1Z%-`J)K=GQ@!hAYXAwJ_nW4${Tqo3QEVdE63=K zxT51Gs%7Diudd9_ekx;p^14@Dl=5|NT6_pvq2C-X<*O(KL-CYfxg%bty!S9Z@Ogou z8FHtF&Ee5_Y_EGk_-N{0MP4m<$icI1QWDw1jXlxxGY2sk# zw>W*Q2_qNuUK9IiP|FISZBGI+C)dUl-~)XW5{6X~rG-zSq9s^b@h*rGJFy0HM{VS? zkJJz4mp=gM8X4!2`tNB|i6mj-9gBp-0;<*f0Y_^cRS)GnqwEZfHC&`oWE5~A-$zc~ zW3&}s8+j7^WK>$VvUd|pV)1qlylFxKrhF~{WU)B2V^`E1Ri z+j!ZHK!^7s*X{{$j#Ooh4SLQ#l~5NEtY-Tlp#`}7vm25fMga=iqv$@Pab;c~B06=N zk4Z(o?3s=9o!y(wAjtb9-D1RVN*6Ip{XxC;7id@?#TPJ%WWfZF?H=^G&Ejmv>S}l6W?h%;%l%<}w8&c{mr2 zPmLfn7V-JXdRziK%kb0$`LpaaO(m#J*T3mv4X3~E>mw>ejIpK?E z3Q>oT@kR|@=cy(VPWQ(iOROgl0VF}QM5)FMGUrbd2!1yLp>xz1N6+4LN<4TUF*=18OI|eE{uxugX|w!E~4@JBGD4?_eX$p zSX1Kvz-ja$JT!ss{HL`K@1wauuM%|hE&7G%tsFEf?URTKhI_i?N(L800B-ca=U9$M zI|+>CsHm{?85S(N6n*EVTJ3rlu+;JX_hIJh=<1_9RSTbvy)>$x+us1%q#~3CI95>H zyqIH&d3ShNb9mX77RTemwIc*gIHNcGBRbRp^=(bZ8%5<3{%1`4ov5(guQzsBFBxw`XoX$Ifzg( zZA<2VT0CgGPl8yR??QO0{@lD2d?tk-jE-a$TOGY!ySNmCpK4nX-p8!}MpZ*{RV+HU zEHs43<^RJsz@w0%ID`{`ZElJOK5I5-pvJ?`T?;80QuYj02`FVZ7UJE#qGVhNv9Li! z_B-j==^dU_Rt@DlKs}lw?(-Y#fWDzy)^>27h@>MzMbkGZcG%IZ%G1`V58;ikt_A@s zDU$?1rZojT?Fj>}E=EMU)Xoa3FNYWGXqcpj@PxsD7QE3@&kz6>c21ZYP7nEW#&(}? zqLpB3+3B8gwpiP@y_jepD}C6Ty4!NYs|n-cafJYcNQ{{8lv2TJCLthY{p{UO1D{>t z=Gv8L!uqLSK+0oFJ_HC=ktf?uiHQtVUA)lop#N7(CR|E%++6@1NAl?XT zKIRM^6rVWm8!NYxmW1Gq9~!2@(WvLI!{~|VTA^UW(+}$%6`1jJO5RJ?o%MceahaFu z$n`Hu!IJfG|IQaK-6eW;>|dNVglQHNiek53wRDsg=!DO1w=6>gRmv`Jq09SyT2Xko z_T_1ztutelzyP)~WfuMJ)I}x`XLo5riO1|PJ(1r^pAy^mEYLT&FFJ@3;^t}5a|68~ z3_-&i@i(2ukb`C+)8pz9#HwVG$@ooo)+_0!+qXRH`nIdkRUg%*V#b!7!tWa=Hv#<~^krXH>=14KSkk=U~UO<~iCvm>Y*TgemfoNcOs|u5nlXP)58Io3j135^lq93$NZLLq+=Z86ynK6!lLvti@VW> zXHny58h*C$D|tJmx)DTnz&;CQ^3--i_xSI^70u6$zhvqEL!$!F_zlyADE{<)h2TVR z;|OS2;LkD4S3U=eDOyCavb2Q|4%wes>Xmda4Em!ORs>Wt&FdJvgcO8r0VO!~ipa}N`jY{4?TM{k!)^W!rc{@}YnbuHiYe6d^6UF8Kcj3h>(S%5vZv^Rp!c;xv*NJYAJP+_clyHc zYSk`sFtZ32v#m`Y{^KSvMu#^q{_rZUV>i~kk>_{T>#XXQW)P2RKloF91bx(|s7O6$ zDVDdSarzkcmI^ks`#hsV*2B;1u_WKJsTwBq zCQd|onfE~Kjgm|%Sfb4!En=sio_ZzWX7S0iuHw*k-Rq{6BWLE#kcd+~WTI#VW#d;D zL-!Xy&;-#lss0k29*1TRBj+f8(6cAdZe9$)y*`>WqEYU(>)oy-NBlL6M*L!OX%pJ3 zX(6;0>kzZ^2e>p+lW95VD1}$Va?tcm+Q=jIV9~!DsVrTQq&o5jiY`9rGiwXm-bSwc zkK>gpU!rBM7ruR(RmPX^D(9IB-X&-el#6z6)p$oPTh%myzk&*1YOK z!CD_ue-Qq{=*fX;kW>?#+QVm-+ffXsmfcwE*7f`gfkS*K*6}&E7^?#WnLa@9n(*h4 z$5Zg(`@h;Bvhc#i(*9wGQKUqV)y2D&&`E^N;lU&-kZK+-#H@FY8j3laEXzqr;KicO zL%l=m?gq&Ws;MN20bRXFyT>fJLkddnnu`6pCxa=c1B_&)bI+zLiLU-GX)Gtfoyet> zhbeTXDbcZu-Tb}Ep;kh%)wsolBmo)$#tljw?_2Uzx%^kNJ$`T-v$&aH`SsLq4|&ft zn?WBDD2tZzQ->H5Q79#AgO4?yFZ&%RlUwzG)o7tcQK+D9y<Uc~OCop9OC{X0l(0tOJIGfUBP~ zJi?S$_DvH*PVxrbkVutSz<@7lbn+uofvz-l->7ls1a8cZ(^^e{yEvMB$W&{9*<<7e znyi5$Tj92TU2&jjmDaI}?wSmr&_n(`nZ$zobSfb%%MXfn3iXzea#SF+FSXLf-76*nu~Mv+e&=VsWJi-**teZ0)r}(9wyo5SKfc1#J?sI;b>sGAZteU) zp413X#OMFm{1RB(7QXYQEKw<~!@d|B)*WW^$XPLE6K^qD;gD{6nc|xlu06VK;8rkU zk2vS7{v{m*HE%!FyhXRMV8StTH?UY25Lk_+VhWxp_TfJo8z-cI3DLPhR`I3nROroE zG(1Iof`%yLliuZKMzwfmKY6H7+AmQ1_2xnu@$?c;Pq74|A)awN3Cy+R(CZ=LJOllZ z5} zG^xd@O{BW{4SnMGK6%9H4^MN}5xXMM1`uxbt1{vg&KsIX- zxuySd-IJqay)c4&+~s+nrTJ<~S;CIJ9-i*-ELU6o_D;og30ccPO@s+Nefp5%8^<72 zp(W#lF(>u7p%Hw${jHclkMK-{ENvoYhr0Y_P4!9?FEd|2f{c9x*t}TR-X|iE35p_n z-3REydBIAJpo5+o%BX5eB4ZC1NDI5MTulP6ER=19Q10_QKNCF5g*46Vv>&h>5*xTE91oIe7bNI>oq-hLq*ZnG`OH~)lPl^CWzmZ2972Y>^E-# zSxy6+odE&7Kofm*_K(qOWG-ix-X_cRw-Nqh!>W{RABkrc!RubIcq5qOqNC1-ILtX#HQ_59E=ZhTOwKkj%72BbH-((o5}Zb;fYTU zoJRQ+eaA4NEmfwfEC!5V*fu_knPCkd|G4TO*@%dZ$Es((xUyrlTvQP| z_9ScchP}7m+dZ`RO}6=Q061njgwXJM8$+Mrg8tN_z9Cb?YV@}?fj700Nfd~b<0WdI ziU-{vG64aM28*>GsShJ&&*s3YzBhC-NNNNm+-rDjs+hJedAEh2V0E&FGmF=q>yrbX zri~-oBosOVwcf7Jx{zQumil6}<`~Sz!qR2?W|7O6R2?Hb)<^&WI({4w_h7Wi~jycRIjdGc|O zKAp=IWt}n$jjB;c?5sxLq$WHlsAH?lv5WUO{0eqNqtX@aGJ~s8kIa z^Y+YQ!x!V3yB4Pdw=Bw)@sE^F5oY!$D)y4gXM$3nM!Rq|%oaQC`Tl&`{HTIGijL7P z|2F4Jg=V5M=tMyBiYJa;XiX#fNorig4@I=P;3_~M#mYmDNepi?w80iqan+P#F}b|c z&agVs##b=T+o@j5QA8pQy753L3#_(IXPTgEq0uGM>7ATCx{SF}YQtRDht0J$Be7$| zoHk}nI5N-F-U%?#d-ej;1M>f1n?Kh;!|IKP3UMq{O>GChn@ zEdQ^RX=O5p)D@i!nKUhbKu*(hwWW5OP^qhG7Z`9MvKah*(HrwY(Za3qs~dFt(~ejdeW1wvIf8mACr5vntHL=SG2$UP z0=u|XFiv|~a0F`n8hhMl_NcvYYW;10(%tOhS)0Zm#w==YG5^-1gkudbRd}m$drAtq zv@X=rP&BIkgi3vFy}+qh4j-nd7$OCo3;{4mqk?wfk2Bo6!eSxdhr!;l zm%V2yfwdeW@Is)W{ZBYlYx&oeA29Kqm~XLXcelN@%^)w(j#~y>suT{RGOjb#Tk^QJ z{X01y<2d{|sQVLVT!!Om&9{>#WTi5WD48G8b9`lIIQRX+U0nkDbmVaAXfG>eUqo`U zwdqPnfY@1#>fg|r%+bgXsU+>6>wwYu{6`w}K@fA%oD>5yT7_q;f%!wLw!B6S#f%BI z?5Dl8axn$Ou}AxdsU~Hu>t$cR3JBk^GrQnHoplqZu3n|s2A6Ord01CWedn${T+(@PmzrbJchbPzf(A3R?4n^N!lA!UagLiV`!u4I@O?W`#j3y)cC@A=yBxbdt} zI5B3~RrQKZw^zqn*oX!OjugFD#$`29kOchZ2)#)#cnP0v)<-l|1m@Sn>8H2r6=af* z2(Ahp7s@>nWb(3cQuSPIOlkD~m~1*MG=Xs0dH!#LleXwQw`It;x3qP=NT#YvQFo|t zYvP?Evsx?e^3ZI$_=M1NwR3%&v_k@tH4gZtzk8S>{W) z=pvMOOqv`~n zEF!$5VIbUWLk(qNr3)9Z8EnkUh%t4Du7aEk? z%SEv&05i#23%HdmXj6#9wZp@4b$^0s=k^uw;(y?ebgsR^WZbnG`;GVMhhtwpkK-mx zR!Vyq(O#zYobMIw^k>+&&Ze@T8BZaskN5c}sZ|=-xSUcIy5uDv`%;ykodhcRaijd^ zzFPV*0>E0}^w`mQPo-=7{OG~3EHMF8$)=uo$zDBs*U3Y}hepr@-PT{Z@`_N-z%>zK zWFJvzi0!LIyUrLlJ!jcltN3}=H zu(xGndGv(;gtfuC58a}oo4ZOko`XKchz(gwXd3xOwS`o>;W9-jCMW#u{?QMklDeE& z4K$K>C{wXyOX4A)Vt2_I(e_J6no;VZzM@#_*KJwy%tAA1lss;MCW@a0l*0K#hzq3f zK50LXLyTLQ3k)Lz3xxv&-HW0|jptZ(%}TEf6T=D{;WUU8g8J=}_Z}vaXbFlplnVz; z`|SSt3zpLpSK4qL=3{An)C z6Z*ata7j|sJ{F&yAb$2Dul+=Mp;7b7y(UE##uiYZc{I{RXw7LN1pUuVM*7GJscZatMw)+-Q9cG06whr%@NOHbA(Eh9w zkQ}+bV?Zc9kA@)+Rz3tjIuf6FOn|?|?3u@Tz(_##UJ|4}(rf;uHgU0=AeHyhUtu3( zyu?GzCgCW%*PAkJ!qlE1kDPa6ss5O#^L5!YQK|09QVI*r7qEdSGqZ~HIAvsEyM~g* zkuw~c89Fb<_$rP5yZ=eQi#J{URUj5hu*i=nca{f8iEnzf*JuvmO~P^&-vp+YUZ%4N zjwtsOLdPrKj3CSof>V!+M~HaF&V;G$mj##@yS>LoN*?JxJT{{7*&e#}SE7&m1SWN& zV}2wS_f>{Zb^S&wLcC+-&?iF#HhD&jNP8QR5XUnwEK^Y{lt`=Jm6}^#6Q(4>d-FX$ zRoA@sMd?njSkzR`1q;{TEw9I9xHhv+r5%`VU(PI9!8(lVH@Ut+q_UGUWkGI$%uIgA z85AC3YCNz&DYiw2IMZMBLr-*AiXM9_SSf~H=Xz7M8kKEYG0-QF4%>UNuMa*eMuW&J z8ojoUwv){=)7^9dVmDD=g-A^mY1(qZt2fGc!}%wbHnn||a| zt+!qY>CNxhxe|rfWpmb26_MC>4>$kMw0!@oSjp#h|deo{^*yH;ER$CP7~LS^0mzo|3M#% zYq$Yd@v|(YGr z9qwpR^_OYmzwCD{t`>ms*XFxJUT1nXH!1jme~(#o&dBw4U}+A7cv~m_`85#pv*GW) z(Gq_1mA&xhRdSCzr`DQb<5`_3Sc#s#0LLpWMP~y zUbdeRf2`gzsst8Qk}H%DE^Bs4`ub>}U&XbJJ7;PClPcze`d*A3z^Q6_*$RgE$Cho3 zD)DMwQya?&xazu+mX$}eDp8l{7d83K-IfvKTYpTXtY_?3XeE~5+Wn)gX?EBN$zF;^ zh)>LeVd;y+_&tl1E|e7}j1m)wBI2r02jdg2AYyR`Kv=P|Bbs8xR*y2-Ieiq(NV6r< zz#j~I{afTJ=`L4x+j^a4}pQ{O)O@Ryfas5Z^j2VNmY6xvNQ* zMGX@uu2x9SdbIiMrM zl$v*3x*D5ShkCX}YXfY|wz{|8KVSb})fjdtH0eDo*yOonAR8+8^#OPhVU*4L-#Z6l z0I8*?1oeI+%mBE~yZIh)@|uJ{0cDVtjDeyl4TW{3JMsNE^t(?I? ziR26Bdy+p?D%ll(%>cONLH92-jDXwoeW|AA%Rodkp$?V!qqPMBQVfGTkGG zkY&{gl*z>kkW63zFM6dMW< zR`@3Y$jd=^atcGNPyo<=01G>8WC6ITg(@X`PY%`Mjb1`{q~O-~94SwQTP_|w+Mzh= zxMx{;Di2k*HIBdG$@y;(s`0q6pD)5uIX+NgvUOiA`(N**WM@=SnpIRm8~3_QgTrl= zci{SiX7v0((fj+ZxbtO3s&i0lIc5tajb3Xsr%BS?ue@}yTqL-TU2 zuHFF?iX4a^S2t?a4RFK8!JRG;|Mz86t(p*?UDEqUP+|WZBjM9PM10k1B-OraSim8Y zBets \ No newline at end of file diff --git a/admin/assets/js/admin.js b/admin/assets/js/admin.js new file mode 100644 index 0000000..d4bac38 --- /dev/null +++ b/admin/assets/js/admin.js @@ -0,0 +1,20 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage admin.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* JS Document */ diff --git a/admin/assets/js/index.html b/admin/assets/js/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/assets/js/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/assets/security/htaccess.txt b/admin/assets/security/htaccess.txt new file mode 100644 index 0000000..0fbd9bb --- /dev/null +++ b/admin/assets/security/htaccess.txt @@ -0,0 +1,26 @@ +## This file was generated automatically by the Akeeba Backup Engine +## +## DO NOT REMOVE THIS FILE +## +## This file makes sure that your backup output directory is not directly accessible from the web if you are using +## the Apache, Lighttpd and Litespeed web server. This prevents unauthorized access to your backup archive files and +## backup log files. Removing this file could have security implications for your site. +## +## You are strongly advised to never delete or modify any of the files automatically created in this folder by the +## Akeeba Backup Engine, namely: +## +## * .htaccess +## * web.config +## * index.html +## * index.htm +## * index.php +## + +Order deny,allow +Deny from all + + + + Require all denied + + \ No newline at end of file diff --git a/admin/assets/security/index.htm b/admin/assets/security/index.htm new file mode 100644 index 0000000..a1e3c00 --- /dev/null +++ b/admin/assets/security/index.htm @@ -0,0 +1,9 @@ + + + + Access Denied + + +

Access Denied

+ + \ No newline at end of file diff --git a/admin/assets/security/index.html b/admin/assets/security/index.html new file mode 100644 index 0000000..a1e3c00 --- /dev/null +++ b/admin/assets/security/index.html @@ -0,0 +1,9 @@ + + + + Access Denied + + +

Access Denied

+ + \ No newline at end of file diff --git a/admin/assets/security/index.php b/admin/assets/security/index.php new file mode 100644 index 0000000..52d29d7 --- /dev/null +++ b/admin/assets/security/index.php @@ -0,0 +1,21 @@ + +This file was generated automatically by the Akeeba Backup Engine + +DO NOT REMOVE THIS FILE + +This file tells your web server to not list the contents of this directory, instead returning an HTTP 403 Forbidden +error. This makes it implausible for a malicious third party to successfully guess the filenames of your backup +archives. Therefore, even if this folder is directly web accessible – despite the .htaccess and web.config file already +put in place by the Akeeba Backup Engine – it will still be reasonably protected against malicious users trying to +download your backup archives. + +Please do not remove this file as it could have security implications for your site. + +You are strongly advised to never delete or modify any of the files automatically created in this folder by the +Akeeba Backup Engine, namely: + +* .htaccess +* web.config +* index.html +* index.htm +* index.php diff --git a/admin/assets/security/web.config b/admin/assets/security/web.config new file mode 100644 index 0000000..296de01 --- /dev/null +++ b/admin/assets/security/web.config @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/admin/config.xml b/admin/config.xml new file mode 100644 index 0000000..6a6e587 --- /dev/null +++ b/admin/config.xml @@ -0,0 +1,356 @@ + + +
+ + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
\ No newline at end of file diff --git a/admin/controller.php b/admin/controller.php new file mode 100644 index 0000000..360eba1 --- /dev/null +++ b/admin/controller.php @@ -0,0 +1,127 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\Utilities\ArrayHelper; + +/** + * General Controller of Extensiondistributor component + */ +class ExtensiondistributorController extends BaseController +{ + /** + * Constructor. + * + * @param array $config An optional associative array of configuration settings. + * Recognized key values include 'name', 'default_task', 'model_path', and + * 'view_path' (this list is not meant to be comprehensive). + * + * @since 3.0 + */ + public function __construct($config = array()) + { + // set the default view + $config['default_view'] = 'extensiondistributor'; + + parent::__construct($config); + } + + /** + * display task + * + * @return void + */ + function display($cachable = false, $urlparams = false) + { + // set default view if not set + $view = $this->input->getCmd('view', 'extensiondistributor'); + $data = $this->getViewRelation($view); + $layout = $this->input->get('layout', null, 'WORD'); + $id = $this->input->getInt('id'); + + // Check for edit form. + if(ExtensiondistributorHelper::checkArray($data)) + { + if ($data['edit'] && $layout == 'edit' && !$this->checkEditId('com_extensiondistributor.edit.'.$data['view'], $id)) + { + // Somehow the person just went to the form - we don't allow that. + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $id)); + $this->setMessage($this->getError(), 'error'); + // check if item was opend from other then its own list view + $ref = $this->input->getCmd('ref', 0); + $refid = $this->input->getInt('refid', 0); + // set redirect + if ($refid > 0 && ExtensiondistributorHelper::checkString($ref)) + { + // redirect to item of ref + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view='.(string)$ref.'&layout=edit&id='.(int)$refid, false)); + } + elseif (ExtensiondistributorHelper::checkString($ref)) + { + + // redirect to ref + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view='.(string)$ref, false)); + } + else + { + // normal redirect back to the list view + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view='.$data['views'], false)); + } + + return false; + } + } + + return parent::display($cachable, $urlparams); + } + + protected function getViewRelation($view) + { + // check the we have a value + if (ExtensiondistributorHelper::checkString($view)) + { + // the view relationships + $views = array( + 'package' => 'packages', + 'extension' => 'extensions', + 'release' => 'releases', + 'subscription' => 'subscriptions', + 'key' => 'keys', + 'download' => 'downloads' + ); + // check if this is a list view + if (in_array($view, $views)) + { + // this is a list view + return array('edit' => false, 'view' => array_search($view,$views), 'views' => $view); + } + // check if it is an edit view + elseif (array_key_exists($view, $views)) + { + // this is a edit view + return array('edit' => true, 'view' => $view, 'views' => $views[$view]); + } + } + return false; + } +} diff --git a/admin/controllers/ajax.json.php b/admin/controllers/ajax.json.php new file mode 100644 index 0000000..a4867ab --- /dev/null +++ b/admin/controllers/ajax.json.php @@ -0,0 +1,119 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Ajax Base Controller + */ +class ExtensiondistributorControllerAjax extends BaseController +{ + public function __construct($config) + { + parent::__construct($config); + // make sure all json stuff are set + JFactory::getDocument()->setMimeEncoding( 'application/json' ); + // get the application + $app = JFactory::getApplication(); + $app->setHeader('Content-Disposition','attachment;filename="getajax.json"'); + $app->setHeader('Access-Control-Allow-Origin', '*'); + // load the tasks + $this->registerTask('showdir', 'ajax'); + } + + public function ajax() + { + // get the user for later use + $user = JFactory::getUser(); + // get the input values + $jinput = JFactory::getApplication()->input; + // check if we should return raw + $returnRaw = $jinput->get('raw', false, 'BOOLEAN'); + // return to a callback function + $callback = $jinput->get('callback', null, 'CMD'); + // Check Token! + $token = JSession::getFormToken(); + $call_token = $jinput->get('token', 0, 'ALNUM'); + if($jinput->get($token, 0, 'ALNUM') || $token === $call_token) + { + // get the task + $task = $this->getTask(); + switch($task) + { + case 'showdir': + try + { + $dirValue = $jinput->get('dir', NULL, 'STRING'); + $folderonlyValue = $jinput->get('folderonly', false, 'BOOLEAN'); + $showrootValue = $jinput->get('showroot', false, 'BOOLEAN'); + $result = $this->getModel('ajax')->showdir($dirValue, $folderonlyValue, $showrootValue); + if($callback) + { + echo $callback . "(".json_encode($result).");"; + } + elseif($returnRaw) + { + echo json_encode($result); + } + else + { + echo "(".json_encode($result).");"; + } + } + catch(Exception $e) + { + if($callback) + { + echo $callback."(".json_encode($e).");"; + } + elseif($returnRaw) + { + echo json_encode($e); + } + else + { + echo "(".json_encode($e).");"; + } + } + break; + } + } + else + { + // return to a callback function + if($callback) + { + echo $callback."(".json_encode(false).");"; + } + elseif($returnRaw) + { + echo json_encode(false); + } + else + { + echo "(".json_encode(false).");"; + } + } + } +} diff --git a/admin/controllers/download.php b/admin/controllers/download.php new file mode 100644 index 0000000..173b5b5 --- /dev/null +++ b/admin/controllers/download.php @@ -0,0 +1,320 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\Utilities\ArrayHelper; + +/** + * Download Form Controller + */ +class ExtensiondistributorControllerDownload extends FormController +{ + /** + * Current or most recently performed task. + * + * @var string + * @since 12.2 + * @note Replaces _task. + */ + protected $task; + + /** + * Class constructor. + * + * @param array $config A named array of configuration variables. + * + * @since 1.6 + */ + public function __construct($config = array()) + { + $this->view_list = 'Downloads'; // safeguard for setting the return view listing to the main view. + parent::__construct($config); + } + + /** + * Method override to check if you can add a new record. + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowAdd($data = array()) + { + // Get user object. + $user = JFactory::getUser(); + + // In the absense of better information, revert to the component permissions. + return parent::allowAdd($data); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // get user object. + $user = JFactory::getUser(); + // get record id. + $recordId = (int) isset($data[$key]) ? $data[$key] : 0; + + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit', 'com_extensiondistributor.download.' . (int) $recordId); + if (!$permission) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor.download.' . $recordId)) + { + // Now test the owner is the user. + $ownerId = (int) isset($data['created_by']) ? $data['created_by'] : 0; + if (empty($ownerId)) + { + // Need to do a lookup from the model. + $record = $this->getModel()->getItem($recordId); + + if (empty($record)) + { + return false; + } + $ownerId = $record->created_by; + } + + // If the owner matches 'me' then allow. + if ($ownerId == $user->id) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor')) + { + return true; + } + } + } + return false; + } + } + // Since there is no permission, revert to the component permissions. + return parent::allowEdit($data, $key); + } + + /** + * Gets the URL arguments to append to an item redirect. + * + * @param integer $recordId The primary key id for the item. + * @param string $urlVar The name of the URL variable for the id. + * + * @return string The arguments to append to the redirect URL. + * + * @since 1.6 + */ + protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') + { + // get the referral options (old method use return instead see parent) + $ref = $this->input->get('ref', 0, 'string'); + $refid = $this->input->get('refid', 0, 'int'); + + // get redirect info. + $append = parent::getRedirectToItemAppend($recordId, $urlVar); + + // set the referral options + if ($refid && $ref) + { + $append = '&ref=' . (string)$ref . '&refid='. (int)$refid . $append; + } + elseif ($ref) + { + $append = '&ref='. (string)$ref . $append; + } + + return $append; + } + + /** + * Method to run batch operations. + * + * @param object $model The model. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 2.5 + */ + public function batch($model = null) + { + JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); + + // Set the model + $model = $this->getModel('Download', '', array()); + + // Preset the redirect + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view=downloads' . $this->getRedirectToListAppend(), false)); + + return parent::batch($model); + } + + /** + * Method to cancel an edit. + * + * @param string $key The name of the primary key of the URL variable. + * + * @return boolean True if access level checks pass, false otherwise. + * + * @since 12.2 + */ + public function cancel($key = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + + $cancel = parent::cancel($key); + + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view='.(string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $cancel; + } + + /** + * Method to save a record. + * + * @param string $key The name of the primary key of the URL variable. + * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). + * + * @return boolean True if successful, false otherwise. + * + * @since 12.2 + */ + public function save($key = null, $urlVar = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + $canReturn = (!is_null($return) && JUri::isInternal(base64_decode($return))); + + if ($this->ref || $this->refid || $canReturn) + { + // to make sure the item is checkedin on redirect + $this->task = 'save'; + } + + $saved = parent::save($key, $urlVar); + + // This is not needed since parent save already does this + // Due to the ref and refid implementation we need to add this + if ($canReturn) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view=' . (string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $saved; + } + + /** + * Function that allows child controller access to model data + * after the data has been saved. + * + * @param JModel &$model The data model object. + * @param array $validData The validated data. + * + * @return void + * + * @since 11.1 + */ + protected function postSaveHook(JModelLegacy $model, $validData = array()) + { + return; + } + +} diff --git a/admin/controllers/downloads.php b/admin/controllers/downloads.php new file mode 100644 index 0000000..4c4217a --- /dev/null +++ b/admin/controllers/downloads.php @@ -0,0 +1,81 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\Utilities\ArrayHelper; + +/** + * Downloads Admin Controller + */ +class ExtensiondistributorControllerDownloads extends AdminController +{ + /** + * The prefix to use with controller messages. + * + * @var string + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR_DOWNLOADS'; + + /** + * Method to get a model object, loading it if required. + * + * @param string $name The model name. Optional. + * @param string $prefix The class prefix. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JModelLegacy The model. + * + * @since 1.6 + */ + public function getModel($name = 'Download', $prefix = 'ExtensiondistributorModel', $config = array('ignore_request' => true)) + { + return parent::getModel($name, $prefix, $config); + } + + public function deleteLogs() + { + JSession::checkToken() or jexit('Wrong token'); + + $app = Jfactory::getApplication(); + $cid = $app->input->get('cid',null,'array'); + $return = 'index.php?option=com_extensiondistributor&view=downloads'; + // Preset the redirect + if (!is_null($return) && JUri::isInternal($return)) + { + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $return, false + ) + ); + } + + $model = $this->getModel('download'); + $model->publish($cid,-2); + $model->delete($cid); + + return true; + } + +} diff --git a/admin/controllers/extension.php b/admin/controllers/extension.php new file mode 100644 index 0000000..11a5d61 --- /dev/null +++ b/admin/controllers/extension.php @@ -0,0 +1,787 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\Utilities\ArrayHelper; + +/** + * Extension Form Controller + */ +class ExtensiondistributorControllerExtension extends FormController +{ + /** + * Current or most recently performed task. + * + * @var string + * @since 12.2 + * @note Replaces _task. + */ + protected $task; + + /** + * Class constructor. + * + * @param array $config A named array of configuration variables. + * + * @since 1.6 + */ + public function __construct($config = array()) + { + $this->view_list = 'Extensions'; // safeguard for setting the return view listing to the main view. + parent::__construct($config); + } + + public function download() + { + JSession::checkToken('get') or jexit('Wrong token'); + + global $cparams; + $app = JFactory::getApplication(); + $extensionId = (int) $app->input->get('extension'); + $packageId = (int) $app->input->get('package'); + $releaseId = (int) $app->input->get('release'); + + // We check if a release ID was provided or we get the latest one + if ($releaseId) + { + $latest = new stdClass(); + $latest->file = ExtensiondistributorHelper::getVar( + 'release', $releaseId, 'id', 'file' + ); + } + else + { + if ($extensionId > 0) + { + $latest = ExtensiondistributorHelper::getReleases( + $extensionId, 'extension', true + ); + } + else + { + $latest = ExtensiondistributorHelper::getReleases( + $packageId, 'package', true + ); + } + } + $folder = $cparams->get('releases_directory'); + $filename = $latest->file; + $folder = JPath::clean(JPATH_SITE . '/' . $folder . '/'); + + $file = $folder . $filename; + $size = filesize($file); + + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header("Content-Transfer-Encoding: Binary"); + header('Content-Disposition: attachment; filename="' . $filename . '"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: ' . $size); + ob_clean(); + flush(); + readfile($file); + + jexit(); + } + + public function uploadFile() + { + JSession::checkToken('get') or jexit('Wrong token'); + + $app = JFactory::getApplication(); + global $cparams; + $release = $cparams->get('releases_directory', 'packages'); + $jcb = $cparams->get('jcb_directory', 'jcb'); + $import = $cparams->get('import_directory', 'importfolder'); + + $type = $app->input->get('type', 'release'); + $path = JPath::clean(JPATH_SITE . '/' . ${$type}); + $filename = ''; + + $userfile = array(); + foreach ($_FILES as $name => $file) + { + foreach ($file as $property => $keys) + { + foreach ($keys as $key => $value) + { + $userfile[$name][$key][$property] = $value; + } + } + } + + $userfile = $userfile['files'][0]; + $model = $this->getModel('extension'); + $package = $model->uploadFile($userfile); + + // Cette partie est empruntée au com_media de Joomla + $extension = strtolower($package['format']); + $filename = JFile::makeSafe($package['packagename']); + $filename = str_replace('.' . $package['format'], '', $filename); + $filename = JStringPunycode::toPunycode($filename); + $filename = preg_replace( + array("/[\\s]/", '/[^a-zA-Z0-9_\-]/'), array('_', ''), $filename + ) . '.' . $extension; + // Fin de l'emprunt + + // move packages to the correct directory + $final_dest = $path . DIRECTORY_SEPARATOR . $filename; + JFile::move($package['dir'], $final_dest); + + echo json_encode($package); + + exit(); + } + + public function getImportFilesList() + { + JSession::checkToken('get') or jexit('Wrong token'); + + global $cparams; + $app = JFactory::getApplication(); + $import = $cparams->get('import_directory', 'importfolder'); + $path = JPath::clean(JPATH_SITE . '/' . $import); + $files = JFolder::files($path, '.zip', false, true); + + echo JLayoutHelper::render('importfiles', array($files)); + + exit(); + } + + public function importExtensions() + { + JSession::checkToken('get') or jexit('Wrong token'); + + global $cparams; + $app = JFactory::getApplication(); + $folder = JPATH_SITE . '/' . $cparams->get( + 'import_directory', 'extmanagerimport' + ); + $category = $cparams->get('catid'); + $file = $this->input->get('file', null, 'base64'); + if (!is_null($file)) + { + $files[] = base64_decode($file); + } + else + { + $files = JFolder::files($folder, '.zip', false, true); + } + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + // Preset the redirect + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + if ($category === null) + { + $app->enqueueMessage( + 'You have to choose the import category in the component configuration', + 'error' + ); + + return false; + } + + if (ExtensiondistributorHelper::checkArray($files)) + { + foreach ($files as $i => $file) + { + $this->importExtension($file); + } + } + + return true; + } + + public function importExtension($file) + { + global $cparams; + $app = JFactory::getApplication(); + $packages = $cparams->get('releases_directory', 'packages'); + $packages_path = JPath::clean(JPATH_SITE . '/' . $packages . '/'); + $folder = JPATH_SITE . '/' . $cparams->get( + 'import_directory', 'extmanagerimport' + ); + $category = $cparams->get('catid'); + $icon_failed = ' '; + $icon_success = ' '; + $success = true; + + // First extract zip file + $zip = new ZipArchive(); + $res = $zip->open($file); + if ($res === true) + { + $zip->extractTo($folder . '/tmp'); + $zip->close(); + $msg[] = sprintf( + 'File %s successfully extracted', + pathinfo($file)['filename'] + ); + } + else + { + $msg[] = sprintf('Cannot extract file %s'); + } + // Check if there's an xml file in the tmp folder + $manifest = JFolder::files($folder . '/tmp', '.xml', false, true); + if (ExtensiondistributorHelper::checkArray($manifest)) + { + $extfolder = $folder . '/tmp/'; + $manifest = $manifest[0]; + } + else + { + // Should be in a subfolder + if (JFolder::exists($folder . '/tmp/__MACOSX')) + { + // Have to find something more global + JFolder::delete($folder . '/tmp/__MACOSX'); + } + // Find the new folder + $extracted = JFolder::folders($folder . '/tmp'); + $extracted = $extracted[0]; + $extfolder = $folder . '/tmp/' . $extracted; + // Get the manifest + $manifest = JFolder::files($extfolder, '.xml', false, true); + $manifest = $manifest[0]; + } + + // Try to find a language directory in the install folder + $language_dir = JFolder::folders($extfolder, 'lang(.*)', true, true); + if (ExtensiondistributorHelper::checkArray($language_dir)) + { + // Find a file + $language_file = JFolder::files($language_dir[0], '.', true); + if (ExtensiondistributorHelper::checkArray($language_file)) + { + $language_file = $language_file[0]; + $ext = explode('.', $language_file); + // Now we can load the extension language file + $language = JFactory::getLanguage(); + $ext = $ext[1]; + $base_dir = str_replace('/language', '', $language_dir[0]); + $language_tag = null; + $reload = true; + $language->load($ext, $base_dir, $language_tag, $reload); + } + } + + // Read the XML data + $xml = simplexml_load_file($manifest); + + $extension = array(); + $extension['type'] = (string) $xml['type']; + $extension['group'] = (string) $xml['group']; + $extension['client'] = (string) $xml['client']; + $extension['name'] = JText::_((string) $xml->name); + $extension['description'] = JText::_((string) $xml->description); + + if ($extension['type'] === 'plugin') + { + $extension['element'] = (string) $xml->files->filename['plugin']; + } + else + { + $extension['element'] = strtolower((string) $xml->name); + } + + $release = array(); + $release['version_number'] = (string) $xml->version; + + try + { + $reldate = JFactory::getDate((string) $xml->creationDate)->toSql(); + } + catch (Exception $e) + { + $reldate = JFactory::getDate()->toSql(); + } + + $release['release_date'] = $reldate; + $release['file'] = pathinfo($file)['basename']; + + // We check if the extension already exists + $extensionId = ExtensiondistributorHelper::isExtension( + $extension['type'], $extension['element'], $extension['group'], + $extension['client'] + ); + if (!$extensionId) + { + // If not we create it + $model = ExtensiondistributorHelper::getModel('extension'); + $element = $model->getItem(); + // Extension + $element->name = $extension['name']; + $element->type = $extension['type']; + $element->element = $extension['element']; + $element->group = $extension['group']; + $element->client = $extension['client']; + $element->description = $extension['description']; + $element->keepfilename = $cparams->get('keepfilename') ? 1 : 0; + $element->newfilename = $cparams->get('newfilename'); + $element->created_by = JFactory::getUser()->id; + $element->created = JFactory::getDate()->toSql(); + $element->alias = JFilterOutput::stringURLSafe( + $element->name + ); + $element->update = JUri::root() . 'updates/' . $element->alias + . '.xml'; + $element->catid = $category; + // Release + $element->version_number = $release['version_number']; + $element->release_date = $release['release_date']; + unset($element->tags); + + if ($model->save((array) $element)) + { + $extensionId = ExtensiondistributorHelper::extensionsLastId(); + $msg[] = sprintf( + '%sExtension %s successfully created | id:%s', + $icon_success, $element->name, $extensionId + ); + } + } + else + { + $name = ExtensiondistributorHelper::getVar( + 'extension', $extensionId, 'id', 'name' + ); + $msg[] = sprintf( + '%sExtension %s already exists | id:%s', $icon_failed, $name, + $extensionId + ); + } + + // We check if the release already exists + $releaseId = ExtensiondistributorHelper::isRelease( + $extensionId, $release['version_number'] + ); + if (!$releaseId) + { + // If not we create it + $model = ExtensiondistributorHelper::getModel('release'); + $element = $model->getItem(); + $element->extension = $extensionId; + $element->version_number = $release['version_number']; + $element->release_date = $release['release_date']; + $element->stability = $cparams->get('stability', 4); + $element->toolbar = 'NOPE'; + $element->zip_content = 'EMPTY'; + $element->reltype = $cparams->get('reltype', 'maintenance'); + $element->created_by = JFactory::getUser()->id; + $element->created = JFactory::getDate()->toSql(); + $element->file = $release['file']; + if (!$cparams->get('keepfilename')) + { + // We create the new filename + $extensionObject = ExtensiondistributorHelper::getExtensionObject( + $extensionId + ); + $filename = ExtensiondistributorHelper::createExtensionFileName( + $extensionObject, + $element->version_number, + $element->stability, + $element->file + ); + $element->file = $filename; + } + unset($element->tags); + + if ($model->save((array) $element)) + { + $msg[] = sprintf( + '%sRelease %s successfully created', $icon_success, + $element->version_number + ); + // We move the releases file to its destination + JFile::move($file, $packages_path . $element->file); + // We delete the original file + if (JFile::exists($file)) + { + JFile::delete($file); + } + } + } + else + { + $version = ExtensiondistributorHelper::getVar( + 'release', $releaseId, 'id', 'version_number' + ); + $msg[] = sprintf( + '%sThe release %s exists already | id:%s', $icon_failed, + $version, $releaseId + ); + $success = false; + } + + $app->enqueueMessage( + implode('
', $msg), $success ? 'message' : 'error' + ); + + // We delete the temp folder + JFolder::delete($folder . '/tmp'); + } + + public function deleteImportFiles() + { + JSession::checkToken('get') or jexit('Wrong token'); + + global $cparams; + $app = JFactory::getApplication(); + $folder = JPATH_SITE . '/' . $cparams->get( + 'import_directory', 'extmanagerimport' + ); + $file = $this->input->get('file', null, 'base64'); + if (!is_null($file)) + { + $files[] = base64_decode($file); + } + else + { + $files = JFolder::files($folder, '.zip', false, true); + } + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + // Preset the redirect + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + + if (ExtensiondistributorHelper::checkArray($files)) + { + foreach ($files as $file) + { + if (JFile::delete($file)) + { + $app->enqueueMessage( + sprintf( + 'The file %s was successfully deleted', + pathinfo($file)['filename'] + ) + ); + } + else + { + $app->enqueueMessage( + sprintf( + 'Cannot delete file %s', pathinfo($file)['filename'] + ), 'error' + ); + } + } + } + else + { + $app->enqueueMessage('No files to delete', 'error'); + } + + return true; + } + + + /** + * Method override to check if you can add a new record. + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowAdd($data = array()) + { + // Get user object. + $user = JFactory::getUser(); + + // In the absense of better information, revert to the component permissions. + return parent::allowAdd($data); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowEdit($data = array(), $key = 'id') + { // get user object. + $user = JFactory::getUser(); + // get record id. + $recordId = (int) isset($data[$key]) ? $data[$key] : 0; + + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit', 'com_extensiondistributor.extension.' . (int) $recordId); + if (!$permission) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor.extension.' . $recordId)) + { + // Fallback on edit.own. Now test the owner is the user. + $ownerId = (int) isset($data['created_by']) ? $data['created_by'] : 0; + if (empty($ownerId)) + { + // Need to do a lookup from the model. + $record = $this->getModel()->getItem($recordId); + + if (empty($record)) + { + return false; + } + $ownerId = $record->created_by; + } + + // If the owner matches 'me' then do the test. + if ($ownerId == $user->id) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor')) + { + return true; + } + } + } + return false; + } + } + // Since there is no permission, revert to the component permissions. + return parent::allowEdit($data, $key); + } + + /** + * Gets the URL arguments to append to an item redirect. + * + * @param integer $recordId The primary key id for the item. + * @param string $urlVar The name of the URL variable for the id. + * + * @return string The arguments to append to the redirect URL. + * + * @since 1.6 + */ + protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') + { + // get the referral options (old method use return instead see parent) + $ref = $this->input->get('ref', 0, 'string'); + $refid = $this->input->get('refid', 0, 'int'); + + // get redirect info. + $append = parent::getRedirectToItemAppend($recordId, $urlVar); + + // set the referral options + if ($refid && $ref) + { + $append = '&ref=' . (string)$ref . '&refid='. (int)$refid . $append; + } + elseif ($ref) + { + $append = '&ref='. (string)$ref . $append; + } + + return $append; + } + + /** + * Method to run batch operations. + * + * @param object $model The model. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 2.5 + */ + public function batch($model = null) + { + JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); + + // Set the model + $model = $this->getModel('Extension', '', array()); + + // Preset the redirect + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view=extensions' . $this->getRedirectToListAppend(), false)); + + return parent::batch($model); + } + + /** + * Method to cancel an edit. + * + * @param string $key The name of the primary key of the URL variable. + * + * @return boolean True if access level checks pass, false otherwise. + * + * @since 12.2 + */ + public function cancel($key = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + + $cancel = parent::cancel($key); + + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view='.(string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $cancel; + } + + /** + * Method to save a record. + * + * @param string $key The name of the primary key of the URL variable. + * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). + * + * @return boolean True if successful, false otherwise. + * + * @since 12.2 + */ + public function save($key = null, $urlVar = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + $canReturn = (!is_null($return) && JUri::isInternal(base64_decode($return))); + + if ($this->ref || $this->refid || $canReturn) + { + // to make sure the item is checkedin on redirect + $this->task = 'save'; + } + + $saved = parent::save($key, $urlVar); + + // This is not needed since parent save already does this + // Due to the ref and refid implementation we need to add this + if ($canReturn) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view=' . (string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $saved; + } + + /** + * Function that allows child controller access to model data + * after the data has been saved. + * + * @param JModel &$model The data model object. + * @param array $validData The validated data. + * + * @return void + * + * @since 11.1 + */ + protected function postSaveHook(JModelLegacy $model, $validData = array()) + { + return; + } + +} diff --git a/admin/controllers/extensiondistributor.php b/admin/controllers/extensiondistributor.php new file mode 100644 index 0000000..5c43d51 --- /dev/null +++ b/admin/controllers/extensiondistributor.php @@ -0,0 +1,33 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Admin Controller + */ +class ExtensiondistributorControllerExtensiondistributor extends AdminController +{ + +} diff --git a/admin/controllers/extensions.php b/admin/controllers/extensions.php new file mode 100644 index 0000000..6865d50 --- /dev/null +++ b/admin/controllers/extensions.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensions Admin Controller + */ +class ExtensiondistributorControllerExtensions extends AdminController +{ + /** + * The prefix to use with controller messages. + * + * @var string + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR_EXTENSIONS'; + + /** + * Method to get a model object, loading it if required. + * + * @param string $name The model name. Optional. + * @param string $prefix The class prefix. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JModelLegacy The model. + * + * @since 1.6 + */ + public function getModel($name = 'Extension', $prefix = 'ExtensiondistributorModel', $config = array('ignore_request' => true)) + { + return parent::getModel($name, $prefix, $config); + } +} diff --git a/admin/controllers/import.php b/admin/controllers/import.php new file mode 100644 index 0000000..61bcee6 --- /dev/null +++ b/admin/controllers/import.php @@ -0,0 +1,42 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Import Base Controller + */ +class ExtensiondistributorControllerImport extends BaseController +{ + public function __construct($config) + { + parent::__construct($config); + } + + public function dashboard() + { + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor', false)); + return; + } +} diff --git a/admin/controllers/index.html b/admin/controllers/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/controllers/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/controllers/key.php b/admin/controllers/key.php new file mode 100644 index 0000000..2909a39 --- /dev/null +++ b/admin/controllers/key.php @@ -0,0 +1,320 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\Utilities\ArrayHelper; + +/** + * Key Form Controller + */ +class ExtensiondistributorControllerKey extends FormController +{ + /** + * Current or most recently performed task. + * + * @var string + * @since 12.2 + * @note Replaces _task. + */ + protected $task; + + /** + * Class constructor. + * + * @param array $config A named array of configuration variables. + * + * @since 1.6 + */ + public function __construct($config = array()) + { + $this->view_list = 'Keys'; // safeguard for setting the return view listing to the main view. + parent::__construct($config); + } + + /** + * Method override to check if you can add a new record. + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowAdd($data = array()) + { + // Get user object. + $user = JFactory::getUser(); + + // In the absense of better information, revert to the component permissions. + return parent::allowAdd($data); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // get user object. + $user = JFactory::getUser(); + // get record id. + $recordId = (int) isset($data[$key]) ? $data[$key] : 0; + + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit', 'com_extensiondistributor.key.' . (int) $recordId); + if (!$permission) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor.key.' . $recordId)) + { + // Now test the owner is the user. + $ownerId = (int) isset($data['created_by']) ? $data['created_by'] : 0; + if (empty($ownerId)) + { + // Need to do a lookup from the model. + $record = $this->getModel()->getItem($recordId); + + if (empty($record)) + { + return false; + } + $ownerId = $record->created_by; + } + + // If the owner matches 'me' then allow. + if ($ownerId == $user->id) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor')) + { + return true; + } + } + } + return false; + } + } + // Since there is no permission, revert to the component permissions. + return parent::allowEdit($data, $key); + } + + /** + * Gets the URL arguments to append to an item redirect. + * + * @param integer $recordId The primary key id for the item. + * @param string $urlVar The name of the URL variable for the id. + * + * @return string The arguments to append to the redirect URL. + * + * @since 1.6 + */ + protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') + { + // get the referral options (old method use return instead see parent) + $ref = $this->input->get('ref', 0, 'string'); + $refid = $this->input->get('refid', 0, 'int'); + + // get redirect info. + $append = parent::getRedirectToItemAppend($recordId, $urlVar); + + // set the referral options + if ($refid && $ref) + { + $append = '&ref=' . (string)$ref . '&refid='. (int)$refid . $append; + } + elseif ($ref) + { + $append = '&ref='. (string)$ref . $append; + } + + return $append; + } + + /** + * Method to run batch operations. + * + * @param object $model The model. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 2.5 + */ + public function batch($model = null) + { + JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); + + // Set the model + $model = $this->getModel('Key', '', array()); + + // Preset the redirect + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view=keys' . $this->getRedirectToListAppend(), false)); + + return parent::batch($model); + } + + /** + * Method to cancel an edit. + * + * @param string $key The name of the primary key of the URL variable. + * + * @return boolean True if access level checks pass, false otherwise. + * + * @since 12.2 + */ + public function cancel($key = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + + $cancel = parent::cancel($key); + + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view='.(string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $cancel; + } + + /** + * Method to save a record. + * + * @param string $key The name of the primary key of the URL variable. + * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). + * + * @return boolean True if successful, false otherwise. + * + * @since 12.2 + */ + public function save($key = null, $urlVar = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + $canReturn = (!is_null($return) && JUri::isInternal(base64_decode($return))); + + if ($this->ref || $this->refid || $canReturn) + { + // to make sure the item is checkedin on redirect + $this->task = 'save'; + } + + $saved = parent::save($key, $urlVar); + + // This is not needed since parent save already does this + // Due to the ref and refid implementation we need to add this + if ($canReturn) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view=' . (string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $saved; + } + + /** + * Function that allows child controller access to model data + * after the data has been saved. + * + * @param JModel &$model The data model object. + * @param array $validData The validated data. + * + * @return void + * + * @since 11.1 + */ + protected function postSaveHook(JModelLegacy $model, $validData = array()) + { + return; + } + +} diff --git a/admin/controllers/keys.php b/admin/controllers/keys.php new file mode 100644 index 0000000..e3277dd --- /dev/null +++ b/admin/controllers/keys.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\Utilities\ArrayHelper; + +/** + * Keys Admin Controller + */ +class ExtensiondistributorControllerKeys extends AdminController +{ + /** + * The prefix to use with controller messages. + * + * @var string + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR_KEYS'; + + /** + * Method to get a model object, loading it if required. + * + * @param string $name The model name. Optional. + * @param string $prefix The class prefix. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JModelLegacy The model. + * + * @since 1.6 + */ + public function getModel($name = 'Key', $prefix = 'ExtensiondistributorModel', $config = array('ignore_request' => true)) + { + return parent::getModel($name, $prefix, $config); + } +} diff --git a/admin/controllers/maintenance.php b/admin/controllers/maintenance.php new file mode 100644 index 0000000..1d55aea --- /dev/null +++ b/admin/controllers/maintenance.php @@ -0,0 +1,187 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Maintenance Base Controller + */ +class ExtensiondistributorControllerMaintenance extends BaseController +{ + public function __construct($config) + { + parent::__construct($config); + } + + public function dashboard() + { + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor', false)); + return; + } + + public function saveconfig() + { + JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); + + global $cparams; + $componentid = JComponentHelper::getComponent('com_extensiondistributor')->id; + $app = JFactory::getApplication(); + $return = 'index.php?option=com_extensiondistributor&view=maintenance'; + $secure = $app->input->get('secure',0,'int') ? 'secure' : 'unsecure'; + + $this->setRedirect( + JRoute::_( + $return, false + ) + ); + + $old['releases_directory'] = JPath::clean(JPATH_SITE . '/' . $cparams->get('releases_directory','packages') . '/'); + $old['jcb_directory'] = JPath::clean(JPATH_SITE . '/' . $cparams->get('jcb_directory','jcb') . '/'); + $old['updates_directory'] = JPath::clean(JPATH_SITE . '/' . $cparams->get('updates_directory','updates') . '/'); + $old['import_directory'] = JPath::clean(JPATH_SITE . '/' . $cparams->get('import_directory','extmanagerimport') . '/'); + + $new['releases_directory'] = JPath::clean(JPATH_SITE . '/' . $app->input->get('releases_directory',null,'string') . '/'); + $new['jcb_directory'] = JPath::clean(JPATH_SITE . '/' . $app->input->get('jcb_directory',null,'string') . '/'); + $new['updates_directory'] = JPath::clean(JPATH_SITE . '/' . $app->input->get('updates_directory',null,'string') . '/'); + $new['import_directory'] = JPath::clean(JPATH_SITE . '/' . $app->input->get('import_directory',null,'string') . '/'); + + foreach ($old as $k => $v) { + if (!is_null($new[$k])) { + if ($k !== 'updates_directory') { + if (JFolder::exists($v)) + { + if (!JFolder::exists($new[$k])) { + JFolder::create($new[$k]); + } + JFolder::move($v,$new[$k]); + ExtensiondistributorHelper::secureFolder($new[$k],$secure); + $cparams->set($k, $app->input->get($k,null,'string')); + $table = JTable::getInstance('extension'); + $table->load($componentid); + $table->bind(array('params' => $cparams->toString())); + // Check for error + if (!$table->check()) { + $this->setError('lastcreatedate: check: ' . $table->getError()); + return false; + } + // Save to database + if (!$table->store()) { + $this->setError('lastcreatedate: store: ' . $table->getError()); + return false; + } + } + } + } + } + + $app->enqueueMessage('Configuration successfully updated!'); + + return true; + } + + public function renameAllFiles() + { + JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); + + $return = 'index.php?option=com_extensiondistributor&view=maintenance'; + + $this->setRedirect( + JRoute::_( + $return, false + ) + ); + + $app = JFactory::getApplication(); + $app->enqueueMessage('Renamed! (Not yet active)'); + + return true; + } + + public function deleteFile() + { + JSession::checkToken('get') or jexit(JText::_('JINVALID_TOKEN')); + + global $cparams; + $app = JFactory::getApplication(); + $return = 'index.php?option=com_extensiondistributor&view=maintenance'; + $path = JPATH_SITE . '/' . $cparams->get('releases_directory','packages') . '/'; + $path = JPath::clean($path); + $filename = $app->input->get('id',null,'base64'); + + $this->setRedirect( + JRoute::_( + $return, false + ) + ); + + if (is_null($filename)) { + $app->enqueueMessage('No file has been chosen','error'); + return false; + } + if (!JFile::exists($path.base64_decode($filename))) { + $app->enqueueMessage('The file does not exist','error'); + return false; + } + + JFile::delete($path.base64_decode($filename)); + $app->enqueueMessage('File successfully deleted!'); + + return true; + } + + public function deleteAllFiles() + { + JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); + + global $cparams; + $app = JFactory::getApplication(); + $return = 'index.php?option=com_extensiondistributor&view=maintenance'; + $path = JPATH_SITE . '/' . $cparams->get('releases_directory','packages') . '/'; + $path = JPath::clean($path); + + $this->setRedirect( + JRoute::_( + $return, false + ) + ); + + $model = $this->getModel('maintenance'); + $orphans = $model->getOrphans(); + + if (!ExtensiondistributorHelper::checkArray($orphans)) { + $app->enqueueMessage('Nothing to delete','error'); + return false; + } + + foreach ($orphans as $orphan) { + if (JFile::exists($path.$orphan)) { + JFile::delete($path.$orphan); + } + } + $app->enqueueMessage(sprintf('%s files successfully deleted!',count($orphans))); + + return true; + } + +} diff --git a/admin/controllers/package.php b/admin/controllers/package.php new file mode 100644 index 0000000..bebf574 --- /dev/null +++ b/admin/controllers/package.php @@ -0,0 +1,413 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\Utilities\ArrayHelper; + +/** + * Package Form Controller + */ +class ExtensiondistributorControllerPackage extends FormController +{ + /** + * Current or most recently performed task. + * + * @var string + * @since 12.2 + * @note Replaces _task. + */ + protected $task; + + /** + * Class constructor. + * + * @param array $config A named array of configuration variables. + * + * @since 1.6 + */ + public function __construct($config = array()) + { + $this->view_list = 'Packages'; // safeguard for setting the return view listing to the main view. + parent::__construct($config); + } + + public function createXml() + { + JSession::checkToken('get') or jexit('Wrong token'); + + $app = JFactory::getApplication(); + $release = $this->input->get('release', null, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + // Preset the redirect + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + if (is_null($release)) { + $app->enqueueMessage('No release selected','error'); + return false; + } + + $details = ExtensiondistributorHelper::getReleaseDetails($release); + global $cparams; + $update_path = JPath::clean(JPATH_SITE.'/'.$cparams->get('updates_directory','updates').'/'); + $update_file = $update_path . $details->update; + $lists = ExtensiondistributorHelper::getApplicationLists(); + $stability = strtolower($lists['stability'][$details->r_stability]['label']); + + $xmldoc = new DOMDocument(); + + $xmldoc->encoding = 'UTF-8'; + $xmldoc->xmlVersion = '1.0'; + $xmldoc->formatOutput = true; + + $xml_file_name = $update_file; + + $root = $xmldoc->createElement('updates'); + + $update_node = $xmldoc->createElement('update'); + $update_node->appendChild($xmldoc->createElement('name', $details->name)); + $update_node->appendChild($xmldoc->createElement('description'))->appendChild($xmldoc->createCDATASection($details->description)); // => dynamic + $update_node->appendChild($xmldoc->createElement('element', 'pkg_'.$details->packagename)); + $update_node->appendChild($xmldoc->createElement('type', 'package')); + + $child_node_tags = $xmldoc->createElement('tags'); + $child_node_tags->appendChild($xmldoc->createElement('tag', $stability)); + $update_node->appendChild($child_node_tags); + + $update_node->appendChild($xmldoc->createElement('client', 'site')); + $update_node->appendChild($xmldoc->createElement('version', $details->r_version_number)); + $infourl = $xmldoc->createElement('infourl', $details->infourl); + $infourl->setAttributeNode(new DOMAttr('title', $details->author)); + $update_node->appendChild($infourl); + + $child_node_downloads = $xmldoc->createElement('downloads'); + $downloadurl = $xmldoc->createElement('downloadurl'); + $downloadurl->appendChild($xmldoc->createCDATASection($cparams->get('updateserver_url', JUri::root()).'index.php?option=com_extensiondistributor&task=package.update&release='.$details->r_id)); + $downloadurl->setAttributeNode(new DOMAttr('type', 'full')); + $downloadurl->setAttributeNode(new DOMAttr('format', 'zip')); + $child_node_downloads->appendChild($downloadurl); + $update_node->appendChild($child_node_downloads); + + $update_node->appendChild($xmldoc->createElement('maintainer', $details->packager)); + $update_node->appendChild($xmldoc->createElement('maintainerurl', $details->packagerurl)); + $targetplatform = $xmldoc->createElement('targetplatform'); + $targetplatform->setAttributeNode(new DOMAttr('name', 'joomla')); + $targetplatform->setAttributeNode(new DOMAttr('version', $details->targetplatform)); + $update_node->appendChild($targetplatform); + $update_node->appendChild($xmldoc->createElement('php_minimum', $details->php_minimum)); + + $root->appendChild($update_node); + + $xmldoc->appendChild($root); + + if ($xmldoc->save($xml_file_name)) { + $app->enqueueMessage(sprintf('File %s successfully updated', pathinfo($xml_file_name)['filename'].'.xml')); + } else { + $app->enqueueMessage(sprintf('Cannot create file %s', pathinfo($xml_file_name)['filename'].'.xml'), 'error'); + } + + return true; + } + + + /** + * Method override to check if you can add a new record. + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowAdd($data = array()) + { + // Get user object. + $user = JFactory::getUser(); + // Access check. + $access = $user->authorise('package.access', 'com_extensiondistributor'); + if (!$access) + { + return false; + } + + // In the absense of better information, revert to the component permissions. + return parent::allowAdd($data); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowEdit($data = array(), $key = 'id') + { // get user object. + $user = JFactory::getUser(); + // get record id. + $recordId = (int) isset($data[$key]) ? $data[$key] : 0; + + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit', 'com_extensiondistributor.extension.' . (int) $recordId); + if (!$permission) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor.extension.' . $recordId)) + { + // Fallback on edit.own. Now test the owner is the user. + $ownerId = (int) isset($data['created_by']) ? $data['created_by'] : 0; + if (empty($ownerId)) + { + // Need to do a lookup from the model. + $record = $this->getModel()->getItem($recordId); + + if (empty($record)) + { + return false; + } + $ownerId = $record->created_by; + } + + // If the owner matches 'me' then do the test. + if ($ownerId == $user->id) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor')) + { + return true; + } + } + } + return false; + } + } + // Since there is no permission, revert to the component permissions. + return parent::allowEdit($data, $key); + } + + /** + * Gets the URL arguments to append to an item redirect. + * + * @param integer $recordId The primary key id for the item. + * @param string $urlVar The name of the URL variable for the id. + * + * @return string The arguments to append to the redirect URL. + * + * @since 1.6 + */ + protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') + { + // get the referral options (old method use return instead see parent) + $ref = $this->input->get('ref', 0, 'string'); + $refid = $this->input->get('refid', 0, 'int'); + + // get redirect info. + $append = parent::getRedirectToItemAppend($recordId, $urlVar); + + // set the referral options + if ($refid && $ref) + { + $append = '&ref=' . (string)$ref . '&refid='. (int)$refid . $append; + } + elseif ($ref) + { + $append = '&ref='. (string)$ref . $append; + } + + return $append; + } + + /** + * Method to run batch operations. + * + * @param object $model The model. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 2.5 + */ + public function batch($model = null) + { + JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); + + // Set the model + $model = $this->getModel('Package', '', array()); + + // Preset the redirect + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view=packages' . $this->getRedirectToListAppend(), false)); + + return parent::batch($model); + } + + /** + * Method to cancel an edit. + * + * @param string $key The name of the primary key of the URL variable. + * + * @return boolean True if access level checks pass, false otherwise. + * + * @since 12.2 + */ + public function cancel($key = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + + $cancel = parent::cancel($key); + + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view='.(string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $cancel; + } + + /** + * Method to save a record. + * + * @param string $key The name of the primary key of the URL variable. + * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). + * + * @return boolean True if successful, false otherwise. + * + * @since 12.2 + */ + public function save($key = null, $urlVar = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + $canReturn = (!is_null($return) && JUri::isInternal(base64_decode($return))); + + if ($this->ref || $this->refid || $canReturn) + { + // to make sure the item is checkedin on redirect + $this->task = 'save'; + } + + $saved = parent::save($key, $urlVar); + + // This is not needed since parent save already does this + // Due to the ref and refid implementation we need to add this + if ($canReturn) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view=' . (string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $saved; + } + + /** + * Function that allows child controller access to model data + * after the data has been saved. + * + * @param JModel &$model The data model object. + * @param array $validData The validated data. + * + * @return void + * + * @since 11.1 + */ + protected function postSaveHook(JModelLegacy $model, $validData = array()) + { + return; + } + +} diff --git a/admin/controllers/packages.php b/admin/controllers/packages.php new file mode 100644 index 0000000..30923fe --- /dev/null +++ b/admin/controllers/packages.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\Utilities\ArrayHelper; + +/** + * Packages Admin Controller + */ +class ExtensiondistributorControllerPackages extends AdminController +{ + /** + * The prefix to use with controller messages. + * + * @var string + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR_PACKAGES'; + + /** + * Method to get a model object, loading it if required. + * + * @param string $name The model name. Optional. + * @param string $prefix The class prefix. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JModelLegacy The model. + * + * @since 1.6 + */ + public function getModel($name = 'Package', $prefix = 'ExtensiondistributorModel', $config = array('ignore_request' => true)) + { + return parent::getModel($name, $prefix, $config); + } +} diff --git a/admin/controllers/release.php b/admin/controllers/release.php new file mode 100644 index 0000000..f954eaa --- /dev/null +++ b/admin/controllers/release.php @@ -0,0 +1,429 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\Utilities\ArrayHelper; + +/** + * Release Form Controller + */ +class ExtensiondistributorControllerRelease extends FormController +{ + /** + * Current or most recently performed task. + * + * @var string + * @since 12.2 + * @note Replaces _task. + */ + protected $task; + + /** + * Class constructor. + * + * @param array $config A named array of configuration variables. + * + * @since 1.6 + */ + public function __construct($config = array()) + { + $this->view_list = 'Releases'; // safeguard for setting the return view listing to the main view. + parent::__construct($config); + } + + public function changeState() + { + JSession::checkToken('get') or jexit('Wrong token'); + + $app = JFactory::getApplication(); + $id = $this->input->get('id', null, 'int'); + $state = $this->input->get('state', null, 'string'); + $states = array( + 'publish' => array( + 'id' => 1, + 'action' => 'published' + ), + 'unpublish' => array( + 'id' => 0, + 'action' => 'unpublished' + ), + 'trash' => array( + 'id' => -2, + 'action' => 'trashed' + ) + ); + $version = ExtensiondistributorHelper::getVar('release',$id,'id','version_number'); + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + // Preset the redirect + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + + $model = $this->getModel('release'); + $pks = (array)$id; + $action = $states[$state]['action']; + try + { + $model->publish($pks, $states[$state]['id']); + $app->enqueueMessage(sprintf('%s was successfully %s', $version, $action)); + } + catch (Exception $e) + { + $app->enqueueMessage($e->getMessage(),'error'); + return false; + } + return true; + } + + public function reldelete() + { + JSession::checkToken('get') or jexit('Wrong token'); + + $app = JFactory::getApplication(); + $id = $this->input->get('id', null, 'int'); + $release = ExtensiondistributorHelper::getReleaseDetails($id); + $version = $release->r_version_number; + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + // Preset the redirect + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + + $model = $this->getModel('release'); + $pks = (array)$id; + $action = 'deleted'; + try + { + $model->delete($pks); + $app->enqueueMessage(sprintf('%s was successfully %s', $version, $action)); + // We need to update the related object + if ((int)$release->r_extension > 0) { + $object = 'extension'; + $objectId = (int)$release->r_extension; + } else { + $object = 'package'; + $objectId = (int)$release->r_package; + } + $last = ExtensiondistributorHelper::getReleases($objectId,$object,true); + $model = ExtensiondistributorHelper::getModel($object); + $element = $model->getItem($objectId); + $element->version_number = $last->version_number; + $element->release_date = $last->release_date; + unset($element->tags); + $model->save((array) $element); + + } + catch (Exception $e) + { + $app->enqueueMessage($e->getMessage(),'error'); + return false; + } + return true; + } + + + /** + * Method override to check if you can add a new record. + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowAdd($data = array()) + { + // Get user object. + $user = JFactory::getUser(); + + // In the absense of better information, revert to the component permissions. + return parent::allowAdd($data); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // get user object. + $user = JFactory::getUser(); + // get record id. + $recordId = (int) isset($data[$key]) ? $data[$key] : 0; + + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit', 'com_extensiondistributor.release.' . (int) $recordId); + if (!$permission) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor.release.' . $recordId)) + { + // Now test the owner is the user. + $ownerId = (int) isset($data['created_by']) ? $data['created_by'] : 0; + if (empty($ownerId)) + { + // Need to do a lookup from the model. + $record = $this->getModel()->getItem($recordId); + + if (empty($record)) + { + return false; + } + $ownerId = $record->created_by; + } + + // If the owner matches 'me' then allow. + if ($ownerId == $user->id) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor')) + { + return true; + } + } + } + return false; + } + } + // Since there is no permission, revert to the component permissions. + return parent::allowEdit($data, $key); + } + + /** + * Gets the URL arguments to append to an item redirect. + * + * @param integer $recordId The primary key id for the item. + * @param string $urlVar The name of the URL variable for the id. + * + * @return string The arguments to append to the redirect URL. + * + * @since 1.6 + */ + protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') + { + // get the referral options (old method use return instead see parent) + $ref = $this->input->get('ref', 0, 'string'); + $refid = $this->input->get('refid', 0, 'int'); + + // get redirect info. + $append = parent::getRedirectToItemAppend($recordId, $urlVar); + + // set the referral options + if ($refid && $ref) + { + $append = '&ref=' . (string)$ref . '&refid='. (int)$refid . $append; + } + elseif ($ref) + { + $append = '&ref='. (string)$ref . $append; + } + + return $append; + } + + /** + * Method to run batch operations. + * + * @param object $model The model. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 2.5 + */ + public function batch($model = null) + { + JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); + + // Set the model + $model = $this->getModel('Release', '', array()); + + // Preset the redirect + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view=releases' . $this->getRedirectToListAppend(), false)); + + return parent::batch($model); + } + + /** + * Method to cancel an edit. + * + * @param string $key The name of the primary key of the URL variable. + * + * @return boolean True if access level checks pass, false otherwise. + * + * @since 12.2 + */ + public function cancel($key = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + + $cancel = parent::cancel($key); + + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view='.(string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $cancel; + } + + /** + * Method to save a record. + * + * @param string $key The name of the primary key of the URL variable. + * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). + * + * @return boolean True if successful, false otherwise. + * + * @since 12.2 + */ + public function save($key = null, $urlVar = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + $canReturn = (!is_null($return) && JUri::isInternal(base64_decode($return))); + + if ($this->ref || $this->refid || $canReturn) + { + // to make sure the item is checkedin on redirect + $this->task = 'save'; + } + + $saved = parent::save($key, $urlVar); + + // This is not needed since parent save already does this + // Due to the ref and refid implementation we need to add this + if ($canReturn) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view=' . (string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $saved; + } + + /** + * Function that allows child controller access to model data + * after the data has been saved. + * + * @param JModel &$model The data model object. + * @param array $validData The validated data. + * + * @return void + * + * @since 11.1 + */ + protected function postSaveHook(JModelLegacy $model, $validData = array()) + { + return; + } + +} diff --git a/admin/controllers/releases.php b/admin/controllers/releases.php new file mode 100644 index 0000000..b560e00 --- /dev/null +++ b/admin/controllers/releases.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\Utilities\ArrayHelper; + +/** + * Releases Admin Controller + */ +class ExtensiondistributorControllerReleases extends AdminController +{ + /** + * The prefix to use with controller messages. + * + * @var string + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR_RELEASES'; + + /** + * Method to get a model object, loading it if required. + * + * @param string $name The model name. Optional. + * @param string $prefix The class prefix. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JModelLegacy The model. + * + * @since 1.6 + */ + public function getModel($name = 'Release', $prefix = 'ExtensiondistributorModel', $config = array('ignore_request' => true)) + { + return parent::getModel($name, $prefix, $config); + } +} diff --git a/admin/controllers/statistics.php b/admin/controllers/statistics.php new file mode 100644 index 0000000..c8d625b --- /dev/null +++ b/admin/controllers/statistics.php @@ -0,0 +1,42 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Statistics Base Controller + */ +class ExtensiondistributorControllerStatistics extends BaseController +{ + public function __construct($config) + { + parent::__construct($config); + } + + public function dashboard() + { + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor', false)); + return; + } +} diff --git a/admin/controllers/subscription.php b/admin/controllers/subscription.php new file mode 100644 index 0000000..06cf6d5 --- /dev/null +++ b/admin/controllers/subscription.php @@ -0,0 +1,320 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\Utilities\ArrayHelper; + +/** + * Subscription Form Controller + */ +class ExtensiondistributorControllerSubscription extends FormController +{ + /** + * Current or most recently performed task. + * + * @var string + * @since 12.2 + * @note Replaces _task. + */ + protected $task; + + /** + * Class constructor. + * + * @param array $config A named array of configuration variables. + * + * @since 1.6 + */ + public function __construct($config = array()) + { + $this->view_list = 'Subscriptions'; // safeguard for setting the return view listing to the main view. + parent::__construct($config); + } + + /** + * Method override to check if you can add a new record. + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowAdd($data = array()) + { + // Get user object. + $user = JFactory::getUser(); + + // In the absense of better information, revert to the component permissions. + return parent::allowAdd($data); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // get user object. + $user = JFactory::getUser(); + // get record id. + $recordId = (int) isset($data[$key]) ? $data[$key] : 0; + + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit', 'com_extensiondistributor.subscription.' . (int) $recordId); + if (!$permission) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor.subscription.' . $recordId)) + { + // Now test the owner is the user. + $ownerId = (int) isset($data['created_by']) ? $data['created_by'] : 0; + if (empty($ownerId)) + { + // Need to do a lookup from the model. + $record = $this->getModel()->getItem($recordId); + + if (empty($record)) + { + return false; + } + $ownerId = $record->created_by; + } + + // If the owner matches 'me' then allow. + if ($ownerId == $user->id) + { + if ($user->authorise('core.edit.own', 'com_extensiondistributor')) + { + return true; + } + } + } + return false; + } + } + // Since there is no permission, revert to the component permissions. + return parent::allowEdit($data, $key); + } + + /** + * Gets the URL arguments to append to an item redirect. + * + * @param integer $recordId The primary key id for the item. + * @param string $urlVar The name of the URL variable for the id. + * + * @return string The arguments to append to the redirect URL. + * + * @since 1.6 + */ + protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') + { + // get the referral options (old method use return instead see parent) + $ref = $this->input->get('ref', 0, 'string'); + $refid = $this->input->get('refid', 0, 'int'); + + // get redirect info. + $append = parent::getRedirectToItemAppend($recordId, $urlVar); + + // set the referral options + if ($refid && $ref) + { + $append = '&ref=' . (string)$ref . '&refid='. (int)$refid . $append; + } + elseif ($ref) + { + $append = '&ref='. (string)$ref . $append; + } + + return $append; + } + + /** + * Method to run batch operations. + * + * @param object $model The model. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 2.5 + */ + public function batch($model = null) + { + JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN')); + + // Set the model + $model = $this->getModel('Subscription', '', array()); + + // Preset the redirect + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view=subscriptions' . $this->getRedirectToListAppend(), false)); + + return parent::batch($model); + } + + /** + * Method to cancel an edit. + * + * @param string $key The name of the primary key of the URL variable. + * + * @return boolean True if access level checks pass, false otherwise. + * + * @since 12.2 + */ + public function cancel($key = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + + $cancel = parent::cancel($key); + + if (!is_null($return) && JUri::isInternal(base64_decode($return))) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view='.(string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $cancel; + } + + /** + * Method to save a record. + * + * @param string $key The name of the primary key of the URL variable. + * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). + * + * @return boolean True if successful, false otherwise. + * + * @since 12.2 + */ + public function save($key = null, $urlVar = null) + { + // get the referral options + $this->ref = $this->input->get('ref', 0, 'word'); + $this->refid = $this->input->get('refid', 0, 'int'); + + // Check if there is a return value + $return = $this->input->get('return', null, 'base64'); + $canReturn = (!is_null($return) && JUri::isInternal(base64_decode($return))); + + if ($this->ref || $this->refid || $canReturn) + { + // to make sure the item is checkedin on redirect + $this->task = 'save'; + } + + $saved = parent::save($key, $urlVar); + + // This is not needed since parent save already does this + // Due to the ref and refid implementation we need to add this + if ($canReturn) + { + $redirect = base64_decode($return); + + // Redirect to the return value. + $this->setRedirect( + JRoute::_( + $redirect, false + ) + ); + } + elseif ($this->refid && $this->ref) + { + $redirect = '&view=' . (string)$this->ref . '&layout=edit&id=' . (int)$this->refid; + + // Redirect to the item screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + elseif ($this->ref) + { + $redirect = '&view=' . (string)$this->ref; + + // Redirect to the list screen. + $this->setRedirect( + JRoute::_( + 'index.php?option=' . $this->option . $redirect, false + ) + ); + } + return $saved; + } + + /** + * Function that allows child controller access to model data + * after the data has been saved. + * + * @param JModel &$model The data model object. + * @param array $validData The validated data. + * + * @return void + * + * @since 11.1 + */ + protected function postSaveHook(JModelLegacy $model, $validData = array()) + { + return; + } + +} diff --git a/admin/controllers/subscriptions.php b/admin/controllers/subscriptions.php new file mode 100644 index 0000000..057ff7b --- /dev/null +++ b/admin/controllers/subscriptions.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\AdminController; +use Joomla\Utilities\ArrayHelper; + +/** + * Subscriptions Admin Controller + */ +class ExtensiondistributorControllerSubscriptions extends AdminController +{ + /** + * The prefix to use with controller messages. + * + * @var string + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS'; + + /** + * Method to get a model object, loading it if required. + * + * @param string $name The model name. Optional. + * @param string $prefix The class prefix. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JModelLegacy The model. + * + * @since 1.6 + */ + public function getModel($name = 'Subscription', $prefix = 'ExtensiondistributorModel', $config = array('ignore_request' => true)) + { + return parent::getModel($name, $prefix, $config); + } +} diff --git a/admin/extensiondistributor.php b/admin/extensiondistributor.php new file mode 100644 index 0000000..dbb94f8 --- /dev/null +++ b/admin/extensiondistributor.php @@ -0,0 +1,51 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + + + +// Access check. +if (!JFactory::getUser()->authorise('core.manage', 'com_extensiondistributor')) +{ + throw new JAccessExceptionNotallowed(JText::_('JERROR_ALERTNOAUTHOR'), 403); +}; + +// Add CSS file for all pages +$document = JFactory::getDocument(); +$document->addStyleSheet('components/com_extensiondistributor/assets/css/admin.css'); +$document->addScript('components/com_extensiondistributor/assets/js/admin.js'); + +// require helper files +JLoader::register('ExtensiondistributorHelper', __DIR__ . '/helpers/extensiondistributor.php'); +JLoader::register('JHtmlBatch_', __DIR__ . '/helpers/html/batch_.php'); + +// Trigger the Global Admin Event +ExtensiondistributorHelper::globalEvent($document); + +// Get an instance of the controller prefixed by Extensiondistributor +$controller = JControllerLegacy::getInstance('Extensiondistributor'); + +// Perform the Request task +$controller->execute(JFactory::getApplication()->input->get('task')); + +// Redirect if set by the controller +$controller->redirect(); diff --git a/admin/helpers/extensiondistributor.php b/admin/helpers/extensiondistributor.php new file mode 100644 index 0000000..3a33a47 --- /dev/null +++ b/admin/helpers/extensiondistributor.php @@ -0,0 +1,2352 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Filesystem\File; +use Joomla\CMS\Language\Language; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor component helper. + */ +abstract class ExtensiondistributorHelper +{ + /** + * Composer Switch + * + * @var array + */ + protected static $composer = array(); + + /** + * The Main Active Language + * + * @var string + */ + public static $langTag; + + /** + * The Global Admin Event Method. + **/ + public static function globalEvent($document) + { + global $cparams; + $app = JFactory::getApplication(); + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $folder = JPATH_SITE . '/' . $cparams->get('import_directory', 'extmanagerimport'); + $files = JFolder::files($folder,'.zip',false,true); + + $model = self::getModel('maintenance'); + $orphans = $model->getOrphans(); + $missing = $model->getMissing(); + $duplicates = $model->getDuplicates(); + $problems = count(@$orphans) + count(@$missing) + count(@$duplicates); + + $copyright = '
Made with and JCB  with a lot of  
Emmanuel Danan - Agence Agerix
'; + + $count = self::checkArray($files) ? count($files) : 0; + $script = ''; + if ($count > 0) { + $badge = '  '.$count.''; + $floatbadge = ''.$count.''; + $script .= ' + jQuery(document).ready(function() { + jQuery("#sidebar #submenu li:nth-child(9) a").append("'.$badge.'"); + jQuery("#dashboard_left .accordion-inner .dashboard-wraper:nth-child(6) a").prepend("'.$floatbadge.'"); + }); + '; + } + if ($problems > 0) { + $badge = '  '.$problems.''; + $floatbadge = ''.$problems.''; + $script .= ' + jQuery(document).ready(function() { + jQuery("#sidebar #submenu li:nth-child(10) a").append("'.$badge.'"); + jQuery("#dashboard_left .accordion-inner .dashboard-wraper:nth-child(7) a").prepend("'.$floatbadge.'"); + }); + '; + } + $copyscript = ' + jQuery(document).ready(function() { + jQuery("#j-main-container").append("'.$copyright.'"); + }); + '; + if (!empty($script)) { + $document->addScriptDeclaration($script); + } + $document->addScriptDeclaration($copyscript); + + } + + public static function getReleases($id, $view = 'extension', $latest = false) + { + if (!$id) { + return null; + } + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName( + array('id','version_number','release_date','reltype','stability','file','extension','package') + )) + ->from($db->quoteName('#__extensiondistributor_release')) + ->where( + array( + $db->quoteName($view) . ' = ' . (int)$id, + $db->quoteName('published') . ' = 1' + ) + ) + ->order('INET_ATON(version_number) DESC') + ; + $db->setQuery($query); + + return $latest ? $db->loadObject() : $db->loadObjectList(); + } + + public static function getPackageContent($packageId): array + { + $package = self::getPackageObject($packageId); + // We first get the extensions of the package + $extensions = $package->files; + $extensions = json_decode($extensions,true); + + $files = array(); + + foreach ($extensions as &$extension) + { + $data = self::getReleases($extension['extension'],'extension',true); + $details = self::getExtensionObject($extension['extension']); + $extension['details'] = $details; + $extension['release'] = $data; + if (isset($data->file)) { + $files[$data->id] = $data->file; + } + } + + return array('extensions' => $extensions, 'files' => $files, 'package' => $package); + } + + public static function createPackage($packageId,$release) + { + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $content = self::getPackageContent($packageId); + $package = $content['package']; + $extensions = $content['extensions']; + $files = $content['files']; + $lists = self::getApplicationLists(); + $releaseFolder = JPATH_SITE . '/' . $cparams->get('releases_directory'); + $tmpFolder = JPATH_SITE . '/tmp/' . $package->packagename; + $update_path = $cparams->get('updates_directory','updates'); + $update_file = $cparams->get('updateserver_url', JUri::root()).$update_path.'/'.$package->update; + + // We first create the temporary folder + JFolder::create($tmpFolder); + // We create the packages folder and copy the files inside + JFolder::create($tmpFolder . '/packages'); + foreach ($files as $file) { + JFile::copy($releaseFolder . '/' . $file, $tmpFolder . '/packages/' . $file); + } + + $zipFilename = self::createPackageFileName($package, $release->version_number, $release->stability); + + $xmldoc = new DOMDocument(); + + $xmldoc->encoding = 'UTF-8'; + $xmldoc->xmlVersion = '1.0'; + $xmldoc->formatOutput = true; + + $xml_file_name = $tmpFolder .'/pkg_'.$package->packagename.'.xml'; + + // Extension + $root = $xmldoc->createElement('extension'); + $root->setAttributeNode(new DOMAttr('type', 'package')); + $root->setAttributeNode(new DOMAttr('version', '3.8')); + $root->setAttributeNode(new DOMAttr('method', 'upgrade')); + + $root->appendChild($xmldoc->createElement('name', $package->name)); + $root->appendChild($xmldoc->createElement('version', $release->version_number)); // => release + $root->appendChild($xmldoc->createElement('author', $package->author)); + $root->appendChild($xmldoc->createElement('authorUrl', $package->authorurl)); + $root->appendChild($xmldoc->createElement('authorEmail', $package->authoremail)); + $root->appendChild($xmldoc->createElement('creationDate', JHtml::date($release->release_date,'M Y'))); // => release + $root->appendChild($xmldoc->createElement('packagename', $package->packagename)); + $root->appendChild($xmldoc->createElement('packager', $package->packager)); + $root->appendChild($xmldoc->createElement('packagerurl', $package->packagerurl)); + $root->appendChild($xmldoc->createElement('copyright', $package->copyright)); + $root->appendChild($xmldoc->createElement('license', $package->license)); + $root->appendChild($xmldoc->createElement('description'))->appendChild($xmldoc->createCDATASection($package->description)); + $root->appendChild($xmldoc->createElement('scriptfile', 'script.php')); // => dynamic A venir + if ($package->blockchilduninstall) { + $root->appendChild($xmldoc->createElement('blockChildUninstall', 'true')); + } + + $files_node = $xmldoc->createElement('files'); + $files_node->setAttributeNode(new DOMAttr('folder', 'packages')); + + foreach ($extensions as $extension) + { + switch ($extension['details']->type) + { + case 'component' : + case 'library' : + $child_node_file = $xmldoc->createElement('file', $extension['release']->file); + $child_node_file->setAttributeNode(new DOMAttr('type', $extension['details']->type)); + $child_node_file->setAttributeNode(new DOMAttr('id', $extension['details']->element)); + $files_node->appendChild($child_node_file); + break; + case 'module' : + case 'template' : + case 'language' : + $child_node_file = $xmldoc->createElement('file', $extension['release']->file); + $child_node_file->setAttributeNode(new DOMAttr('type', $extension['details']->type)); + $child_node_file->setAttributeNode(new DOMAttr('id', $extension['details']->element)); + $child_node_file->setAttributeNode(new DOMAttr('client', $extension['release']->client)); + $files_node->appendChild($child_node_file); + break; + case 'plugin' : + $child_node_file = $xmldoc->createElement('file', $extension['release']->file); + $child_node_file->setAttributeNode(new DOMAttr('type', $extension['details']->type)); + $child_node_file->setAttributeNode(new DOMAttr('id', $extension['details']->element)); + $child_node_file->setAttributeNode(new DOMAttr('group', $extension['details']->group)); + $files_node->appendChild($child_node_file); + break; + } + } + + $root->appendChild($files_node); + + $updateservers_node = $xmldoc->createElement('updateservers'); + $child_node_server = $xmldoc->createElement('server', $update_file); + $child_node_server->setAttributeNode(new DOMAttr('type', 'extension')); + $child_node_server->setAttributeNode(new DOMAttr('priority', '1')); + $child_node_server->setAttributeNode(new DOMAttr('name', $cparams->get('server_name'))); + $updateservers_node->appendChild($child_node_server); + $root->appendChild($updateservers_node); + + $xmldoc->appendChild($root); + $xmldoc->save($xml_file_name); + + // We create the script file + self::createScriptFile($packageId, $tmpFolder); + + // We zip everything in a package + self::zipPackage($releaseFolder.'/'.$zipFilename,$tmpFolder); + + JFolder::delete($tmpFolder); + + return $zipFilename; + } + + public static function zipPackage($zipFile, $zipDir) + { + $zip = new ZipArchive(); + $zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE); + + $files = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($zipDir), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $name => $file) + { + // Skip directories (they would be added automatically) + if (!$file->isDir()) + { + // Get real and relative path for current file + $filePath = $file->getRealPath(); + $relativePath = substr($filePath, strlen($zipDir) + 1); + + // Add current file to archive + $zip->addFile($filePath, $relativePath); + } + } + + $zip->close(); + } + + protected static $tabSpacerBucket = array(); + + protected static $tabSpacer = "\t"; + + protected static $debugLinenr = false; + + public static function _t($nr) + { + // check if we already have the string + if (!isset(self::$tabSpacerBucket[$nr])) + { + // get the string + self::$tabSpacerBucket[$nr] = str_repeat(self::$tabSpacer, (int) $nr); + } + // return stored string + return self::$tabSpacerBucket[$nr]; + } + + public static function getExtensionInstallClass(&$extension) + { + // yes we are adding it + $script = PHP_EOL . '/**'; + $script .= PHP_EOL . ' * ' . $extension->official_name + . ' script file.'; + $script .= PHP_EOL . ' *'; + $script .= PHP_EOL . ' * @package ' . $extension->class_name; + $script .= PHP_EOL . ' */'; + $script .= PHP_EOL . 'class ' . $extension->installer_class_name; + $script .= PHP_EOL . '{'; + // set constructor + if (isset($extension->add_php_script_construct) + && $extension->add_php_script_construct == 1 + && self::checkString( + $extension->php_script_construct + )) + { + $script .= self::setInstallMethodScript( + 'construct', $extension->php_script_construct + ); + } + // add PHP in extension install + $addScriptMethods = array('php_preflight', 'php_postflight', + 'php_method'); + $addScriptTypes = array('install', 'update', 'uninstall', + 'discover_install'); + // set some buckets for sorting + $function_install = array(); + $function_update = array(); + $function_uninstall = array(); + $has_php_preflight = false; + $function_php_preflight = array('install' => array(), + 'uninstall' => array(), + 'discover_install' => array(), + 'update' => array()); + $has_php_postflight = false; + $function_php_postflight = array('install' => array(), + 'uninstall' => array(), + 'discover_install' => array(), + 'update' => array()); + // the function sorter + foreach ($addScriptMethods as $scriptMethod) + { + foreach ($addScriptTypes as $scriptType) + { + if (isset( + $extension->{'add_' . $scriptMethod . '_' . $scriptType} + ) + && $extension->{'add_' . $scriptMethod . '_' . $scriptType} + == 1 + && self::checkString( + $extension->{$scriptMethod . '_' . $scriptType} + )) + { + // add to the main methods + if ('php_method' === $scriptMethod) + { + ${'function_' . $scriptType}[] + = $extension->{$scriptMethod . '_' . $scriptType}; + } + else + { + ${'function_' . $scriptMethod}[$scriptType][] + = $extension->{$scriptMethod + . '_' . $scriptType}; + ${'has_' . $scriptMethod} = true; + } + } + } + } + // now add the install script. + if (self::checkArray($function_install)) + { + $script .= self::setInstallMethodScript( + 'install', $function_install + ); + } + // now add the update script. + if (self::checkArray($function_update)) + { + $script .= self::setInstallMethodScript( + 'update', $function_update + ); + } + // now add the uninstall script. + if (self::checkArray($function_uninstall)) + { + $script .= self::setInstallMethodScript( + 'uninstall', $function_uninstall + ); + } + // now add the preflight script. + if ($has_php_preflight) + { + $script .= self::setInstallMethodScript( + 'preflight', $function_php_preflight + ); + } + // now add the postflight script. + if ($has_php_postflight) + { + $script .= self::setInstallMethodScript( + 'postflight', $function_php_postflight + ); + } + $script .= PHP_EOL . '}' . PHP_EOL; + + return $script; + } + + public static function setInstallMethodScript($function_name, &$scripts) + { + $script = ''; + // build function + switch ($function_name) + { + case 'install': + case 'update': + case 'uninstall': + // the main function types + $script = PHP_EOL . PHP_EOL . self::_t(1) . '/**'; + $script .= PHP_EOL . self::_t(1) . ' * Called on ' + . $function_name; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @param JAdapterInstance $adapter The object responsible for running this script'; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @return boolean True on success'; + $script .= PHP_EOL . self::_t(1) . ' */'; + $script .= PHP_EOL . self::_t(1) . 'public function ' + . $function_name . '(JAdapterInstance $adapter)'; + $script .= PHP_EOL . self::_t(1) . '{'; + $script .= PHP_EOL . implode(PHP_EOL . PHP_EOL, $scripts); + // Added the HTML message + $script .= PHP_EOL . '// Custom HTML message uninstall'; + // return true + if ('uninstall' !== $function_name) + { + $script .= PHP_EOL . self::_t(2) . 'return true;'; + } + break; + case 'preflight': + case 'postflight': + // the pre/post function types + $script = PHP_EOL . PHP_EOL . self::_t(1) . '/**'; + $script .= PHP_EOL . self::_t(1) + . ' * Called before any type of action'; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @param string $route Which action is happening (install|uninstall|discover_install|update)'; + $script .= PHP_EOL . self::_t(1) + . ' * @param JAdapterInstance $adapter The object responsible for running this script'; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @return boolean True on success'; + $script .= PHP_EOL . self::_t(1) . ' */'; + $script .= PHP_EOL . self::_t(1) . 'public function ' + . $function_name . '($route, JAdapterInstance $adapter)'; + $script .= PHP_EOL . self::_t(1) . '{'; + $script .= PHP_EOL . self::_t(2) . '//' . self::setLine( + __LINE__ + ) . ' get application'; + $script .= PHP_EOL . self::_t(2) + . '$app = JFactory::getApplication();' . PHP_EOL; + // add the default version check (TODO) must make this dynamic + if ('preflight' === $function_name) + { + $script .= PHP_EOL . self::_t(2) . '//' . self::setLine( + __LINE__ + ) . ' the default for both install and update'; + $script .= PHP_EOL . self::_t(2) + . '$jversion = new JVersion();'; + $script .= PHP_EOL . self::_t(2) + . "if (!\$jversion->isCompatible('3.8.0'))"; + $script .= PHP_EOL . self::_t(2) . '{'; + $script .= PHP_EOL . self::_t(3) + . "\$app->enqueueMessage('Please upgrade to at least Joomla! 3.8.0 before continuing!', 'error');"; + $script .= PHP_EOL . self::_t(3) . 'return false;'; + $script .= PHP_EOL . self::_t(2) . '}' . PHP_EOL; + } + // now add the scripts + foreach ($scripts as $route => $_script) + { + if (self::checkArray($_script)) + { + // set the if and script + $script .= PHP_EOL . self::_t(2) . "if ('" . $route + . "' === \$route)"; + $script .= PHP_EOL . self::_t(2) . '{'; + $script .= PHP_EOL . self::_t(3) . implode( + PHP_EOL, $_script + ); + // Ajouté pour la gestion du block custom html + if ($route === 'install') { + $script .= PHP_EOL . '// Custom HTML message install'; + } elseif ($route === 'update') { + $script .= PHP_EOL . '// Custom HTML message update'; + } elseif ($route === 'uninstall') { + $script .= PHP_EOL . '// Custom HTML message uninstall'; + } + // Fin de l'ajout + $script .= PHP_EOL . self::_t(2) . '}' . PHP_EOL; + } + } + // return true + $script .= PHP_EOL . self::_t(2) . 'return true;'; + break; + case 'construct': + // the __construct script + $script = PHP_EOL . PHP_EOL . self::_t(1) . '/**'; + $script .= PHP_EOL . self::_t(1) . ' * Constructor'; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @param JAdapterInstance $adapter The object responsible for running this script'; + $script .= PHP_EOL . self::_t(1) . ' */'; + $script .= PHP_EOL . self::_t(1) + . 'public function __construct(JAdapterInstance $adapter)'; + $script .= PHP_EOL . self::_t(1) . '{'; + $script .= PHP_EOL . $scripts; + break; + default: + // oops error + return ''; + } + // close the function + $script .= PHP_EOL . self::_t(1) . '}'; + + return $script; + } + + private static function setLine($nr) + { + if (self::$debugLinenr) + { + return ' [Interpretation ' . $nr . ']'; + } + + return ''; + } + + public static function createScriptFile($packageId, $path) + { + $extension = self::getPackageObject(1); + $extension->official_name = $extension->name; + $extension->class_name = 'Pkg_'.$extension->packagename; + $extension->installer_class_name = $extension->class_name . 'InstallerScript'; + $extension->php_method_uninstall = base64_decode($extension->php_method_uninstall); + $extension->php_postflight_install = base64_decode($extension->php_postflight_install); + $extension->php_postflight_update = base64_decode($extension->php_postflight_update); + $extension->php_preflight_install = base64_decode($extension->php_preflight_install); + $extension->php_preflight_uninstall = base64_decode($extension->php_preflight_uninstall); + $extension->php_preflight_update = base64_decode($extension->php_preflight_update); + $script = self::getExtensionInstallClass($extension); + $file = $path . '/script.php'; + file_put_contents($file, "getQuery(true); + + $query + ->select($db->quoteName( + array( + 'id', + 'alias', + 'catid', + 'client', + 'description', + 'element', + 'group', + 'image', + 'keepfilename', + 'name', + 'newfilename', + 'release_date', + 'type', + 'update', + 'version_number', + 'published' + ) + )) + ->from($db->quoteName('#__extensiondistributor_extension')) + ->where($db->quoteName('id') . ' = ' . (int)$extension) + ; + $db->setQuery($query); + + return $db->loadObject(); + } + + public static function getPackageObject($package) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select('*') + ->from($db->quoteName('#__extensiondistributor_package')) + ->where($db->quoteName('id') . ' = ' . (int)$package) + ; + $db->setQuery($query); + + return $db->loadObject(); + } + + public static function isExtension($type,$element,$group,$client) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName('id')) + ->from($db->quoteName('#__extensiondistributor_extension')) + ->where( + array( + $db->quoteName('type') . ' = ' . $db->quote($type), + $db->quoteName('element') . ' = ' . $db->quote($element), + $db->quoteName('group') . ' = ' . $db->quote($group), + $db->quoteName('client') . ' = ' . $db->quote($client) + ) + ) + ; + $db->setQuery($query); + + return $db->loadResult(); + } + + public static function extensionsLastId() + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName('id')) + ->from($db->quoteName('#__extensiondistributor_extension')) + ->order('id DESC') + ; + $db->setQuery($query,0,1); + + return $db->loadResult(); + } + + public static function isRelease($extensionId,$version) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName('id')) + ->from($db->quoteName('#__extensiondistributor_release')) + ->where( + array( + $db->quoteName('extension') . ' = ' . (int)$extensionId, + $db->quoteName('version_number') . ' = ' . $db->quote($version) + ) + ) + ; + $db->setQuery($query); + + return $db->loadResult(); + } + + public static function getApplicationLists(): array + { + $lists = array(); + + $lists['stability'] = array( + '0' => array( + 'label' => 'Development', + 'color' => 'default' + ), + '1' => array( + 'label' => 'Alpha', + 'color' => 'important' + ), + '2' => array( + 'label' => 'Beta', + 'color' => 'warning' + ), + '3' => array( + 'label' => 'RC', + 'color' => 'warning' + ), + '4' => array( + 'label' => 'Stable', + 'color' => 'success' + ) + ); + + $lists['extensionTypes'] = array( + 'component' => 'com', + 'plugin' => 'plg', + 'module' => 'mod', + 'library' => 'lib', + 'template' => 'tpl', + 'language' => 'lang', + 'file' => 'file' + ); + + return $lists; + } + + public static function createBadge($id, $name, $type, $group = '', $client = ''): string + { + $tooltip = ''; + $tooltip .= ''; + $tooltip .= ucfirst($type); + $tooltip .= ' ' . $group; + $tooltip .= ' ' . $client; + $tooltip .= ''; + $tooltip .= '
'; + $tooltip .= ""; + $tooltip .= $name; + $return = base64_encode(urlencode(JUri::getInstance()->toString())); + + $bagde = ''; + $bagde .= ''; + $bagde .= strtoupper(substr($type,0,1)); + $bagde .= ''; + if (!empty($group)) { + $bagde .= strtolower(substr($group,0,1)); + } + if (!empty($client)) { + $bagde .= strtolower(substr($client,0,1)); + } + $bagde .= ''; + $bagde .= ''; + $bagde .= ''; + + return $bagde; + } + + public static function getReleaseToFile() + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName(array('id','file'))) + ->from($db->quoteName('#__extensiondistributor_release')) + ; + $db->setQuery($query); + + return $db->loadAssocList('id','file'); + } + + public static function createExtensionFileName($extension, $version, $stability, $file) + { + if ($extension->keepfilename) { // if we keep the filename no need to waste more time + return $file; + } + + $lists = self::getApplicationLists(); + $extensionTypes = $lists['extensionTypes']; + $stabilityList = $lists['stability']; + $pattern = $extension->newfilename; + $version = explode('.',$version); + $replacements = array( + 'type' => $extensionTypes[$extension->type], + 'element' => str_replace(array('com_','mod_'),'',$extension->element), + 'client' => $extension->client, + '_client' => $extension->client ? '_'.$extension->client : '', // Find a cleaner solution after + 'group' => $extension->group, + '_group' => $extension->group ? '_'.$extension->group : '', // Find a cleaner solution after + 'stability' => $stabilityList[$stability]['label'], + 'major' => $version[0], + 'minor' => $version[1], + 'release' => $version[2] + ); + foreach ($replacements as $k => $v) { + $pattern = str_replace('{'.$k.'}',$v,$pattern); + } + + return strtolower($pattern); + } + + public static function createPackageFileName($package, $version, $stability) + { + $lists = self::getApplicationLists(); + $stabilityList = $lists['stability']; + $pattern = $package->newfilename; + $version = explode('.',$version); + $replacements = array( + 'type' => 'pkg', + 'packagename' => $package->packagename, + 'stability' => $stabilityList[$stability]['label'], + 'major' => $version[0], + 'minor' => $version[1], + 'release' => $version[2] + ); + foreach ($replacements as $k => $v) { + $pattern = str_replace('{'.$k.'}',$v,$pattern); + } + + return strtolower($pattern); + } + + public static function getXmlVersions($xml_file) + { + $updates = simplexml_load_file($xml_file); + $versions = array(); + foreach ($updates->children() as $update) { + $versions[] = $update->version; + } + return $versions; + } + + public static function getReleaseDetails($releaseId) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName( + array('r.id','r.version_number','r.release_date','r.reltype','r.stability','r.file','r.extension','r.package'), + array('r_id','r_version_number','r_release_date','r_reltype','r_stability','r_file','r_extension','r_package') + )) + ->select('p.*') + ->join('LEFT','#__extensiondistributor_package AS p ON p.id = r.package') + ->from($db->quoteName('#__extensiondistributor_release','r')) + ->where($db->quoteName('r.id') . ' = ' . (int)$releaseId) + ; + $db->setQuery($query); + + return $db->loadObject(); + } + + public static function getReleaseFile($releaseId, $key = null): array + { + $file = true; + $msg = ''; + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $folder = $cparams->get('releases_directory'); + $folder = JPath::clean(JPATH_SITE . '/' . $folder . '/'); + + $query + ->clear() + ->select($db->quoteName( + array('id','file','extension','package','published') + )) + ->from($db->quoteName('#__extensiondistributor_release')) + ->where($db->quoteName('id') . ' = ' . (int)$releaseId) + ; + $db->setQuery($query); + $release = $db->loadObject(); + + if (is_null($release) || !$release) { + $file = false; + $msg = 'The requested file does not exist'; + return array('file' => $file, 'msg' => $msg); + } + if ($release->published != 1) { + $file = false; + $msg = 'The requested file is not published'; + return array('file' => $file, 'msg' => $msg); + } + + if ($release->extension > 0) { + // For extensions we do not check the key + $file = false; + $msg = 'The individual extension download is not authorized'; + return array('file' => $file, 'msg' => $msg); + } + + if ($release->package > 0) { + $latest = self::getPackageObject($release->package); + if (is_null($latest) || !$latest) { + $file = false; + $msg = 'The requested package is not available anymore'; + return array('file' => $file, 'msg' => $msg); + } + if (!$latest->free) { + if (is_null($key)) { + $file = false; + $msg = 'This is a commercial extension you need to provide a download key'; + return array('file' => $file, 'msg' => $msg); + } + $userId = self::getVar('key',$key,'key','user'); + if (!$userId) { + $file = false; + $msg = 'The download key you provided does not exists'; + return array('file' => $file, 'msg' => $msg); + } + $isLegitimate = self::isLegitimate($userId,$release->package); + if (!$isLegitimate) { + $file = false; + $msg = 'Your subscription is not active anymore'; + return array('file' => $file, 'msg' => $msg); + } + } + if (!JFile::exists($folder . $release->file)) { + $file = false; + $msg = sprintf('The file %s is not available', $release->file); + return array('file' => $file, 'msg' => $msg); + } + $file = true; + $msg = $release->file; + return array('file' => $file, 'msg' => $msg); + } + + return array('file' => $file, 'msg' => $msg); + } + + public static function isLegitimate($userId,$packageId): bool + { + $now = JFactory::getDate()->toSql(); + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->clear() + ->select($db->quoteName('id')) + ->from($db->quoteName('#__extensiondistributor_subscription')) + ->where( + array( + $db->quoteName('user') . ' = ' . (int)$userId, + $db->quoteName('active') . ' = 1', + $db->quoteName('date_start') . ' <= ' . $db->quote($now), + $db->quoteName('date_expire') . ' > ' . $db->quote($now) + ) + ) + ->extendWhere('AND', array( + $db->quoteName('package') . ' = ' . (int)$packageId, + $db->quoteName('package') . ' = ' . $db->quote('All') + ),'OR') + ; + $db->setQuery($query); + $result = $db->loadResult(); + + return $db->loadResult() ? true : false; + } + + public static function getRealIpAddr() + { + if (!empty($_SERVER['HTTP_CLIENT_IP'])) { + // Check IP from internet. + $ip = $_SERVER['HTTP_CLIENT_IP']; + } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) ) { + // Check IP is passed from proxy. + $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; + } else { + // Get IP address from remote address. + $ip = $_SERVER['REMOTE_ADDR']; + } + return $ip; + } + + public static function getFileUsage($filename) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName( + array('r.id','r.version_number','r.release_date','r.reltype','r.stability','r.file','r.extension','r.package') + )) + ->from($db->quoteName('#__extensiondistributor_release','r')) + ->select($db->quoteName( + array('e.id','e.name','e.element','e.type','e.group','e.client'), + array('e_id','e_name','e_element','e_type','e_group','e_client') + )) + ->join('LEFT','#__extensiondistributor_extension AS e ON r.extension = e.id') + ->select($db->quoteName( + array('p.id','p.packagename','p.name'), + array('p_id','p_packagename','p_name') + )) + ->join('LEFT','#__extensiondistributor_package AS p ON r.package = p.id') + ->where($db->quoteName('r.file') . ' = ' . $db->quote($filename)) + ; + $db->setQuery($query); + + return $db->loadObjectList('id'); + } + + public static function secureFolder($path, $action = 'secure') + { + $destPath = JPath::clean($path); + $srcPath = JPATH_ADMINISTRATOR.'/components/com_extensiondistributor/assets/security/'; + $files = array( + 'htaccess.txt' => '.htaccess', + 'index.htm' => 'index.htm', + 'index.html' => 'index.html', + 'index.php' => 'index.php', + 'web.config' => 'web.config' + ); + foreach ($files as $src => $dest) { + if ($action === 'secure') { + if (JFile::exists($srcPath.$src)) { + JFile::copy($srcPath.$src, $destPath.$dest); + } + } else { + if (JFile::exists($path.$dest)) { + JFile::delete($path.$dest); + } + } + } + } + + + /** + * Load the Composer Vendors + */ + public static function composerAutoload($target) + { + // insure we load the composer vendor only once + if (!isset(self::$composer[$target])) + { + // get the function name + $functionName = self::safeString('compose' . $target); + // check if method exist + if (method_exists(__CLASS__, $functionName)) + { + return self::{$functionName}(); + } + return false; + } + return self::$composer[$target]; + } + + /** + * Load the Component xml manifest. + */ + public static function manifest() + { + $manifestUrl = JPATH_ADMINISTRATOR."/components/com_extensiondistributor/extensiondistributor.xml"; + return simplexml_load_file($manifestUrl); + } + + /** + * Joomla version object + */ + protected static $JVersion; + + /** + * set/get Joomla version + */ + public static function jVersion() + { + // check if set + if (!self::checkObject(self::$JVersion)) + { + self::$JVersion = new JVersion(); + } + return self::$JVersion; + } + + /** + * Load the Contributors details. + */ + public static function getContributors() + { + // get params + $params = JComponentHelper::getParams('com_extensiondistributor'); + // start contributors array + $contributors = array(); + // get all Contributors (max 20) + $searchArray = range('0','20'); + foreach($searchArray as $nr) + { + if ((NULL !== $params->get("showContributor".$nr)) && ($params->get("showContributor".$nr) == 1 || $params->get("showContributor".$nr) == 3)) + { + // set link based of selected option + if($params->get("useContributor".$nr) == 1) + { + $link_front = ''; + $link_back = ''; + } + elseif($params->get("useContributor".$nr) == 2) + { + $link_front = ''; + $link_back = ''; + } + else + { + $link_front = ''; + $link_back = ''; + } + $contributors[$nr]['title'] = self::htmlEscape($params->get("titleContributor".$nr)); + $contributors[$nr]['name'] = $link_front.self::htmlEscape($params->get("nameContributor".$nr)).$link_back; + } + } + return $contributors; + } + + /** + * Can be used to build help urls. + **/ + public static function getHelpUrl($view) + { + return false; + } + + /** + * Configure the Linkbar. + */ + public static function addSubmenu($submenu) + { + // load user for access menus + $user = JFactory::getUser(); + // load the submenus to sidebar + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_DASHBOARD'), 'index.php?option=com_extensiondistributor&view=extensiondistributor', $submenu === 'extensiondistributor'); + if ($user->authorise('package.access', 'com_extensiondistributor') && $user->authorise('package.submenu', 'com_extensiondistributor')) + { + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_PACKAGES'), 'index.php?option=com_extensiondistributor&view=packages', $submenu === 'packages'); + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_PACKAGE_CATEGORIES'), 'index.php?option=com_categories&view=categories&extension=com_extensiondistributor', $submenu === 'categories'); + } + if (JComponentHelper::isEnabled('com_fields')) + { + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_PACKAGES_FIELDS'), 'index.php?option=com_fields&context=com_extensiondistributor.package', $submenu === 'fields.fields'); + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_PACKAGES_FIELDS_GROUPS'), 'index.php?option=com_fields&view=groups&context=com_extensiondistributor.package', $submenu === 'fields.groups'); + } + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_EXTENSIONS'), 'index.php?option=com_extensiondistributor&view=extensions', $submenu === 'extensions'); + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_SUBSCRIPTIONS'), 'index.php?option=com_extensiondistributor&view=subscriptions', $submenu === 'subscriptions'); + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_KEYS'), 'index.php?option=com_extensiondistributor&view=keys', $submenu === 'keys'); + // Access control (import.submenu). + if ($user->authorise('import.submenu', 'com_extensiondistributor')) + { + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_IMPORT'), 'index.php?option=com_extensiondistributor&view=import', $submenu === 'import'); + } + // Access control (maintenance.submenu). + if ($user->authorise('maintenance.submenu', 'com_extensiondistributor')) + { + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_MAINTENANCE'), 'index.php?option=com_extensiondistributor&view=maintenance', $submenu === 'maintenance'); + } + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_DOWNLOADS'), 'index.php?option=com_extensiondistributor&view=downloads', $submenu === 'downloads'); + if ($user->authorise('statistics.submenu', 'com_extensiondistributor')) + { + JHtmlSidebar::addEntry(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBMENU_STATISTICS'), 'index.php?option=com_extensiondistributor&view=statistics', $submenu === 'statistics'); + } + } + + /** + * Get a Variable + * + * @param string $table The table from which to get the variable + * @param string $where The value where + * @param string $whereString The target/field string where/name + * @param string $what The return field + * @param string $operator The operator between $whereString/field and $where/value + * @param string $main The component in which the table is found + * + * @return mix string/int/float + * + */ + public static function getVar($table, $where = null, $whereString = 'user', $what = 'id', $operator = '=', $main = 'extensiondistributor') + { + if(!$where) + { + $where = JFactory::getUser()->id; + } + // Get a db connection. + $db = JFactory::getDbo(); + // Create a new query object. + $query = $db->getQuery(true); + $query->select($db->quoteName(array($what))); + if (empty($table)) + { + $query->from($db->quoteName('#__'.$main)); + } + else + { + $query->from($db->quoteName('#__'.$main.'_'.$table)); + } + if (is_numeric($where)) + { + $query->where($db->quoteName($whereString) . ' '.$operator.' '.(int) $where); + } + elseif (is_string($where)) + { + $query->where($db->quoteName($whereString) . ' '.$operator.' '. $db->quote((string)$where)); + } + else + { + return false; + } + $db->setQuery($query); + $db->execute(); + if ($db->getNumRows()) + { + return $db->loadResult(); + } + return false; + } + + /** + * Get array of variables + * + * @param string $table The table from which to get the variables + * @param string $where The value where + * @param string $whereString The target/field string where/name + * @param string $what The return field + * @param string $operator The operator between $whereString/field and $where/value + * @param string $main The component in which the table is found + * @param bool $unique The switch to return a unique array + * + * @return array + * + */ + public static function getVars($table, $where = null, $whereString = 'user', $what = 'id', $operator = 'IN', $main = 'extensiondistributor', $unique = true) + { + if(!$where) + { + $where = JFactory::getUser()->id; + } + + if (!self::checkArray($where) && $where > 0) + { + $where = array($where); + } + + if (self::checkArray($where)) + { + // prep main <-- why? well if $main='' is empty then $table can be categories or users + if (self::checkString($main)) + { + $main = '_'.ltrim($main, '_'); + } + // Get a db connection. + $db = JFactory::getDbo(); + // Create a new query object. + $query = $db->getQuery(true); + + $query->select($db->quoteName(array($what))); + if (empty($table)) + { + $query->from($db->quoteName('#__'.$main)); + } + else + { + $query->from($db->quoteName('#_'.$main.'_'.$table)); + } + // add strings to array search + if ('IN_STRINGS' === $operator || 'NOT IN_STRINGS' === $operator) + { + $query->where($db->quoteName($whereString) . ' ' . str_replace('_STRINGS', '', $operator) . ' ("' . implode('","',$where) . '")'); + } + else + { + $query->where($db->quoteName($whereString) . ' ' . $operator . ' (' . implode(',',$where) . ')'); + } + $db->setQuery($query); + $db->execute(); + if ($db->getNumRows()) + { + if ($unique) + { + return array_unique($db->loadColumn()); + } + return $db->loadColumn(); + } + } + return false; + } + + public static function jsonToString($value, $sperator = ", ", $table = null, $id = 'id', $name = 'name') + { + // do some table foot work + $external = false; + if (strpos($table, '#__') !== false) + { + $external = true; + $table = str_replace('#__', '', $table); + } + // check if string is JSON + $result = json_decode($value, true); + if (json_last_error() === JSON_ERROR_NONE) + { + // is JSON + if (self::checkArray($result)) + { + if (self::checkString($table)) + { + $names = array(); + foreach ($result as $val) + { + if ($external) + { + if ($_name = self::getVar(null, $val, $id, $name, '=', $table)) + { + $names[] = $_name; + } + } + else + { + if ($_name = self::getVar($table, $val, $id, $name)) + { + $names[] = $_name; + } + } + } + if (self::checkArray($names)) + { + return (string) implode($sperator,$names); + } + } + return (string) implode($sperator,$result); + } + return (string) json_decode($value); + } + return $value; + } + + public static function isPublished($id,$type) + { + if ($type == 'raw') + { + $type = 'item'; + } + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + $query->select(array('a.published')); + $query->from('#__extensiondistributor_'.$type.' AS a'); + $query->where('a.id = '. (int) $id); + $query->where('a.published = 1'); + $db->setQuery($query); + $db->execute(); + $found = $db->getNumRows(); + if($found) + { + return true; + } + return false; + } + + public static function getGroupName($id) + { + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + $query->select(array('a.title')); + $query->from('#__usergroups AS a'); + $query->where('a.id = '. (int) $id); + $db->setQuery($query); + $db->execute(); + $found = $db->getNumRows(); + if($found) + { + return $db->loadResult(); + } + return $id; + } + + /** + * Get the action permissions + * + * @param string $view The related view name + * @param int $record The item to act upon + * @param string $views The related list view name + * @param mixed $target Only get this permission (like edit, create, delete) + * @param string $component The target component + * @param object $user The user whose permissions we are loading + * + * @return object The JObject of permission/authorised actions + * + */ + public static function getActions($view, &$record = null, $views = null, $target = null, $component = 'extensiondistributor', $user = 'null') + { + // load the user if not given + if (!self::checkObject($user)) + { + // get the user object + $user = JFactory::getUser(); + } + // load the JObject + $result = new JObject; + // make view name safe (just incase) + $view = self::safeString($view); + if (self::checkString($views)) + { + $views = self::safeString($views); + } + // get all actions from component + $actions = JAccess::getActionsFromFile( + JPATH_ADMINISTRATOR . '/components/com_' . $component . '/access.xml', + "/access/section[@name='component']/" + ); + // if non found then return empty JObject + if (empty($actions)) + { + return $result; + } + // get created by if not found + if (self::checkObject($record) && !isset($record->created_by) && isset($record->id)) + { + $record->created_by = self::getVar($view, $record->id, 'id', 'created_by', '=', $component); + } + // set actions only set in component settings + $componentActions = array('core.admin', 'core.manage', 'core.options', 'core.export'); + // check if we have a target + $checkTarget = false; + if ($target) + { + // convert to an array + if (self::checkString($target)) + { + $target = array($target); + } + // check if we are good to go + if (self::checkArray($target)) + { + $checkTarget = true; + } + } + // loop the actions and set the permissions + foreach ($actions as $action) + { + // check target action filter + if ($checkTarget && self::filterActions($view, $action->name, $target)) + { + continue; + } + // set to use component default + $fallback = true; + // reset permission per/action + $permission = false; + $catpermission = false; + // set area + $area = 'comp'; + // check if the record has an ID and the action is item related (not a component action) + if (self::checkObject($record) && isset($record->id) && $record->id > 0 && !in_array($action->name, $componentActions) && + (strpos($action->name, 'core.') !== false || strpos($action->name, $view . '.') !== false)) + { + // we are in item + $area = 'item'; + // The record has been set. Check the record permissions. + $permission = $user->authorise($action->name, 'com_' . $component . '.' . $view . '.' . (int) $record->id); + // if no permission found, check edit own + if (!$permission) + { + // With edit, if the created_by matches current user then dig deeper. + if (($action->name === 'core.edit' || $action->name === $view . '.edit') && $record->created_by > 0 && ($record->created_by == $user->id)) + { + // the correct target + $coreCheck = (array) explode('.', $action->name); + // check that we have both local and global access + if ($user->authorise($coreCheck[0] . '.edit.own', 'com_' . $component . '.' . $view . '.' . (int) $record->id) && + $user->authorise($coreCheck[0] . '.edit.own', 'com_' . $component)) + { + // allow edit + $result->set($action->name, true); + // set not to use global default + // because we already validated it + $fallback = false; + } + else + { + // do not allow edit + $result->set($action->name, false); + $fallback = false; + } + } + } + elseif (self::checkString($views) && isset($record->catid) && $record->catid > 0) + { + // we are in item + $area = 'category'; + // set the core check + $coreCheck = explode('.', $action->name); + $core = $coreCheck[0]; + // make sure we use the core. action check for the categories + if (strpos($action->name, $view) !== false && strpos($action->name, 'core.') === false ) + { + $coreCheck[0] = 'core'; + $categoryCheck = implode('.', $coreCheck); + } + else + { + $categoryCheck = $action->name; + } + // The record has a category. Check the category permissions. + $catpermission = $user->authorise($categoryCheck, 'com_' . $component . '.' . $views . '.category.' . (int) $record->catid); + if (!$catpermission && !is_null($catpermission)) + { + // With edit, if the created_by matches current user then dig deeper. + if (($action->name === 'core.edit' || $action->name === $view . '.edit') && $record->created_by > 0 && ($record->created_by == $user->id)) + { + // check that we have both local and global access + if ($user->authorise('core.edit.own', 'com_' . $component . '.' . $views . '.category.' . (int) $record->catid) && + $user->authorise($core . '.edit.own', 'com_' . $component)) + { + // allow edit + $result->set($action->name, true); + // set not to use global default + // because we already validated it + $fallback = false; + } + else + { + // do not allow edit + $result->set($action->name, false); + $fallback = false; + } + } + } + } + } + // if allowed then fallback on component global settings + if ($fallback) + { + // if item/category blocks access then don't fall back on global + if ((($area === 'item') && !$permission) || (($area === 'category') && !$catpermission)) + { + // do not allow + $result->set($action->name, false); + } + // Finally remember the global settings have the final say. (even if item allow) + // The local item permissions can block, but it can't open and override of global permissions. + // Since items are created by users and global permissions is set by system admin. + else + { + $result->set($action->name, $user->authorise($action->name, 'com_' . $component)); + } + } + } + return $result; + } + + /** + * Filter the action permissions + * + * @param string $action The action to check + * @param array $targets The array of target actions + * + * @return boolean true if action should be filtered out + * + */ + protected static function filterActions(&$view, &$action, &$targets) + { + foreach ($targets as $target) + { + if (strpos($action, $view . '.' . $target) !== false || + strpos($action, 'core.' . $target) !== false) + { + return false; + break; + } + } + return true; + } + + /** + * Get any component's model + */ + public static function getModel($name, $path = JPATH_COMPONENT_ADMINISTRATOR, $Component = 'Extensiondistributor', $config = array()) + { + // fix the name + $name = self::safeString($name); + // full path to models + $fullPathModels = $path . '/models'; + // load the model file + JModelLegacy::addIncludePath($fullPathModels, $Component . 'Model'); + // make sure the table path is loaded + if (!isset($config['table_path']) || !self::checkString($config['table_path'])) + { + // This is the JCB default path to tables in Joomla 3.x + $config['table_path'] = JPATH_ADMINISTRATOR . '/components/com_' . strtolower($Component) . '/tables'; + } + // get instance + $model = JModelLegacy::getInstance($name, $Component . 'Model', $config); + // if model not found (strange) + if ($model == false) + { + jimport('joomla.filesystem.file'); + // get file path + $filePath = $path . '/' . $name . '.php'; + $fullPathModel = $fullPathModels . '/' . $name . '.php'; + // check if it exists + if (File::exists($filePath)) + { + // get the file + require_once $filePath; + } + elseif (File::exists($fullPathModel)) + { + // get the file + require_once $fullPathModel; + } + // build class names + $modelClass = $Component . 'Model' . $name; + if (class_exists($modelClass)) + { + // initialize the model + return new $modelClass($config); + } + } + return $model; + } + + /** + * Add to asset Table + */ + public static function setAsset($id, $table, $inherit = true) + { + $parent = JTable::getInstance('Asset'); + $parent->loadByName('com_extensiondistributor'); + + $parentId = $parent->id; + $name = 'com_extensiondistributor.'.$table.'.'.$id; + $title = ''; + + $asset = JTable::getInstance('Asset'); + $asset->loadByName($name); + + // Check for an error. + $error = $asset->getError(); + + if ($error) + { + return false; + } + else + { + // Specify how a new or moved node asset is inserted into the tree. + if ($asset->parent_id != $parentId) + { + $asset->setLocation($parentId, 'last-child'); + } + + // Prepare the asset to be stored. + $asset->parent_id = $parentId; + $asset->name = $name; + $asset->title = $title; + // get the default asset rules + $rules = self::getDefaultAssetRules('com_extensiondistributor', $table, $inherit); + if ($rules instanceof JAccessRules) + { + $asset->rules = (string) $rules; + } + + if (!$asset->check() || !$asset->store()) + { + JFactory::getApplication()->enqueueMessage($asset->getError(), 'warning'); + return false; + } + else + { + // Create an asset_id or heal one that is corrupted. + $object = new stdClass(); + + // Must be a valid primary key value. + $object->id = $id; + $object->asset_id = (int) $asset->id; + + // Update their asset_id to link to the asset table. + return JFactory::getDbo()->updateObject('#__extensiondistributor_'.$table, $object, 'id'); + } + } + return false; + } + + /** + * Gets the default asset Rules for a component/view. + */ + protected static function getDefaultAssetRules($component, $view, $inherit = true) + { + // if new or inherited + $assetId = 0; + // Only get the actual item rules if not inheriting + if (!$inherit) + { + // Need to find the asset id by the name of the component. + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote($component)); + $db->setQuery($query); + $db->execute(); + // check that there is a value + if ($db->getNumRows()) + { + // asset already set so use saved rules + $assetId = (int) $db->loadResult(); + } + } + // get asset rules + $result = JAccess::getAssetRules($assetId); + if ($result instanceof JAccessRules) + { + $_result = (string) $result; + $_result = json_decode($_result); + foreach ($_result as $name => &$rule) + { + $v = explode('.', $name); + if ($view !== $v[0]) + { + // remove since it is not part of this view + unset($_result->$name); + } + elseif ($inherit) + { + // clear the value since we inherit + $rule = array(); + } + } + // check if there are any view values remaining + if (count((array) $_result)) + { + $_result = json_encode($_result); + $_result = array($_result); + // Instantiate and return the JAccessRules object for the asset rules. + $rules = new JAccessRules($_result); + // return filtered rules + return $rules; + } + } + return $result; + } + + /** + * xmlAppend + * + * @param SimpleXMLElement $xml The XML element reference in which to inject a comment + * @param mixed $node A SimpleXMLElement node to append to the XML element reference, or a stdClass object containing a comment attribute to be injected before the XML node and a fieldXML attribute containing a SimpleXMLElement + * + * @return null + * + */ + public static function xmlAppend(&$xml, $node) + { + if (!$node) + { + // element was not returned + return; + } + switch (get_class($node)) + { + case 'stdClass': + if (property_exists($node, 'comment')) + { + self::xmlComment($xml, $node->comment); + } + if (property_exists($node, 'fieldXML')) + { + self::xmlAppend($xml, $node->fieldXML); + } + break; + case 'SimpleXMLElement': + $domXML = dom_import_simplexml($xml); + $domNode = dom_import_simplexml($node); + $domXML->appendChild($domXML->ownerDocument->importNode($domNode, true)); + $xml = simplexml_import_dom($domXML); + break; + } + } + + /** + * xmlComment + * + * @param SimpleXMLElement $xml The XML element reference in which to inject a comment + * @param string $comment The comment to inject + * + * @return null + * + */ + public static function xmlComment(&$xml, $comment) + { + $domXML = dom_import_simplexml($xml); + $domComment = new DOMComment($comment); + $nodeTarget = $domXML->ownerDocument->importNode($domComment, true); + $domXML->appendChild($nodeTarget); + $xml = simplexml_import_dom($domXML); + } + + /** + * xmlAddAttributes + * + * @param SimpleXMLElement $xml The XML element reference in which to inject a comment + * @param array $attributes The attributes to apply to the XML element + * + * @return null + * + */ + public static function xmlAddAttributes(&$xml, $attributes = array()) + { + foreach ($attributes as $key => $value) + { + $xml->addAttribute($key, $value); + } + } + + /** + * xmlAddOptions + * + * @param SimpleXMLElement $xml The XML element reference in which to inject a comment + * @param array $options The options to apply to the XML element + * + * @return void + * + */ + public static function xmlAddOptions(&$xml, $options = array()) + { + foreach ($options as $key => $value) + { + $addOption = $xml->addChild('option'); + $addOption->addAttribute('value', $key); + $addOption[] = $value; + } + } + + /** + * get the field object + * + * @param array $attributes The array of attributes + * @param string $default The default of the field + * @param array $options The options to apply to the XML element + * + * @return object + * + */ + public static function getFieldObject(&$attributes, $default = '', $options = null) + { + // make sure we have attributes and a type value + if (self::checkArray($attributes) && isset($attributes['type'])) + { + // make sure the form helper class is loaded + if (!method_exists('JFormHelper', 'loadFieldType')) + { + jimport('joomla.form.form'); + } + // get field type + $field = JFormHelper::loadFieldType($attributes['type'], true); + // get field xml + $XML = self::getFieldXML($attributes, $options); + // setup the field + $field->setup($XML, $default); + // return the field object + return $field; + } + return false; + } + + /** + * get the field xml + * + * @param array $attributes The array of attributes + * @param array $options The options to apply to the XML element + * + * @return object + * + */ + public static function getFieldXML(&$attributes, $options = null) + { + // make sure we have attributes and a type value + if (self::checkArray($attributes)) + { + // start field xml + $XML = new SimpleXMLElement(''); + // load the attributes + self::xmlAddAttributes($XML, $attributes); + // check if we have options + if (self::checkArray($options)) + { + // load the options + self::xmlAddOptions($XML, $options); + } + // return the field xml + return $XML; + } + return false; + } + + /** + * Render Bool Button + * + * @param array $args All the args for the button + * 0) name + * 1) additional (options class) // not used at this time + * 2) default + * 3) yes (name) + * 4) no (name) + * + * @return string The input html of the button + * + */ + public static function renderBoolButton() + { + $args = func_get_args(); + // check if there is additional button class + $additional = isset($args[1]) ? (string) $args[1] : ''; // not used at this time + // button attributes + $buttonAttributes = array( + 'type' => 'radio', + 'name' => isset($args[0]) ? self::htmlEscape($args[0]) : 'bool_button', + 'label' => isset($args[0]) ? self::safeString(self::htmlEscape($args[0]), 'Ww') : 'Bool Button', // not seen anyway + 'class' => 'btn-group', + 'filter' => 'INT', + 'default' => isset($args[2]) ? (int) $args[2] : 0); + // set the button options + $buttonOptions = array( + '1' => isset($args[3]) ? self::htmlEscape($args[3]) : 'JYES', + '0' => isset($args[4]) ? self::htmlEscape($args[4]) : 'JNO'); + // return the input + return self::getFieldObject($buttonAttributes, $buttonAttributes['default'], $buttonOptions)->input; + } + + /** + * Check if have an json string + * + * @input string The json string to check + * + * @returns bool true on success + */ + public static function checkJson($string) + { + if (self::checkString($string)) + { + json_decode($string); + return (json_last_error() === JSON_ERROR_NONE); + } + return false; + } + + /** + * Check if have an object with a length + * + * @input object The object to check + * + * @returns bool true on success + */ + public static function checkObject($object) + { + if (isset($object) && is_object($object)) + { + return count((array)$object) > 0; + } + return false; + } + + /** + * Check if have an array with a length + * + * @input array The array to check + * + * @returns bool/int number of items in array on success + */ + public static function checkArray($array, $removeEmptyString = false) + { + if (isset($array) && is_array($array) && ($nr = count((array)$array)) > 0) + { + // also make sure the empty strings are removed + if ($removeEmptyString) + { + foreach ($array as $key => $string) + { + if (empty($string)) + { + unset($array[$key]); + } + } + return self::checkArray($array, false); + } + return $nr; + } + return false; + } + + /** + * Check if have a string with a length + * + * @input string The string to check + * + * @returns bool true on success + */ + public static function checkString($string) + { + if (isset($string) && is_string($string) && strlen($string) > 0) + { + return true; + } + return false; + } + + /** + * Check if we are connected + * Thanks https://stackoverflow.com/a/4860432/1429677 + * + * @returns bool true on success + */ + public static function isConnected() + { + // If example.com is down, then probably the whole internet is down, since IANA maintains the domain. Right? + $connected = @fsockopen("www.example.com", 80); + // website, port (try 80 or 443) + if ($connected) + { + //action when connected + $is_conn = true; + fclose($connected); + } + else + { + //action in connection failure + $is_conn = false; + } + return $is_conn; + } + + /** + * Merge an array of array's + * + * @input array The arrays you would like to merge + * + * @returns array on success + */ + public static function mergeArrays($arrays) + { + if(self::checkArray($arrays)) + { + $arrayBuket = array(); + foreach ($arrays as $array) + { + if (self::checkArray($array)) + { + $arrayBuket = array_merge($arrayBuket, $array); + } + } + return $arrayBuket; + } + return false; + } + + // typo sorry! + public static function sorten($string, $length = 40, $addTip = true) + { + return self::shorten($string, $length, $addTip); + } + + /** + * Shorten a string + * + * @input string The you would like to shorten + * + * @returns string on success + */ + public static function shorten($string, $length = 40, $addTip = true) + { + if (self::checkString($string)) + { + $initial = strlen($string); + $words = preg_split('/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE); + $words_count = count((array)$words); + + $word_length = 0; + $last_word = 0; + for (; $last_word < $words_count; ++$last_word) + { + $word_length += strlen($words[$last_word]); + if ($word_length > $length) + { + break; + } + } + + $newString = implode(array_slice($words, 0, $last_word)); + $final = strlen($newString); + if ($initial != $final && $addTip) + { + $title = self::shorten($string, 400 , false); + return ''.trim($newString).'...'; + } + elseif ($initial != $final && !$addTip) + { + return trim($newString).'...'; + } + } + return $string; + } + + /** + * Making strings safe (various ways) + * + * @input string The you would like to make safe + * + * @returns string on success + */ + public static function safeString($string, $type = 'L', $spacer = '_', $replaceNumbers = true, $keepOnlyCharacters = true) + { + if ($replaceNumbers === true) + { + // remove all numbers and replace with english text version (works well only up to millions) + $string = self::replaceNumbers($string); + } + // 0nly continue if we have a string + if (self::checkString($string)) + { + // create file name without the extention that is safe + if ($type === 'filename') + { + // make sure VDM is not in the string + $string = str_replace('VDM', 'vDm', $string); + // Remove anything which isn't a word, whitespace, number + // or any of the following caracters -_() + // If you don't need to handle multi-byte characters + // you can use preg_replace rather than mb_ereg_replace + // Thanks @Łukasz Rysiak! + // $string = mb_ereg_replace("([^\w\s\d\-_\(\)])", '', $string); + $string = preg_replace("([^\w\s\d\-_\(\)])", '', $string); + // http://stackoverflow.com/a/2021729/1429677 + return preg_replace('/\s+/', ' ', $string); + } + // remove all other characters + $string = trim($string); + $string = preg_replace('/'.$spacer.'+/', ' ', $string); + $string = preg_replace('/\s+/', ' ', $string); + // Transliterate string + $string = self::transliterate($string); + // remove all and keep only characters + if ($keepOnlyCharacters) + { + $string = preg_replace("/[^A-Za-z ]/", '', $string); + } + // keep both numbers and characters + else + { + $string = preg_replace("/[^A-Za-z0-9 ]/", '', $string); + } + // select final adaptations + if ($type === 'L' || $type === 'strtolower') + { + // replace white space with underscore + $string = preg_replace('/\s+/', $spacer, $string); + // default is to return lower + return strtolower($string); + } + elseif ($type === 'W') + { + // return a string with all first letter of each word uppercase(no undersocre) + return ucwords(strtolower($string)); + } + elseif ($type === 'w' || $type === 'word') + { + // return a string with all lowercase(no undersocre) + return strtolower($string); + } + elseif ($type === 'Ww' || $type === 'Word') + { + // return a string with first letter of the first word uppercase and all the rest lowercase(no undersocre) + return ucfirst(strtolower($string)); + } + elseif ($type === 'WW' || $type === 'WORD') + { + // return a string with all the uppercase(no undersocre) + return strtoupper($string); + } + elseif ($type === 'U' || $type === 'strtoupper') + { + // replace white space with underscore + $string = preg_replace('/\s+/', $spacer, $string); + // return all upper + return strtoupper($string); + } + elseif ($type === 'F' || $type === 'ucfirst') + { + // replace white space with underscore + $string = preg_replace('/\s+/', $spacer, $string); + // return with first caracter to upper + return ucfirst(strtolower($string)); + } + elseif ($type === 'cA' || $type === 'cAmel' || $type === 'camelcase') + { + // convert all words to first letter uppercase + $string = ucwords(strtolower($string)); + // remove white space + $string = preg_replace('/\s+/', '', $string); + // now return first letter lowercase + return lcfirst($string); + } + // return string + return $string; + } + // not a string + return ''; + } + + public static function transliterate($string) + { + // set tag only once + if (!self::checkString(self::$langTag)) + { + // get global value + self::$langTag = JComponentHelper::getParams('com_extensiondistributor')->get('language', 'en-GB'); + } + // Transliterate on the language requested + $lang = Language::getInstance(self::$langTag); + return $lang->transliterate($string); + } + + public static function htmlEscape($var, $charset = 'UTF-8', $shorten = false, $length = 40) + { + if (self::checkString($var)) + { + $filter = new JFilterInput(); + $string = $filter->clean(html_entity_decode(htmlentities($var, ENT_COMPAT, $charset)), 'HTML'); + if ($shorten) + { + return self::shorten($string,$length); + } + return $string; + } + else + { + return ''; + } + } + + public static function replaceNumbers($string) + { + // set numbers array + $numbers = array(); + // first get all numbers + preg_match_all('!\d+!', $string, $numbers); + // check if we have any numbers + if (isset($numbers[0]) && self::checkArray($numbers[0])) + { + foreach ($numbers[0] as $number) + { + $searchReplace[$number] = self::numberToString((int)$number); + } + // now replace numbers in string + $string = str_replace(array_keys($searchReplace), array_values($searchReplace),$string); + // check if we missed any, strange if we did. + return self::replaceNumbers($string); + } + // return the string with no numbers remaining. + return $string; + } + + /** + * Convert an integer into an English word string + * Thanks to Tom Nicholson + * + * @input an int + * @returns a string + */ + public static function numberToString($x) + { + $nwords = array( "zero", "one", "two", "three", "four", "five", "six", "seven", + "eight", "nine", "ten", "eleven", "twelve", "thirteen", + "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", + "nineteen", "twenty", 30 => "thirty", 40 => "forty", + 50 => "fifty", 60 => "sixty", 70 => "seventy", 80 => "eighty", + 90 => "ninety" ); + + if(!is_numeric($x)) + { + $w = $x; + } + elseif(fmod($x, 1) != 0) + { + $w = $x; + } + else + { + if($x < 0) + { + $w = 'minus '; + $x = -$x; + } + else + { + $w = ''; + // ... now $x is a non-negative integer. + } + + if($x < 21) // 0 to 20 + { + $w .= $nwords[$x]; + } + elseif($x < 100) // 21 to 99 + { + $w .= $nwords[10 * floor($x/10)]; + $r = fmod($x, 10); + if($r > 0) + { + $w .= ' '. $nwords[$r]; + } + } + elseif($x < 1000) // 100 to 999 + { + $w .= $nwords[floor($x/100)] .' hundred'; + $r = fmod($x, 100); + if($r > 0) + { + $w .= ' and '. self::numberToString($r); + } + } + elseif($x < 1000000) // 1000 to 999999 + { + $w .= self::numberToString(floor($x/1000)) .' thousand'; + $r = fmod($x, 1000); + if($r > 0) + { + $w .= ' '; + if($r < 100) + { + $w .= 'and '; + } + $w .= self::numberToString($r); + } + } + else // millions + { + $w .= self::numberToString(floor($x/1000000)) .' million'; + $r = fmod($x, 1000000); + if($r > 0) + { + $w .= ' '; + if($r < 100) + { + $w .= 'and '; + } + $w .= self::numberToString($r); + } + } + } + return $w; + } + + /** + * Random Key + * + * @returns a string + */ + public static function randomkey($size) + { + $bag = "abcefghijknopqrstuwxyzABCDDEFGHIJKLLMMNOPQRSTUVVWXYZabcddefghijkllmmnopqrstuvvwxyzABCEFGHIJKNOPQRSTUWXYZ"; + $key = array(); + $bagsize = strlen($bag) - 1; + for ($i = 0; $i < $size; $i++) + { + $get = rand(0, $bagsize); + $key[] = $bag[$get]; + } + return implode($key); + } +} + diff --git a/admin/helpers/headercheck.php b/admin/helpers/headercheck.php new file mode 100644 index 0000000..cc0bfa1 --- /dev/null +++ b/admin/helpers/headercheck.php @@ -0,0 +1,80 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +class extensiondistributorHeaderCheck +{ + function js_loaded($script_name) + { + // UIkit check point + if (strpos($script_name,'uikit') !== false) + { + $app = JFactory::getApplication(); + $getTemplateName = $app->getTemplate('template')->template; + + if (strpos($getTemplateName,'yoo') !== false) + { + return true; + } + } + + $document = JFactory::getDocument(); + $head_data = $document->getHeadData(); + foreach (array_keys($head_data['scripts']) as $script) + { + if (stristr($script, $script_name)) + { + return true; + } + } + + return false; + } + + function css_loaded($script_name) + { + // UIkit check point + if (strpos($script_name,'uikit') !== false) + { + $app = JFactory::getApplication(); + $getTemplateName = $app->getTemplate('template')->template; + + if (strpos($getTemplateName,'yoo') !== false) + { + return true; + } + } + + $document = JFactory::getDocument(); + $head_data = $document->getHeadData(); + + foreach (array_keys($head_data['styleSheets']) as $script) + { + if (stristr($script, $script_name)) + { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/admin/helpers/html/batch_.php b/admin/helpers/html/batch_.php new file mode 100644 index 0000000..313bf41 --- /dev/null +++ b/admin/helpers/html/batch_.php @@ -0,0 +1,87 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('JPATH_PLATFORM') or die; + +/** + * Utility class to render a list view batch selection options + * + * @since 3.0 + */ +abstract class JHtmlBatch_ +{ + /** + * ListSelection + * + * @var array + * @since 3.0 + */ + protected static $ListSelection = array(); + + /** + * Render the batch selection options. + * + * @return string The necessary HTML to display the batch selection options + * + * @since 3.0 + */ + public static function render() + { + // Collect display data + $data = new stdClass; + $data->ListSelection = static::getListSelection(); + + // Create a layout object and ask it to render the batch selection options + $layout = new JLayoutFile('batchselection'); + $batchHtml = $layout->render($data); + + return $batchHtml; + } + + /** + * Method to add a list selection to the batch modal + * + * @param string $label Label for the menu item. + * @param string $name Name for the filter. Also used as id. + * @param string $options Options for the select field. + * @param bool $noDefault Don't the label as the empty option + * + * @return void + * + * @since 3.0 + */ + public static function addListSelection($label, $name, $options, $noDefault = false) + { + array_push(static::$ListSelection, array('label' => $label, 'name' => $name, 'options' => $options, 'noDefault' => $noDefault)); + } + + /** + * Returns an array of all ListSelection + * + * @return array + * + * @since 3.0 + */ + public static function getListSelection() + { + return static::$ListSelection; + } +} diff --git a/admin/helpers/html/index.html b/admin/helpers/html/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/helpers/html/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/helpers/index.html b/admin/helpers/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/helpers/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/index.html b/admin/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/language/en-GB/en-GB.com_extensiondistributor.ini b/admin/language/en-GB/en-GB.com_extensiondistributor.ini new file mode 100644 index 0000000..fbd7483 --- /dev/null +++ b/admin/language/en-GB/en-GB.com_extensiondistributor.ini @@ -0,0 +1,714 @@ +COM_EXTENSIONDISTRIBUTOR="Extension Distributor" +COM_EXTENSIONDISTRIBUTOR_ALL_PACKAGES="All packages" +COM_EXTENSIONDISTRIBUTOR_ARCHIVED="Archived" +COM_EXTENSIONDISTRIBUTOR_AUTHOR="Author" +COM_EXTENSIONDISTRIBUTOR_BACK="Back" +COM_EXTENSIONDISTRIBUTOR_CLOSE_NEW="Close & New" +COM_EXTENSIONDISTRIBUTOR_CONFIG_ALPHA="Alpha" +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHOR="Author Info" +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHOREMAIL_HINT="your@email.com" +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHOREMAIL_LABEL="Author Email" +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHOREMAIL_MESSAGE="Error! Please add some text here." +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHORURL_HINT="https://agerix.fr" +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHORURL_LABEL="Author URL" +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHOR_EMAIL_DESC="The email address of the author of this component." +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHOR_EMAIL_LABEL="Author Email" +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHOR_LABEL="Author" +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHOR_NAME_DESC="The name of the author of this component." +COM_EXTENSIONDISTRIBUTOR_CONFIG_AUTHOR_NAME_LABEL="Author Name" +COM_EXTENSIONDISTRIBUTOR_CONFIG_BETA="Beta" +COM_EXTENSIONDISTRIBUTOR_CONFIG_CATID_DESCRIPTION="Select one of the following categories" +COM_EXTENSIONDISTRIBUTOR_CONFIG_CATID_LABEL="Import category" +COM_EXTENSIONDISTRIBUTOR_CONFIG_CHECK_TIMER_DESC="Set the intervals for the auto checkin fuction of tables that checks out the items to an user." +COM_EXTENSIONDISTRIBUTOR_CONFIG_CHECK_TIMER_LABEL="Check in timer" +COM_EXTENSIONDISTRIBUTOR_CONFIG_CHECK_TIMER_OPTION_FIVE="Once a week" +COM_EXTENSIONDISTRIBUTOR_CONFIG_CHECK_TIMER_OPTION_FOUR="Every second day" +COM_EXTENSIONDISTRIBUTOR_CONFIG_CHECK_TIMER_OPTION_ONE="Every five hours" +COM_EXTENSIONDISTRIBUTOR_CONFIG_CHECK_TIMER_OPTION_SIX="Never" +COM_EXTENSIONDISTRIBUTOR_CONFIG_CHECK_TIMER_OPTION_THREE="Once a day" +COM_EXTENSIONDISTRIBUTOR_CONFIG_CHECK_TIMER_OPTION_TWO="Every twelve hours" +COM_EXTENSIONDISTRIBUTOR_CONFIG_COPYRIGHT_LABEL="Copyright" +COM_EXTENSIONDISTRIBUTOR_CONFIG_DEVELOPMENT="Development" +COM_EXTENSIONDISTRIBUTOR_CONFIG_DIRECTORIES="Directories" +COM_EXTENSIONDISTRIBUTOR_CONFIG_GLOBAL_DESC="The Global Parameters" +COM_EXTENSIONDISTRIBUTOR_CONFIG_GLOBAL_LABEL="Global" +COM_EXTENSIONDISTRIBUTOR_CONFIG_IMPORT="Import" +COM_EXTENSIONDISTRIBUTOR_CONFIG_IMPORT_DIRECTORY_LABEL="Import directory path" +COM_EXTENSIONDISTRIBUTOR_CONFIG_INFOURL_LABEL="Info URL" +COM_EXTENSIONDISTRIBUTOR_CONFIG_JCB_DIRECTORY_LABEL="JCB directory path" +COM_EXTENSIONDISTRIBUTOR_CONFIG_KEEPFILENAME_LABEL="Keep original filename" +COM_EXTENSIONDISTRIBUTOR_CONFIG_LICENSE_LABEL="License" +COM_EXTENSIONDISTRIBUTOR_CONFIG_MAINTENANCE="Maintenance" +COM_EXTENSIONDISTRIBUTOR_CONFIG_NEWFILENAME_DESCRIPTION="You can define your own pattern in this field. The available placeholders are:
{type} : The type (3 letters)
{element} : The element
{_group} : The group (conditionnal)
{group} : The group (always displayed)
{_client} : The client (conditionnal)
{client} : The client (always displayed)
{major} : Version major
{minor} : Version major
{release} : Version release
{stability} : Stability level
Note: for component and modules the com_ and mod_ prefix are removed. The conditionnal placeholders are remove if the data is empty." +COM_EXTENSIONDISTRIBUTOR_CONFIG_NEWFILENAME_LABEL="Filename pattern" +COM_EXTENSIONDISTRIBUTOR_CONFIG_NEW_VERSION="New version" +COM_EXTENSIONDISTRIBUTOR_CONFIG_NO="No" +COM_EXTENSIONDISTRIBUTOR_CONFIG_NOTE_PATH_DESCRIPTION="The directory configuration should be set in the maintenance tab. This ensures that changing the path will move folder together with its content." +COM_EXTENSIONDISTRIBUTOR_CONFIG_PACKAGERURL_HINT="https://agerix.fr" +COM_EXTENSIONDISTRIBUTOR_CONFIG_PACKAGERURL_LABEL="Packager URL" +COM_EXTENSIONDISTRIBUTOR_CONFIG_PACKAGER_LABEL="Packager" +COM_EXTENSIONDISTRIBUTOR_CONFIG_PACKAGES="Packages" +COM_EXTENSIONDISTRIBUTOR_CONFIG_PHP_MINIMUM_LABEL="PHP minimum" +COM_EXTENSIONDISTRIBUTOR_CONFIG_PLEASE_SELECT="Please select" +COM_EXTENSIONDISTRIBUTOR_CONFIG_RC="RC" +COM_EXTENSIONDISTRIBUTOR_CONFIG_RELEASES_DIRECTORY_LABEL="Releases directory path" +COM_EXTENSIONDISTRIBUTOR_CONFIG_RELTYPE_LABEL="Release type" +COM_EXTENSIONDISTRIBUTOR_CONFIG_SECURITY="Security" +COM_EXTENSIONDISTRIBUTOR_CONFIG_SERVER_NAME_LABEL="Update server name" +COM_EXTENSIONDISTRIBUTOR_CONFIG_STABILITY_LABEL="Stability level" +COM_EXTENSIONDISTRIBUTOR_CONFIG_STABLE="Stable" +COM_EXTENSIONDISTRIBUTOR_CONFIG_TARGETPLATFORM_DESCRIPTION="You can use a REGEX" +COM_EXTENSIONDISTRIBUTOR_CONFIG_TARGETPLATFORM_LABEL="Joomla version" +COM_EXTENSIONDISTRIBUTOR_CONFIG_UPDATESERVER_URL_LABEL="Update Server URL" +COM_EXTENSIONDISTRIBUTOR_CONFIG_UPDATES_DIRECTORY_LABEL="Updates directory" +COM_EXTENSIONDISTRIBUTOR_CONFIG_YES="Yes" +COM_EXTENSIONDISTRIBUTOR_CONTRIBUTOR="Contributor" +COM_EXTENSIONDISTRIBUTOR_CONTRIBUTORS="Contributors" +COM_EXTENSIONDISTRIBUTOR_CREATE_NEW_S="Create New %s" +COM_EXTENSIONDISTRIBUTOR_DASH="Dashboard" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD="Extension Distributor Dashboard" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD_DOWNLOADS="Downloads

" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD_EXTENSIONS="Extensions

" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD_EXTENSIONS_CATID="Categories" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD_IMPORT="Import

" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD_KEYS="Keys

" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD_MAINTENANCE="Maintenance

" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD_PACKAGES="Packages

" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD_STATISTICS="Statistics

" +COM_EXTENSIONDISTRIBUTOR_DASHBOARD_SUBSCRIPTIONS="Subscriptions

" +COM_EXTENSIONDISTRIBUTOR_DATE="Date" +COM_EXTENSIONDISTRIBUTOR_DELETE="Delete" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD="Download" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS="Downloads" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_BATCH_OPTIONS="Batch process the selected Downloads" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_BATCH_TIP="All changes will be applied to all selected Downloads" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_BATCH_USE="Downloads Batch Use" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch downloads" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_DASHBOARD_LIST="Downloads Dashboard List" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of download" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_ARCHIVED="%s Downloads archived." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_ARCHIVED_1="%s Download archived." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_CHECKED_IN_0="No Download successfully checked in." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_CHECKED_IN_1="%d Download successfully checked in." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_CHECKED_IN_MORE="%d Downloads successfully checked in." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_DELETED="%s Downloads deleted." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_DELETED_1="%s Download deleted." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_FAILED_PUBLISHING="%s Downloads failed publishing." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_FAILED_PUBLISHING_1="%s Download failed publishing." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_FEATURED="%s Downloads featured." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_FEATURED_1="%s Download featured." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_PUBLISHED="%s Downloads published." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_PUBLISHED_1="%s Download published." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_TRASHED="%s Downloads trashed." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_TRASHED_1="%s Download trashed." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_UNFEATURED="%s Downloads unfeatured." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_UNFEATURED_1="%s Download unfeatured." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_UNPUBLISHED="%s Downloads unpublished." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_N_ITEMS_UNPUBLISHED_1="%s Download unpublished." +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_SUBMENU="Downloads Submenu" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_SUBMENU_DESC="Allows the users in this group to submenu of download" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_CREATED_BY_DESC="The user that created this Download." +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_CREATED_BY_LABEL="Created By" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_CREATED_DATE_DESC="The date this Download was created." +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_CREATED_DATE_LABEL="Created Date" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_CREATED_LABEL="Date/Time" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_DELETE_BUTTON_ACCESS="Download Delete Button Access" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_DELETE_BUTTON_ACCESS_DESC="Allows the users in this group to access the delete button." +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_DETAILS="Details" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_DOMAIN_LABEL="Domain" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_EDIT="Editing the Download" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_ERROR_UNIQUE_ALIAS="Another Download has the same alias." +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_ID="Id" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_IP_LABEL="IP address" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_JOOMLA_UPDATER="Joomla updater" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_KEY_LABEL="Download Key" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_MODIFIED_BY_DESC="The last user that modified this Download." +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_MODIFIED_BY_LABEL="Modified By" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_MODIFIED_DATE_DESC="The date this Download was modified." +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_MODIFIED_DATE_LABEL="Modified Date" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_NEW="A New Download" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_NO="No" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_ORDERING_LABEL="Ordering" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_OTHER="Other" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_PACKAGE_LABEL="Package" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_PUBLISHED_LABEL="State" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_PUBLISHING="Publishing" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_SAVE_WARNING="Alias already existed so a number was added at the end. You can re-edit the Download to customise the alias." +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_STATUS="Status" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_TRANSACTION_LABEL="Transaction type" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_USER_LABEL="Joomla User" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_VERSION_DESC="A count of the number of times this Download has been revised." +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_VERSION_LABEL="Version" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_WATCHFUL="Watchful" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_WEBSITE="Website" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_YES="Yes" +COM_EXTENSIONDISTRIBUTOR_EDIT_CREATED_BY="Edit Created By" +COM_EXTENSIONDISTRIBUTOR_EDIT_CREATED_BY_DESC="Allows users in this group to edit created by." +COM_EXTENSIONDISTRIBUTOR_EDIT_CREATED_DATE="Edit Created Date" +COM_EXTENSIONDISTRIBUTOR_EDIT_CREATED_DATE_DESC="Allows users in this group to edit created date." +COM_EXTENSIONDISTRIBUTOR_EDIT_S="Edit %s" +COM_EXTENSIONDISTRIBUTOR_EDIT_VERSIONS="Edit Version" +COM_EXTENSIONDISTRIBUTOR_EDIT_VERSIONS_DESC="Allows users in this group to edit versions." +COM_EXTENSIONDISTRIBUTOR_EXTENSION="Extension" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS="Extensions" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_BATCH_OPTIONS="Batch process the selected Extensions" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_BATCH_TIP="All changes will be applied to all selected Extensions" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_BATCH_USE="Extensions Batch Use" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch extensions" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_DASHBOARD_LIST="Extensions Dashboard List" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of extension" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_DOWNLOAD="Download" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_EDIT_VERSION="Extensions Edit Version" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_EDIT_VERSION_DESC="Allows users in this group to edit versions of version extensions" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_EXTENSION_TYPE="Extension type" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_LATEST_VERSION="Latest version" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_ARCHIVED="%s Extensions archived." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_ARCHIVED_1="%s Extension archived." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_CHECKED_IN_0="No Extension successfully checked in." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_CHECKED_IN_1="%d Extension successfully checked in." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_CHECKED_IN_MORE="%d Extensions successfully checked in." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_DELETED="%s Extensions deleted." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_DELETED_1="%s Extension deleted." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_FAILED_PUBLISHING="%s Extensions failed publishing." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_FAILED_PUBLISHING_1="%s Extension failed publishing." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_FEATURED="%s Extensions featured." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_FEATURED_1="%s Extension featured." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_PUBLISHED="%s Extensions published." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_PUBLISHED_1="%s Extension published." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_TRASHED="%s Extensions trashed." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_TRASHED_1="%s Extension trashed." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_UNFEATURED="%s Extensions unfeatured." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_UNFEATURED_1="%s Extension unfeatured." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_UNPUBLISHED="%s Extensions unpublished." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_N_ITEMS_UNPUBLISHED_1="%s Extension unpublished." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_RELEASE_DATE="Release date" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_SUBMENU="Extensions Submenu" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_SUBMENU_DESC="Allows the users in this group to submenu of extension" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_ADMINISTRATOR="administrator" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_ALIAS_LABEL="Alias" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CATEGORIES="Categories" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CATID_LABEL="Category" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CATID_OTHERNAME="Categories" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CATID_VIEW="extension" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CATID_VIEWS="extensions" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CLIENT_LABEL="Client" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_COMPONENT="Component" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CREATED_BY_DESC="The user that created this Extension." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CREATED_BY_LABEL="Created By" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CREATED_DATE_DESC="The date this Extension was created." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_CREATED_DATE_LABEL="Created Date" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_DESCRIPTION_LABEL="Description" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_DETAILS="Details" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_EDIT="Editing the Extension" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_ELEMENT_LABEL="Element" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_ERROR_UNIQUE_ALIAS="Another Extension has the same alias." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_FILE="File" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_GROUP_LABEL="Group" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_ID="Id" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_IMAGE_LABEL="Image" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_KEEPFILENAME_LABEL="Keep original filename" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_LANGUAGE="Language" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_LIBRARY="Library" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_MODIFIED_BY_DESC="The last user that modified this Extension." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_MODIFIED_BY_LABEL="Modified By" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_MODIFIED_DATE_DESC="The date this Extension was modified." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_MODIFIED_DATE_LABEL="Modified Date" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_MODULE="Module" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_NAME_LABEL="Name" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_NAME_MESSAGE="Error! Please add some text here." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_NEW="A New Extension" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_NEWFILENAME_DESCRIPTION="You can define your own pattern in this field. The available placeholders are:
{type} : The type (3 letters)
{element} : The element
{_group} : The group (conditionnal)
{group} : The group (always displayed)
{_client} : The client (conditionnal)
{client} : The client (always displayed)
{major} : Version major
{minor} : Version major
{release} : Version release
{stability} : Stability level
Note: for component and modules the com_ and mod_ prefix are removed. The conditionnal placeholders are remove if the data is empty." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_NEWFILENAME_LABEL="Filename pattern" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_NO="No" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_ORDERING_LABEL="Ordering" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_PERMISSION="Permissions" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_PLEASE_SELECT="Please select" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_PLUGIN="Plugin" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_PUBLISHING="Publishing" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_RELEASES="Releases" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_RELEASE_DATE="Release Date" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_SAVE_WARNING="Alias already existed so a number was added at the end. You can re-edit the Extension to customise the alias." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_SITE="site" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_STATUS="Status" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_TAGS_DESCRIPTION="Assign tags to EXTENSION items. Tag names must be unique." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_TAGS_LABEL="Tags" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_TEMPLATE="Template" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_TYPE_LABEL="Extension type" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_UPDATE_HINT="automatically generated..." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_UPDATE_LABEL="Update XML" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_VERSION_DESC="A count of the number of times this Extension has been revised." +COM_EXTENSIONDISTRIBUTOR_EXTENSION_VERSION_LABEL="Version" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_VERSION_NUMBER="Version Number" +COM_EXTENSIONDISTRIBUTOR_EXTENSION_YES="Yes" +COM_EXTENSIONDISTRIBUTOR_FILTER_ACTIVE_ASCENDING="Active ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_ACTIVE_DESCENDING="Active descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_DATETIME_ASCENDING="Date/Time ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_DATETIME_DESCENDING="Date/Time descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_DOMAIN_ASCENDING="Domain ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_DOMAIN_DESCENDING="Domain descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_EXPIRE_ASCENDING="Expire ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_EXPIRE_DESCENDING="Expire descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_EXTENSION_ASCENDING="Extension ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_EXTENSION_DESCENDING="Extension descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_EXTENSION_TYPE_ASCENDING="Extension type ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_EXTENSION_TYPE_DESCENDING="Extension type descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_IP_ADDRESS_ASCENDING="IP address ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_IP_ADDRESS_DESCENDING="IP address descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_JOOMLA_USER_ASCENDING="Joomla User ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_JOOMLA_USER_DESCENDING="Joomla User descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_NAME_ASCENDING="Name ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_NAME_DESCENDING="Name descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_PACKAGE_ASCENDING="Package ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_PACKAGE_DESCENDING="Package descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_PUBLISHED="Status" +COM_EXTENSIONDISTRIBUTOR_FILTER_PUBLISHED_EXTENSIONS="Status options for extensions" +COM_EXTENSIONDISTRIBUTOR_FILTER_PUBLISHED_KEYS="Status options for keys" +COM_EXTENSIONDISTRIBUTOR_FILTER_PUBLISHED_PACKAGES="Status options for packages" +COM_EXTENSIONDISTRIBUTOR_FILTER_PUBLISHED_RELEASES="Status options for releases" +COM_EXTENSIONDISTRIBUTOR_FILTER_PUBLISHED_SUBSCRIPTIONS="Status options for subscriptions" +COM_EXTENSIONDISTRIBUTOR_FILTER_RELEASE_DATE_ASCENDING="Release Date ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_RELEASE_DATE_DESCENDING="Release Date descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_RELEASE_TYPE_ASCENDING="Release type ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_RELEASE_TYPE_DESCENDING="Release type descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_SEARCH="Searchpackages" +COM_EXTENSIONDISTRIBUTOR_FILTER_SEARCH_DOWNLOADS="Search the download items. Prefix with ID: to search for an item by ID." +COM_EXTENSIONDISTRIBUTOR_FILTER_SEARCH_EXTENSIONS="Search the extension items. Prefix with ID: to search for an item by ID." +COM_EXTENSIONDISTRIBUTOR_FILTER_SEARCH_KEYS="Search the key items. Prefix with ID: to search for an item by ID." +COM_EXTENSIONDISTRIBUTOR_FILTER_SEARCH_PACKAGES="Search the package items. Prefix with ID: to search for an item by ID." +COM_EXTENSIONDISTRIBUTOR_FILTER_SEARCH_RELEASES="Search the release items. Prefix with ID: to search for an item by ID." +COM_EXTENSIONDISTRIBUTOR_FILTER_SEARCH_SUBSCRIPTIONS="Search the subscription items. Prefix with ID: to search for an item by ID." +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_ACCESS="Select Access" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_ACTIVE="Select Active" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_CATEGORIES="Select Categories" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_CLIENT="Select Client" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_EXTENSION="Select Extension" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_EXTENSION_TYPE="Select Extension type" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_FREE="Select Free" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_GROUP="Select Group" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_JOOMLA_USER="Select Joomla User" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_PACKAGE="Select Package" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_RELEASE_TYPE="Select Release type" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_STABILITY_LEVEL="Select Stability level" +COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_TRANSACTION_TYPE="Select Transaction type" +COM_EXTENSIONDISTRIBUTOR_FILTER_STARTS_ASCENDING="Starts ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_STARTS_DESCENDING="Starts descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_TRANSACTION_TYPE_ASCENDING="Transaction type ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_TRANSACTION_TYPE_DESCENDING="Transaction type descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_VERSION_ASCENDING="Version ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_VERSION_DESCENDING="Version descending" +COM_EXTENSIONDISTRIBUTOR_FILTER_VERSION_NUMBER_ASCENDING="Version Number ascending" +COM_EXTENSIONDISTRIBUTOR_FILTER_VERSION_NUMBER_DESCENDING="Version Number descending" +COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER="Help" +COM_EXTENSIONDISTRIBUTOR_IMPORT="Import" +COM_EXTENSIONDISTRIBUTOR_IMPORT_ACCESS="Import Access" +COM_EXTENSIONDISTRIBUTOR_IMPORT_ACCESS_DESC="Allows the users in this group to access import." +COM_EXTENSIONDISTRIBUTOR_IMPORT_DASHBOARD_LIST="Import Dashboard List" +COM_EXTENSIONDISTRIBUTOR_IMPORT_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of Import" +COM_EXTENSIONDISTRIBUTOR_IMPORT_DESC="Import" +COM_EXTENSIONDISTRIBUTOR_IMPORT_SUBMENU="Import Submenu" +COM_EXTENSIONDISTRIBUTOR_IMPORT_SUBMENU_DESC="Allows the users in this group to submenu of Import" +COM_EXTENSIONDISTRIBUTOR_INACTIVE="Inactive" +COM_EXTENSIONDISTRIBUTOR_INVALID_FILENAME="Invalid filename" +COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_ACCESS="- Keep Original Access -" +COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_CATEGORY="- Keep Original Category -" +COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_STATE="- Keep Original State -" +COM_EXTENSIONDISTRIBUTOR_KEY="Key" +COM_EXTENSIONDISTRIBUTOR_KEYS="Keys" +COM_EXTENSIONDISTRIBUTOR_KEYS_ACCESS_SITE="Keys (Site) Access" +COM_EXTENSIONDISTRIBUTOR_KEYS_ACCESS_SITE_DESC="Allows the users in this group to access site keys." +COM_EXTENSIONDISTRIBUTOR_KEYS_BATCH_OPTIONS="Batch process the selected Keys" +COM_EXTENSIONDISTRIBUTOR_KEYS_BATCH_TIP="All changes will be applied to all selected Keys" +COM_EXTENSIONDISTRIBUTOR_KEYS_BATCH_USE="Keys Batch Use" +COM_EXTENSIONDISTRIBUTOR_KEYS_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch keys" +COM_EXTENSIONDISTRIBUTOR_KEYS_DASHBOARD_LIST="Keys Dashboard List" +COM_EXTENSIONDISTRIBUTOR_KEYS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of key" +COM_EXTENSIONDISTRIBUTOR_KEYS_EDIT_VERSION="Keys Edit Version" +COM_EXTENSIONDISTRIBUTOR_KEYS_EDIT_VERSION_DESC="Allows users in this group to edit versions of version keys" +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_ARCHIVED="%s Keys archived." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_ARCHIVED_1="%s Key archived." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_CHECKED_IN_0="No Key successfully checked in." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_CHECKED_IN_1="%d Key successfully checked in." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_CHECKED_IN_MORE="%d Keys successfully checked in." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_DELETED="%s Keys deleted." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_DELETED_1="%s Key deleted." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_FAILED_PUBLISHING="%s Keys failed publishing." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_FAILED_PUBLISHING_1="%s Key failed publishing." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_FEATURED="%s Keys featured." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_FEATURED_1="%s Key featured." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_PUBLISHED="%s Keys published." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_PUBLISHED_1="%s Key published." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_TRASHED="%s Keys trashed." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_TRASHED_1="%s Key trashed." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_UNFEATURED="%s Keys unfeatured." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_UNFEATURED_1="%s Key unfeatured." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_UNPUBLISHED="%s Keys unpublished." +COM_EXTENSIONDISTRIBUTOR_KEYS_N_ITEMS_UNPUBLISHED_1="%s Key unpublished." +COM_EXTENSIONDISTRIBUTOR_KEYS_SUBMENU="Keys Submenu" +COM_EXTENSIONDISTRIBUTOR_KEYS_SUBMENU_DESC="Allows the users in this group to submenu of key" +COM_EXTENSIONDISTRIBUTOR_KEY_CREATED_BY_DESC="The user that created this Key." +COM_EXTENSIONDISTRIBUTOR_KEY_CREATED_BY_LABEL="Created By" +COM_EXTENSIONDISTRIBUTOR_KEY_CREATED_DATE_DESC="The date this Key was created." +COM_EXTENSIONDISTRIBUTOR_KEY_CREATED_DATE_LABEL="Created Date" +COM_EXTENSIONDISTRIBUTOR_KEY_DETAILS="Details" +COM_EXTENSIONDISTRIBUTOR_KEY_EDIT="Editing the Key" +COM_EXTENSIONDISTRIBUTOR_KEY_ERROR_UNIQUE_ALIAS="Another Key has the same alias." +COM_EXTENSIONDISTRIBUTOR_KEY_ID="Id" +COM_EXTENSIONDISTRIBUTOR_KEY_KEY_LABEL="Download Key" +COM_EXTENSIONDISTRIBUTOR_KEY_MODIFIED_BY_DESC="The last user that modified this Key." +COM_EXTENSIONDISTRIBUTOR_KEY_MODIFIED_BY_LABEL="Modified By" +COM_EXTENSIONDISTRIBUTOR_KEY_MODIFIED_DATE_DESC="The date this Key was modified." +COM_EXTENSIONDISTRIBUTOR_KEY_MODIFIED_DATE_LABEL="Modified Date" +COM_EXTENSIONDISTRIBUTOR_KEY_NEW="A New Key" +COM_EXTENSIONDISTRIBUTOR_KEY_ORDERING_LABEL="Ordering" +COM_EXTENSIONDISTRIBUTOR_KEY_PERMISSION="Permissions" +COM_EXTENSIONDISTRIBUTOR_KEY_PUBLISHING="Publishing" +COM_EXTENSIONDISTRIBUTOR_KEY_SAVE_WARNING="Alias already existed so a number was added at the end. You can re-edit the Key to customise the alias." +COM_EXTENSIONDISTRIBUTOR_KEY_STATUS="Status" +COM_EXTENSIONDISTRIBUTOR_KEY_USER_LABEL="Joomla User" +COM_EXTENSIONDISTRIBUTOR_KEY_VERSION_DESC="A count of the number of times this Key has been revised." +COM_EXTENSIONDISTRIBUTOR_KEY_VERSION_LABEL="Version" +COM_EXTENSIONDISTRIBUTOR_KEY_WEBSITE_DESCRIPTION="Enter the address of the website where this key will used." +COM_EXTENSIONDISTRIBUTOR_KEY_WEBSITE_LABEL="Website" +COM_EXTENSIONDISTRIBUTOR_LICENSE="License" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE="Maintenance" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_ACCESS="Maintenance Access" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_ACCESS_DESC="Allows the users in this group to access maintenance." +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_DASHBOARD_LIST="Maintenance Dashboard List" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of Maintenance" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_DESC="Maintenance" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_SUBMENU="Maintenance Submenu" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_SUBMENU_DESC="Allows the users in this group to submenu of Maintenance" +COM_EXTENSIONDISTRIBUTOR_NEW="New" +COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED="Not found, or access denied." +COM_EXTENSIONDISTRIBUTOR_NO_ACCESS_GRANTED="No Access Granted!" +COM_EXTENSIONDISTRIBUTOR_PACKAGE="Package" +COM_EXTENSIONDISTRIBUTOR_PACKAGES="Packages" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_ACCESS="Packages Access" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_ACCESS_DESC="Allows the users in this group to access access packages" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_BATCH_OPTIONS="Batch process the selected Packages" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_BATCH_TIP="All changes will be applied to all selected Packages" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_BATCH_USE="Packages Batch Use" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch packages" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_DASHBOARD_LIST="Packages Dashboard List" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of package" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_DOWNLOAD_UPDATE="Download & Update" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_EDIT_VERSION="Packages Edit Version" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_EDIT_VERSION_DESC="Allows users in this group to edit versions of version packages" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_EXTENSIONS="Extensions" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_NAME="Name" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_ARCHIVED="%s Packages archived." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_ARCHIVED_1="%s Package archived." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_CHECKED_IN_0="No Package successfully checked in." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_CHECKED_IN_1="%d Package successfully checked in." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_CHECKED_IN_MORE="%d Packages successfully checked in." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_DELETED="%s Packages deleted." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_DELETED_1="%s Package deleted." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_FAILED_PUBLISHING="%s Packages failed publishing." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_FAILED_PUBLISHING_1="%s Package failed publishing." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_FEATURED="%s Packages featured." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_FEATURED_1="%s Package featured." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_PUBLISHED="%s Packages published." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_PUBLISHED_1="%s Package published." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_TRASHED="%s Packages trashed." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_TRASHED_1="%s Package trashed." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_UNFEATURED="%s Packages unfeatured." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_UNFEATURED_1="%s Package unfeatured." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_UNPUBLISHED="%s Packages unpublished." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_N_ITEMS_UNPUBLISHED_1="%s Package unpublished." +COM_EXTENSIONDISTRIBUTOR_PACKAGES_SUBMENU="Packages Submenu" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_SUBMENU_DESC="Allows the users in this group to submenu of package" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_VERSION="Version" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ADD_PHP_METHOD_UNINSTALL_LABEL="Add PHP Method (uninstall)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ADD_PHP_POSTFLIGHT_INSTALL_LABEL="Add PHP Postflight (install)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ADD_PHP_POSTFLIGHT_UPDATE_LABEL="Add PHP Postflight (update)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ADD_PHP_PREFLIGHT_INSTALL_LABEL="Add PHP Preflight (install)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ADD_PHP_PREFLIGHT_UNINSTALL_LABEL="Add PHP Preflight (uninstall)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ADD_PHP_PREFLIGHT_UPDATE_LABEL="Add PHP Preflight (update)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ALIAS_LABEL="Alias" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_AUTHOREMAIL_HINT="your@email.com" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_AUTHOREMAIL_LABEL="Author Email" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_AUTHOREMAIL_MESSAGE="Error! Please add some text here." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_AUTHORURL_HINT="https://agerix.fr" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_AUTHORURL_LABEL="Author URL" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_AUTHOR_LABEL="Author" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_BLOCKCHILDUNINSTALL_LABEL="Block Child Uninstall" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CATEGORIES="Categories" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CATID_LABEL="Category" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CATID_OTHERNAME="Categories" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CATID_VIEW="extension" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CATID_VIEWS="extensions" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CONFIGURATION="Configuration" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_COPYRIGHT_LABEL="Copyright" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CREATED_BY_DESC="The user that created this Package." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CREATED_BY_LABEL="Created By" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CREATED_DATE_DESC="The date this Package was created." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_CREATED_DATE_LABEL="Created Date" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_DESCRIPTION_LABEL="Description" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_DETAILS="Details" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_EDIT="Editing the Package" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ERROR_UNIQUE_ALIAS="Another Package has the same alias." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_EXTENSION="Extension" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_EXTENSION_DESCRIPTION="Select the extensions you wish to include in your package" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_EXTENSION_LABEL="Extension" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_FILES="Files" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_FILES_LABEL="Extensions" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_FILE_LABEL="JCB package" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_FREE_LABEL="Free" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ID="Id" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_IMAGE_LABEL="Image" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_INFOURL_LABEL="Info URL" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_JED_LABEL="JED link" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_LICENSE_LABEL="License" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_MODIFIED_BY_DESC="The last user that modified this Package." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_MODIFIED_BY_LABEL="Modified By" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_MODIFIED_DATE_DESC="The date this Package was modified." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_MODIFIED_DATE_LABEL="Modified Date" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_NAME_LABEL="Name" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_NAME_MESSAGE="Error! Please add some text here." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_NEW="A New Package" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_NEWFILENAME_DESCRIPTION="You can define your own pattern in this field. The available placeholders are:
{packagename} : The package name
{major} : Version major
{minor} : Version major
{release} : Version release
{stability} : Stability level" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_NEWFILENAME_LABEL="Filename pattern" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_NO="No" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_NOTE_JCB_PACKAGE_DESCRIPTION="This extension has been created to extend JCB. You can store your package here." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_NOTE_JCB_PACKAGE_LABEL="Upload JCB package" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_ORDERING_LABEL="Ordering" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PACKAGENAME_LABEL="Package Name" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PACKAGERURL_HINT="https://agerix.fr" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PACKAGERURL_LABEL="Packager URL" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PACKAGER_LABEL="Packager" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PERMISSION="Permissions" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_METHOD_UNINSTALL_DESCRIPTION="PHP script that should run during uninstall." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_METHOD_UNINSTALL_LABEL="PHP Method (uninstall)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_MINIMUM_LABEL="PHP minimum" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_POSTFLIGHT_INSTALL_DESCRIPTION="PHP script that should run postflight during install." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_POSTFLIGHT_INSTALL_LABEL="PHP Postflight (install)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_POSTFLIGHT_UPDATE_DESCRIPTION="PHP script that should run postflight during update." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_POSTFLIGHT_UPDATE_LABEL="PHP Postflight (update)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_PREFLIGHT_INSTALL_DESCRIPTION="PHP script that should run preflight during install." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_PREFLIGHT_INSTALL_LABEL="PHP Preflight (install)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_PREFLIGHT_UNINSTALL_DESCRIPTION="PHP script that should run preflight during uninstall." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_PREFLIGHT_UNINSTALL_LABEL="PHP Preflight (uninstall)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_PREFLIGHT_UPDATE_DESCRIPTION="PHP script that should run preflight during update." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PHP_PREFLIGHT_UPDATE_LABEL="PHP Preflight (update)" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PUBLISHING="Publishing" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PUBLISH_DESCRIPTION="Choose if the extension is automatically published after the install" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_PUBLISH_LABEL="Auto publish" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_RELEASES="Releases" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_RELEASE_DATE="Release Date" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_SAVE_WARNING="Alias already existed so a number was added at the end. You can re-edit the Package to customise the alias." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_SCRIPT="Script" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_STATUS="Status" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_TARGETPLATFORM_DESCRIPTION="You can use a REGEX" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_TARGETPLATFORM_LABEL="Joomla version" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_UPDATE_HINT="automatically generated..." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_UPDATE_LABEL="Update XML" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_UPLOAD_JCB_DESCRIPTION="
+ + Drop your file on this area +
+ + ... or click here +
+
+ +
+
" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_USEUPDATE_LABEL="Use Update" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_VERSION_DESC="A count of the number of times this Package has been revised." +COM_EXTENSIONDISTRIBUTOR_PACKAGE_VERSION_LABEL="Version" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_VERSION_NUMBER="Version Number" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_YES="Yes" +COM_EXTENSIONDISTRIBUTOR_PUBLISHED="Published" +COM_EXTENSIONDISTRIBUTOR_RELEASE="Release" +COM_EXTENSIONDISTRIBUTOR_RELEASES="Releases" +COM_EXTENSIONDISTRIBUTOR_RELEASES_="" +COM_EXTENSIONDISTRIBUTOR_RELEASES_BATCH_OPTIONS="Batch process the selected Releases" +COM_EXTENSIONDISTRIBUTOR_RELEASES_BATCH_TIP="All changes will be applied to all selected Releases" +COM_EXTENSIONDISTRIBUTOR_RELEASES_BATCH_USE="Releases Batch Use" +COM_EXTENSIONDISTRIBUTOR_RELEASES_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch releases" +COM_EXTENSIONDISTRIBUTOR_RELEASES_DOWNLOAD="Download" +COM_EXTENSIONDISTRIBUTOR_RELEASES_EDIT_VERSION="Releases Edit Version" +COM_EXTENSIONDISTRIBUTOR_RELEASES_EDIT_VERSION_DESC="Allows users in this group to edit versions of version releases" +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_ARCHIVED="%s Releases archived." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_ARCHIVED_1="%s Release archived." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_CHECKED_IN_0="No Release successfully checked in." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_CHECKED_IN_1="%d Release successfully checked in." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_CHECKED_IN_MORE="%d Releases successfully checked in." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_DELETED="%s Releases deleted." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_DELETED_1="%s Release deleted." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_FAILED_PUBLISHING="%s Releases failed publishing." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_FAILED_PUBLISHING_1="%s Release failed publishing." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_FEATURED="%s Releases featured." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_FEATURED_1="%s Release featured." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_PUBLISHED="%s Releases published." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_PUBLISHED_1="%s Release published." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_TRASHED="%s Releases trashed." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_TRASHED_1="%s Release trashed." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_UNFEATURED="%s Releases unfeatured." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_UNFEATURED_1="%s Release unfeatured." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_UNPUBLISHED="%s Releases unpublished." +COM_EXTENSIONDISTRIBUTOR_RELEASES_N_ITEMS_UNPUBLISHED_1="%s Release unpublished." +COM_EXTENSIONDISTRIBUTOR_RELEASE_ADD="Add" +COM_EXTENSIONDISTRIBUTOR_RELEASE_ALPHA="Alpha" +COM_EXTENSIONDISTRIBUTOR_RELEASE_BETA="Beta" +COM_EXTENSIONDISTRIBUTOR_RELEASE_BUG_FIX="Bug Fix" +COM_EXTENSIONDISTRIBUTOR_RELEASE_CHANGE="Change" +COM_EXTENSIONDISTRIBUTOR_RELEASE_CHANGELOG="Changelog" +COM_EXTENSIONDISTRIBUTOR_RELEASE_CHANGELOG_LABEL="Changelog" +COM_EXTENSIONDISTRIBUTOR_RELEASE_COMMENT_DESCRIPTION="Describe the change" +COM_EXTENSIONDISTRIBUTOR_RELEASE_COMMENT_LABEL="Comment" +COM_EXTENSIONDISTRIBUTOR_RELEASE_CREATED_BY_DESC="The user that created this Release." +COM_EXTENSIONDISTRIBUTOR_RELEASE_CREATED_BY_LABEL="Created By" +COM_EXTENSIONDISTRIBUTOR_RELEASE_CREATED_DATE_DESC="The date this Release was created." +COM_EXTENSIONDISTRIBUTOR_RELEASE_CREATED_DATE_LABEL="Created Date" +COM_EXTENSIONDISTRIBUTOR_RELEASE_DESCRIPTION_LABEL="Short description" +COM_EXTENSIONDISTRIBUTOR_RELEASE_DETAILS="Details" +COM_EXTENSIONDISTRIBUTOR_RELEASE_DEVELOPMENT="Development" +COM_EXTENSIONDISTRIBUTOR_RELEASE_EDIT="Editing the Release" +COM_EXTENSIONDISTRIBUTOR_RELEASE_ERROR_UNIQUE_ALIAS="Another Release has the same alias." +COM_EXTENSIONDISTRIBUTOR_RELEASE_EXTENSION_LABEL="Extension" +COM_EXTENSIONDISTRIBUTOR_RELEASE_FILE_LABEL="Release package" +COM_EXTENSIONDISTRIBUTOR_RELEASE_FORCEUPDATE_DESCRIPTION="Force to update the package content with the latest releases of each extension." +COM_EXTENSIONDISTRIBUTOR_RELEASE_FORCEUPDATE_LABEL="Force update" +COM_EXTENSIONDISTRIBUTOR_RELEASE_ID="Id" +COM_EXTENSIONDISTRIBUTOR_RELEASE_MAINTENANCE="Maintenance" +COM_EXTENSIONDISTRIBUTOR_RELEASE_MODIFIED_BY_DESC="The last user that modified this Release." +COM_EXTENSIONDISTRIBUTOR_RELEASE_MODIFIED_BY_LABEL="Modified By" +COM_EXTENSIONDISTRIBUTOR_RELEASE_MODIFIED_DATE_DESC="The date this Release was modified." +COM_EXTENSIONDISTRIBUTOR_RELEASE_MODIFIED_DATE_LABEL="Modified Date" +COM_EXTENSIONDISTRIBUTOR_RELEASE_NEW="A New Release" +COM_EXTENSIONDISTRIBUTOR_RELEASE_NEW_VERSION="New version" +COM_EXTENSIONDISTRIBUTOR_RELEASE_NO="No" +COM_EXTENSIONDISTRIBUTOR_RELEASE_ORDERING_LABEL="Ordering" +COM_EXTENSIONDISTRIBUTOR_RELEASE_PACKAGE_LABEL="Package" +COM_EXTENSIONDISTRIBUTOR_RELEASE_PERMISSION="Permissions" +COM_EXTENSIONDISTRIBUTOR_RELEASE_PLEASE_SELECT="Please select" +COM_EXTENSIONDISTRIBUTOR_RELEASE_PUBLISHING="Publishing" +COM_EXTENSIONDISTRIBUTOR_RELEASE_RC="RC" +COM_EXTENSIONDISTRIBUTOR_RELEASE_RELEASE_DATE_LABEL="Release date" +COM_EXTENSIONDISTRIBUTOR_RELEASE_RELTYPE_LABEL="Release type" +COM_EXTENSIONDISTRIBUTOR_RELEASE_REMOVE="Remove" +COM_EXTENSIONDISTRIBUTOR_RELEASE_SAVE_WARNING="Alias already existed so a number was added at the end. You can re-edit the Release to customise the alias." +COM_EXTENSIONDISTRIBUTOR_RELEASE_SECURITY="Security" +COM_EXTENSIONDISTRIBUTOR_RELEASE_STABILITY_LABEL="Stability level" +COM_EXTENSIONDISTRIBUTOR_RELEASE_STABLE="Stable" +COM_EXTENSIONDISTRIBUTOR_RELEASE_STATUS="Status" +COM_EXTENSIONDISTRIBUTOR_RELEASE_TOOLBAR="Toolbar" +COM_EXTENSIONDISTRIBUTOR_RELEASE_TYPE_DESCRIPTION="What kind of change is it ?" +COM_EXTENSIONDISTRIBUTOR_RELEASE_TYPE_LABEL="Type" +COM_EXTENSIONDISTRIBUTOR_RELEASE_UPLOAD_PACKAGE_DESCRIPTION="
+ + Drop your file on this area +
+ + ... or click here +
+
+ +
+
" +COM_EXTENSIONDISTRIBUTOR_RELEASE_VERSION_DESC="A count of the number of times this Release has been revised." +COM_EXTENSIONDISTRIBUTOR_RELEASE_VERSION_LABEL="Version" +COM_EXTENSIONDISTRIBUTOR_RELEASE_VERSION_NUMBER_HINT="1.0.0" +COM_EXTENSIONDISTRIBUTOR_RELEASE_VERSION_NUMBER_LABEL="Version" +COM_EXTENSIONDISTRIBUTOR_RELEASE_VERSION_NUMBER_MESSAGE="Error! Please provide a version" +COM_EXTENSIONDISTRIBUTOR_RELEASE_XMLPUBLISHED_LABEL="XML published" +COM_EXTENSIONDISTRIBUTOR_RELEASE_YES="Yes" +COM_EXTENSIONDISTRIBUTOR_RELEASE_ZIP_CONTENT="Zip Content" +COM_EXTENSIONDISTRIBUTOR_SAVE_SUCCESS="Great! Item successfully saved." +COM_EXTENSIONDISTRIBUTOR_SAVE_WARNING="The value already existed so please select another." +COM_EXTENSIONDISTRIBUTOR_SELECT_AN_OPTION="Select an option" +COM_EXTENSIONDISTRIBUTOR_STATISTICS="Statistics" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_ACCESS="Statistics Access" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_ACCESS_DESC="Allows the users in this group to access statistics." +COM_EXTENSIONDISTRIBUTOR_STATISTICS_DASHBOARD_LIST="Statistics Dashboard List" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of Statistics" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_DESC="Statistics" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_SUBMENU="Statistics Submenu" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_SUBMENU_DESC="Allows the users in this group to submenu of Statistics" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_DASHBOARD="Dashboard" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_DOWNLOADS="Downloads" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_EXTENSIONS="Extensions" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_IMPORT="Import" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_KEYS="Keys" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_MAINTENANCE="Maintenance" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_PACKAGES="Packages" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_PACKAGES_FIELDS="Packages Fields" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_PACKAGES_FIELDS_GROUPS="Packages Field Groups" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_STATISTICS="Statistics" +COM_EXTENSIONDISTRIBUTOR_SUBMENU_SUBSCRIPTIONS="Subscriptions" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION="Subscription" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS="Subscriptions" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_="" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_BATCH_OPTIONS="Batch process the selected Subscriptions" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_BATCH_TIP="All changes will be applied to all selected Subscriptions" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_BATCH_USE="Subscriptions Batch Use" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch subscriptions" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_DASHBOARD_LIST="Subscriptions Dashboard List" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of subscription" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_ARCHIVED="%s Subscriptions archived." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_ARCHIVED_1="%s Subscription archived." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_CHECKED_IN_0="No Subscription successfully checked in." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_CHECKED_IN_1="%d Subscription successfully checked in." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_CHECKED_IN_MORE="%d Subscriptions successfully checked in." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_DELETED="%s Subscriptions deleted." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_DELETED_1="%s Subscription deleted." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_FAILED_PUBLISHING="%s Subscriptions failed publishing." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_FAILED_PUBLISHING_1="%s Subscription failed publishing." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_FEATURED="%s Subscriptions featured." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_FEATURED_1="%s Subscription featured." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_PUBLISHED="%s Subscriptions published." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_PUBLISHED_1="%s Subscription published." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_TRASHED="%s Subscriptions trashed." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_TRASHED_1="%s Subscription trashed." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_UNFEATURED="%s Subscriptions unfeatured." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_UNFEATURED_1="%s Subscription unfeatured." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_UNPUBLISHED="%s Subscriptions unpublished." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_N_ITEMS_UNPUBLISHED_1="%s Subscription unpublished." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_SUBMENU="Subscriptions Submenu" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_SUBMENU_DESC="Allows the users in this group to submenu of subscription" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_SUBSCRIBER_PACKAGE="Subscriber / Package" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_ACTIVE_LABEL="Active" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_COMMENT_LABEL="Private comment" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_CREATED_BY_DESC="The user that created this Subscription." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_CREATED_BY_LABEL="Created By" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_CREATED_DATE_DESC="The date this Subscription was created." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_CREATED_DATE_LABEL="Created Date" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_DATE_EXPIRE_LABEL="Expire" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_DATE_START_LABEL="Starts" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_DETAILS="Details" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_EDIT="Editing the Subscription" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_ERROR_UNIQUE_ALIAS="Another Subscription has the same alias." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_ID="Id" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_MODIFIED_BY_DESC="The last user that modified this Subscription." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_MODIFIED_BY_LABEL="Modified By" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_MODIFIED_DATE_DESC="The date this Subscription was modified." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_MODIFIED_DATE_LABEL="Modified Date" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_NEW="A New Subscription" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_NO="No" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_ORDERING_LABEL="Ordering" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_PACKAGE_LABEL="Package" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_PUBLISHING="Publishing" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_SAVE_WARNING="Alias already existed so a number was added at the end. You can re-edit the Subscription to customise the alias." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_STATUS="Status" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_USER_LABEL="Joomla User" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_VERSION_DESC="A count of the number of times this Subscription has been revised." +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_VERSION_LABEL="Version" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_YES="Yes" +COM_EXTENSIONDISTRIBUTOR_TEMPORARY_PATH_ISNT_SET="Temporary path isn't set" +COM_EXTENSIONDISTRIBUTOR_THE_FILE_IS_TOO_LARGE="The file is too large" +COM_EXTENSIONDISTRIBUTOR_THE_PACKAGE_FILE_S_HAS_BEEN_SUCCESSFULLY_CREATED="The package file %s has been successfully created." +COM_EXTENSIONDISTRIBUTOR_THE_UPLOAD_ISNT_AVAILABLE="The upload isn't available" +COM_EXTENSIONDISTRIBUTOR_TRASHED="Trashed" +COM_EXTENSIONDISTRIBUTOR_TYPE_DOWNLOAD="Download" +COM_EXTENSIONDISTRIBUTOR_TYPE_EXTENSION="Extension" +COM_EXTENSIONDISTRIBUTOR_TYPE_KEY="Key" +COM_EXTENSIONDISTRIBUTOR_TYPE_PACKAGE="Package" +COM_EXTENSIONDISTRIBUTOR_TYPE_RELEASE="Release" +COM_EXTENSIONDISTRIBUTOR_TYPE_SUBSCRIPTION="Subscription" +COM_EXTENSIONDISTRIBUTOR_UPLOAD_FAILED="Upload failed" +COM_EXTENSIONDISTRIBUTOR_USE_BATCH="Use Batch" +COM_EXTENSIONDISTRIBUTOR_USE_BATCH_DESC="Allows users in this group to use batch copy/update method." +COM_EXTENSIONDISTRIBUTOR_VERSION="Version" +COM_EXTENSIONDISTRIBUTOR_WEBSITE="Website" +COM_EXTENSIONDISTRIBUTOR_YOU_DIDNT_SELECT_ANY_FILE="You didn't select any file" \ No newline at end of file diff --git a/admin/language/en-GB/en-GB.com_extensiondistributor.sys.ini b/admin/language/en-GB/en-GB.com_extensiondistributor.sys.ini new file mode 100644 index 0000000..da052e1 --- /dev/null +++ b/admin/language/en-GB/en-GB.com_extensiondistributor.sys.ini @@ -0,0 +1,86 @@ +COM_EXTENSIONDISTRIBUTOR="Extension Distributor" +COM_EXTENSIONDISTRIBUTOR_CONFIGURATION="Extension Distributor Configuration" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_BATCH_USE="Downloads Batch Use" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch downloads" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_DASHBOARD_LIST="Downloads Dashboard List" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of download" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_SUBMENU="Downloads Submenu" +COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_SUBMENU_DESC="Allows the users in this group to submenu of download" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_DELETE_BUTTON_ACCESS="Download Delete Button Access" +COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_DELETE_BUTTON_ACCESS_DESC="Allows the users in this group to access the delete button." +COM_EXTENSIONDISTRIBUTOR_EDIT_CREATED_BY="Edit Created By" +COM_EXTENSIONDISTRIBUTOR_EDIT_CREATED_BY_DESC="Allows users in this group to edit created by." +COM_EXTENSIONDISTRIBUTOR_EDIT_CREATED_DATE="Edit Created Date" +COM_EXTENSIONDISTRIBUTOR_EDIT_CREATED_DATE_DESC="Allows users in this group to edit created date." +COM_EXTENSIONDISTRIBUTOR_EDIT_VERSIONS="Edit Version" +COM_EXTENSIONDISTRIBUTOR_EDIT_VERSIONS_DESC="Allows users in this group to edit versions." +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_BATCH_USE="Extensions Batch Use" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch extensions" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_DASHBOARD_LIST="Extensions Dashboard List" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of extension" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_EDIT_VERSION="Extensions Edit Version" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_EDIT_VERSION_DESC="Allows users in this group to edit versions of version extensions" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_SUBMENU="Extensions Submenu" +COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_SUBMENU_DESC="Allows the users in this group to submenu of extension" +COM_EXTENSIONDISTRIBUTOR_IMPORT_ACCESS="Import Access" +COM_EXTENSIONDISTRIBUTOR_IMPORT_ACCESS_DESC="Allows the users in this group to access import." +COM_EXTENSIONDISTRIBUTOR_IMPORT_DASHBOARD_LIST="Import Dashboard List" +COM_EXTENSIONDISTRIBUTOR_IMPORT_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of Import" +COM_EXTENSIONDISTRIBUTOR_IMPORT_SUBMENU="Import Submenu" +COM_EXTENSIONDISTRIBUTOR_IMPORT_SUBMENU_DESC="Allows the users in this group to submenu of Import" +COM_EXTENSIONDISTRIBUTOR_KEYS_ACCESS_SITE="Keys (Site) Access" +COM_EXTENSIONDISTRIBUTOR_KEYS_ACCESS_SITE_DESC="Allows the users in this group to access site keys." +COM_EXTENSIONDISTRIBUTOR_KEYS_BATCH_USE="Keys Batch Use" +COM_EXTENSIONDISTRIBUTOR_KEYS_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch keys" +COM_EXTENSIONDISTRIBUTOR_KEYS_DASHBOARD_LIST="Keys Dashboard List" +COM_EXTENSIONDISTRIBUTOR_KEYS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of key" +COM_EXTENSIONDISTRIBUTOR_KEYS_EDIT_VERSION="Keys Edit Version" +COM_EXTENSIONDISTRIBUTOR_KEYS_EDIT_VERSION_DESC="Allows users in this group to edit versions of version keys" +COM_EXTENSIONDISTRIBUTOR_KEYS_SUBMENU="Keys Submenu" +COM_EXTENSIONDISTRIBUTOR_KEYS_SUBMENU_DESC="Allows the users in this group to submenu of key" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_ACCESS="Maintenance Access" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_ACCESS_DESC="Allows the users in this group to access maintenance." +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_DASHBOARD_LIST="Maintenance Dashboard List" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of Maintenance" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_SUBMENU="Maintenance Submenu" +COM_EXTENSIONDISTRIBUTOR_MAINTENANCE_SUBMENU_DESC="Allows the users in this group to submenu of Maintenance" +COM_EXTENSIONDISTRIBUTOR_MENU="d03; Extension Distributor" +COM_EXTENSIONDISTRIBUTOR_MENU_DOWNLOADS="Downloads" +COM_EXTENSIONDISTRIBUTOR_MENU_EXTENSIONS="Extensions" +COM_EXTENSIONDISTRIBUTOR_MENU_IMPORT="Import" +COM_EXTENSIONDISTRIBUTOR_MENU_KEYS="Keys" +COM_EXTENSIONDISTRIBUTOR_MENU_KEYS_DESC="User Keys" +COM_EXTENSIONDISTRIBUTOR_MENU_KEYS_OPTION="Keys" +COM_EXTENSIONDISTRIBUTOR_MENU_KEYS_TITLE="Keys" +COM_EXTENSIONDISTRIBUTOR_MENU_MAINTENANCE="Maintenance" +COM_EXTENSIONDISTRIBUTOR_MENU_PACKAGES="Packages" +COM_EXTENSIONDISTRIBUTOR_MENU_STATISTICS="Statistics" +COM_EXTENSIONDISTRIBUTOR_MENU_SUBSCRIPTIONS="Subscriptions" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_ACCESS="Packages Access" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_ACCESS_DESC="Allows the users in this group to access access packages" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_BATCH_USE="Packages Batch Use" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch packages" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_DASHBOARD_LIST="Packages Dashboard List" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of package" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_EDIT_VERSION="Packages Edit Version" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_EDIT_VERSION_DESC="Allows users in this group to edit versions of version packages" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_SUBMENU="Packages Submenu" +COM_EXTENSIONDISTRIBUTOR_PACKAGES_SUBMENU_DESC="Allows the users in this group to submenu of package" +COM_EXTENSIONDISTRIBUTOR_RELEASES_BATCH_USE="Releases Batch Use" +COM_EXTENSIONDISTRIBUTOR_RELEASES_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch releases" +COM_EXTENSIONDISTRIBUTOR_RELEASES_EDIT_VERSION="Releases Edit Version" +COM_EXTENSIONDISTRIBUTOR_RELEASES_EDIT_VERSION_DESC="Allows users in this group to edit versions of version releases" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_ACCESS="Statistics Access" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_ACCESS_DESC="Allows the users in this group to access statistics." +COM_EXTENSIONDISTRIBUTOR_STATISTICS_DASHBOARD_LIST="Statistics Dashboard List" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of Statistics" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_SUBMENU="Statistics Submenu" +COM_EXTENSIONDISTRIBUTOR_STATISTICS_SUBMENU_DESC="Allows the users in this group to submenu of Statistics" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_BATCH_USE="Subscriptions Batch Use" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_BATCH_USE_DESC="Allows users in this group to use batch copy/update method of batch subscriptions" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_DASHBOARD_LIST="Subscriptions Dashboard List" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_DASHBOARD_LIST_DESC="Allows the users in this group to dashboard list of subscription" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_SUBMENU="Subscriptions Submenu" +COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_SUBMENU_DESC="Allows the users in this group to submenu of subscription" +COM_EXTENSIONDISTRIBUTOR_USE_BATCH="Use Batch" +COM_EXTENSIONDISTRIBUTOR_USE_BATCH_DESC="Allows users in this group to use batch copy/update method." \ No newline at end of file diff --git a/admin/language/en-GB/index.html b/admin/language/en-GB/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/language/en-GB/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/language/index.html b/admin/language/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/language/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/layouts/batchselection.php b/admin/layouts/batchselection.php new file mode 100644 index 0000000..629497c --- /dev/null +++ b/admin/layouts/batchselection.php @@ -0,0 +1,57 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('JPATH_BASE') or die; + +JHtmlBehavior::core(); +$divWrapper = range(1,120,2); +$counter = 0; +?> +ListSelection) : ?> +
+ ListSelection as $ListSelection) : ?> +
+
+ + +
+
+ +
+
+ + + +
+
+
+ + +
+
+ \ No newline at end of file diff --git a/admin/layouts/download/details_left.php b/admin/layouts/download/details_left.php new file mode 100644 index 0000000..2b1ef72 --- /dev/null +++ b/admin/layouts/download/details_left.php @@ -0,0 +1,56 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'transaction', + 'package', + 'user', + 'ip', + 'domain', + 'key' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/download/details_right.php b/admin/layouts/download/details_right.php new file mode 100644 index 0000000..e2a0a52 --- /dev/null +++ b/admin/layouts/download/details_right.php @@ -0,0 +1,51 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'published' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/download/index.html b/admin/layouts/download/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/layouts/download/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/layouts/download/publishing.php b/admin/layouts/download/publishing.php new file mode 100644 index 0000000..a7d762f --- /dev/null +++ b/admin/layouts/download/publishing.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'created', + 'created_by', + 'modified', + 'modified_by' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/download/publlshing.php b/admin/layouts/download/publlshing.php new file mode 100644 index 0000000..c710814 --- /dev/null +++ b/admin/layouts/download/publlshing.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'ordering', + 'version', + 'hits', + 'id' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/extension/details_above.php b/admin/layouts/extension/details_above.php new file mode 100644 index 0000000..4b44dbc --- /dev/null +++ b/admin/layouts/extension/details_above.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'name', + 'alias' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + +
+ + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + +
+ diff --git a/admin/layouts/extension/details_left.php b/admin/layouts/extension/details_left.php new file mode 100644 index 0000000..b5345ec --- /dev/null +++ b/admin/layouts/extension/details_left.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'description', + 'image', + 'version_number', + 'release_date' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/extension/details_right.php b/admin/layouts/extension/details_right.php new file mode 100644 index 0000000..30995ae --- /dev/null +++ b/admin/layouts/extension/details_right.php @@ -0,0 +1,59 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'catid', + 'tags', + 'type', + 'element', + 'group', + 'client', + 'keepfilename', + 'newfilename', + 'update' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/extension/index.html b/admin/layouts/extension/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/layouts/extension/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/layouts/extension/publishing.php b/admin/layouts/extension/publishing.php new file mode 100644 index 0000000..bc54c18 --- /dev/null +++ b/admin/layouts/extension/publishing.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'title', + 'created', + 'created_by', + 'modified', + 'modified_by' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/extension/publlshing.php b/admin/layouts/extension/publlshing.php new file mode 100644 index 0000000..42f4653 --- /dev/null +++ b/admin/layouts/extension/publlshing.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'published', + 'ordering', + 'version', + 'hits', + 'id' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/extension/releases_fullwidth.php b/admin/layouts/extension/releases_fullwidth.php new file mode 100644 index 0000000..462c793 --- /dev/null +++ b/admin/layouts/extension/releases_fullwidth.php @@ -0,0 +1,185 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// set the defaults +$items = $displayData->vvwreleases; +$user = JFactory::getUser(); +$id = $displayData->item->id; +// set the edit URL +$edit = "index.php?option=com_extensiondistributor&view=releases&task=release.edit"; +// set a return value +$return = ($id) ? "index.php?option=com_extensiondistributor&view=extension&layout=edit&id=" . $id : ""; +// check for a return value +$jinput = JFactory::getApplication()->input; +if ($_return = $jinput->get('return', null, 'base64')) +{ + $return .= "&return=" . $_return; +} +// check if return value was set +if (ExtensiondistributorHelper::checkString($return)) +{ + // set the referral values + $ref = ($id) ? "&ref=extension&refid=" . $id . "&return=" . urlencode(base64_encode($return)) : "&return=" . urlencode(base64_encode($return)); +} +else +{ + $ref = ($id) ? "&ref=extension&refid=" . $id : ""; +} +// set the create new URL +$new = "index.php?option=com_extensiondistributor&view=releases&task=release.edit" . $ref; +// load the action object +$can = ExtensiondistributorHelper::getActions('release'); + +?> +
+get('core.create')): ?> +

+ + + + + + + + + + + + + + + + + $item): ?> + authorise('core.manage', 'com_checkin') || $item->checked_out == $user->id || $item->checked_out == 0; + $userChkOut = JFactory::getUser($item->checked_out); + $canDo = ExtensiondistributorHelper::getActions('release',$item,'releases'); + ?> + + + + + + + + published == 1): ?> + + published == 0): ?> + + published == 2): ?> + + published == -2): ?> + + + + + + +
+ + + + + + + + + + + + + + + +
+
+ +
+
+
+ get('core.edit')): ?> + escape($item->version_number); ?> + checked_out): ?> + name, $item->checked_out_time, 'releases.', $canCheckin); ?> + + + escape($item->version_number); ?> + +id,'id','stability'); +$stability = $lists['stability']; +?> + + + +
+
+
file == -1) : ?> +
N/A
+ +input->get('view')); ?> + + Download + + + +
+
+
release_date,'DATE_FORMAT_LC3'); ?> + +
+
+ reltype); ?> + +
description; ?> +
+
+ + + + + + + + + + + + + + + + + id; ?> +
+ +
+ +
+ +
diff --git a/admin/layouts/importfiles.php b/admin/layouts/importfiles.php new file mode 100644 index 0000000..423d7ac --- /dev/null +++ b/admin/layouts/importfiles.php @@ -0,0 +1,67 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('JPATH_BASE') or die('Restricted access'); + +$files = $displayData[0]; +$return = 'index.php?option=com_extensiondistributor&view=import'; +$redirect = base64_encode($return); +JHtml::_('bootstrap.tooltip', '.hasTooltip', ['placement' => 'left']); + + +?> + +

Files ready to import

+ + + + + + + + + +
+ +
+ + + + +
+ There aren't any files in the import folder +
+ + diff --git a/admin/layouts/index.html b/admin/layouts/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/layouts/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/layouts/key/details_left.php b/admin/layouts/key/details_left.php new file mode 100644 index 0000000..eefb0b8 --- /dev/null +++ b/admin/layouts/key/details_left.php @@ -0,0 +1,53 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'user', + 'website', + 'key' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/key/index.html b/admin/layouts/key/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/layouts/key/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/layouts/key/publishing.php b/admin/layouts/key/publishing.php new file mode 100644 index 0000000..bc54c18 --- /dev/null +++ b/admin/layouts/key/publishing.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'title', + 'created', + 'created_by', + 'modified', + 'modified_by' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/key/publlshing.php b/admin/layouts/key/publlshing.php new file mode 100644 index 0000000..42f4653 --- /dev/null +++ b/admin/layouts/key/publlshing.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'published', + 'ordering', + 'version', + 'hits', + 'id' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/package/configuration_left.php b/admin/layouts/package/configuration_left.php new file mode 100644 index 0000000..5582ae7 --- /dev/null +++ b/admin/layouts/package/configuration_left.php @@ -0,0 +1,56 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'packagename', + 'author', + 'authorurl', + 'authoremail', + 'targetplatform', + 'php_minimum' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/package/configuration_right.php b/admin/layouts/package/configuration_right.php new file mode 100644 index 0000000..dc437d0 --- /dev/null +++ b/admin/layouts/package/configuration_right.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'packager', + 'packagerurl', + 'license', + 'copyright', + 'infourl' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/package/details_above.php b/admin/layouts/package/details_above.php new file mode 100644 index 0000000..4b44dbc --- /dev/null +++ b/admin/layouts/package/details_above.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'name', + 'alias' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + +
+ + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + +
+ diff --git a/admin/layouts/package/details_left.php b/admin/layouts/package/details_left.php new file mode 100644 index 0000000..1ecf7af --- /dev/null +++ b/admin/layouts/package/details_left.php @@ -0,0 +1,51 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'description' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/package/details_right.php b/admin/layouts/package/details_right.php new file mode 100644 index 0000000..436d58d --- /dev/null +++ b/admin/layouts/package/details_right.php @@ -0,0 +1,63 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'catid', + 'image', + 'free', + 'blockchilduninstall', + 'useupdate', + 'update', + 'newfilename', + 'jed', + 'note_jcb_package', + 'upload_jcb', + 'file', + 'version_number', + 'release_date' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/package/files_left.php b/admin/layouts/package/files_left.php new file mode 100644 index 0000000..0925698 --- /dev/null +++ b/admin/layouts/package/files_left.php @@ -0,0 +1,51 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'files' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/package/index.html b/admin/layouts/package/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/layouts/package/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/layouts/package/metadata.php b/admin/layouts/package/metadata.php new file mode 100644 index 0000000..0ce4a02 --- /dev/null +++ b/admin/layouts/package/metadata.php @@ -0,0 +1,50 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +$form = $displayData->getForm(); + +// JLayout for standard handling of metadata fields in the administrator content edit screens. +$fieldSets = $form->getFieldsets('metadata'); +?> + + $fieldSet) : ?> + description) && trim($fieldSet->description)) : ?> +

escape(JText::_($fieldSet->description)); ?>

+ + + renderField('metadesc'); + echo $form->renderField('metakey'); + } + + foreach ($form->getFieldset($name) as $field) + { + if ($field->name != 'jform[metadata][tags][]') + { + echo $field->renderField(); + } + } ?> + diff --git a/admin/layouts/package/publishing.php b/admin/layouts/package/publishing.php new file mode 100644 index 0000000..ee45c64 --- /dev/null +++ b/admin/layouts/package/publishing.php @@ -0,0 +1,61 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'title', + 'created', + 'created_by', + 'modified', + 'modified_by', + 'published', + 'ordering', + 'access', + 'version', + 'hits', + 'id' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/package/releases_fullwidth.php b/admin/layouts/package/releases_fullwidth.php new file mode 100644 index 0000000..a0c6ba8 --- /dev/null +++ b/admin/layouts/package/releases_fullwidth.php @@ -0,0 +1,185 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// set the defaults +$items = $displayData->vvvreleases; +$user = JFactory::getUser(); +$id = $displayData->item->id; +// set the edit URL +$edit = "index.php?option=com_extensiondistributor&view=releases&task=release.edit"; +// set a return value +$return = ($id) ? "index.php?option=com_extensiondistributor&view=package&layout=edit&id=" . $id : ""; +// check for a return value +$jinput = JFactory::getApplication()->input; +if ($_return = $jinput->get('return', null, 'base64')) +{ + $return .= "&return=" . $_return; +} +// check if return value was set +if (ExtensiondistributorHelper::checkString($return)) +{ + // set the referral values + $ref = ($id) ? "&ref=package&refid=" . $id . "&return=" . urlencode(base64_encode($return)) : "&return=" . urlencode(base64_encode($return)); +} +else +{ + $ref = ($id) ? "&ref=package&refid=" . $id : ""; +} +// set the create new URL +$new = "index.php?option=com_extensiondistributor&view=releases&task=release.edit" . $ref; +// load the action object +$can = ExtensiondistributorHelper::getActions('release'); + +?> +
+get('core.create')): ?> +

+ + + + + + + + + + + + + + + + + $item): ?> + authorise('core.manage', 'com_checkin') || $item->checked_out == $user->id || $item->checked_out == 0; + $userChkOut = JFactory::getUser($item->checked_out); + $canDo = ExtensiondistributorHelper::getActions('release',$item,'releases'); + ?> + + + + + + + + published == 1): ?> + + published == 0): ?> + + published == 2): ?> + + published == -2): ?> + + + + + + +
+ + + + + + + + + + + + + + + +
+
+ +
+
+
+ get('core.edit')): ?> + escape($item->version_number); ?> + checked_out): ?> + name, $item->checked_out_time, 'releases.', $canCheckin); ?> + + + escape($item->version_number); ?> + +id,'id','stability'); +$stability = $lists['stability']; +?> + + + +
+
+
file == -1) : ?> +
N/A
+ +input->get('view')); ?> + + Download + + + +
+
+
release_date,'DATE_FORMAT_LC3'); ?> + +
+
+ reltype); ?> + +
description; ?> +
+
+ + + + + + + + + + + + + + + + + id; ?> +
+ +
+ +
+ +
diff --git a/admin/layouts/package/script_fullwidth.php b/admin/layouts/package/script_fullwidth.php new file mode 100644 index 0000000..268d5c4 --- /dev/null +++ b/admin/layouts/package/script_fullwidth.php @@ -0,0 +1,64 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'add_php_preflight_install', + 'php_preflight_install', + 'add_php_preflight_update', + 'php_preflight_update', + 'add_php_preflight_uninstall', + 'php_preflight_uninstall', + 'add_php_postflight_install', + 'php_postflight_install', + 'add_php_postflight_update', + 'php_postflight_update', + 'add_php_method_uninstall', + 'php_method_uninstall' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + +
+ + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + +
+ diff --git a/admin/layouts/release/changelog_fullwidth.php b/admin/layouts/release/changelog_fullwidth.php new file mode 100644 index 0000000..d0d51c8 --- /dev/null +++ b/admin/layouts/release/changelog_fullwidth.php @@ -0,0 +1,53 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'changelog' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + +
+ + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + +
+ diff --git a/admin/layouts/release/details_above.php b/admin/layouts/release/details_above.php new file mode 100644 index 0000000..669ab0d --- /dev/null +++ b/admin/layouts/release/details_above.php @@ -0,0 +1,57 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'extension', + 'package', + 'version_number', + 'release_date', + 'forceupdate' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + +
+ + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + +
+ diff --git a/admin/layouts/release/details_left.php b/admin/layouts/release/details_left.php new file mode 100644 index 0000000..f9f451e --- /dev/null +++ b/admin/layouts/release/details_left.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'description', + 'stability', + 'reltype', + 'xmlpublished' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/release/details_right.php b/admin/layouts/release/details_right.php new file mode 100644 index 0000000..9bca3c0 --- /dev/null +++ b/admin/layouts/release/details_right.php @@ -0,0 +1,53 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'upload_package', + 'file', + 'zip_content' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/release/details_under.php b/admin/layouts/release/details_under.php new file mode 100644 index 0000000..6556e1e --- /dev/null +++ b/admin/layouts/release/details_under.php @@ -0,0 +1,53 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'toolbar' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + +
+ + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + +
+ diff --git a/admin/layouts/release/index.html b/admin/layouts/release/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/layouts/release/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/layouts/release/publishing.php b/admin/layouts/release/publishing.php new file mode 100644 index 0000000..bc54c18 --- /dev/null +++ b/admin/layouts/release/publishing.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'title', + 'created', + 'created_by', + 'modified', + 'modified_by' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/release/publlshing.php b/admin/layouts/release/publlshing.php new file mode 100644 index 0000000..42f4653 --- /dev/null +++ b/admin/layouts/release/publlshing.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'published', + 'ordering', + 'version', + 'hits', + 'id' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/statewidget.php b/admin/layouts/statewidget.php new file mode 100644 index 0000000..5405906 --- /dev/null +++ b/admin/layouts/statewidget.php @@ -0,0 +1,84 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('JPATH_BASE') or die('Restricted access'); + +$release = $displayData[0]; +$view = strtolower(JFactory::getApplication()->input->get('view')); +$token = '&'.JSession::getFormToken().'=1'; + +// set the edit URL +$edit = "index.php?option=com_extensiondistributor&view=releases&task=release.edit"; +$publish = "index.php?option=com_extensiondistributor&view=releases&task=release.changestate&state=publish&id=".$release->id; +$unpublish = "index.php?option=com_extensiondistributor&view=releases&task=release.changestate&state=unpublish&id=".$release->id; +$trash = "index.php?option=com_extensiondistributor&view=releases&task=release.changestate&state=trash&id=".$release->id; +$delete = "index.php?option=com_extensiondistributor&view=releases&task=release.reldelete&id=".$release->id; +// set a return value +$return = ($view !== 'releases') ? "index.php?option=com_extensiondistributor&view=" . $view . "&layout=edit&id=" . $release->$view : ""; +// check if return value was set +if (ExtensiondistributorHelper::checkString($return)) +{ + // set the referral values + $ref = ($release->$view) ? "&ref=" . $view . "&refid=" . $release->$view . "&return=" . urlencode(base64_encode($return)) : "&return=" . urlencode(base64_encode($return)); +} +else +{ + $ref = ($view !== 'releases') ? "&ref=" . $view . "&refid=" . $release->$view : ""; +} + +$publish_states = array( + 0 => array( + 'tooltip' => 'Unpublished', + 'icon' => 'unpublish' + ), + 1 => array( + 'tooltip' => 'Published', + 'icon' => 'publish' + ), + -2 => array( + 'tooltip' => 'Trashed', + 'icon' => 'trash' + ) +); + + +?> + +  Edit + + + + + + + diff --git a/admin/layouts/subscription/details_left.php b/admin/layouts/subscription/details_left.php new file mode 100644 index 0000000..2195786 --- /dev/null +++ b/admin/layouts/subscription/details_left.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'user', + 'package', + 'date_start', + 'date_expire', + 'active' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/subscription/details_right.php b/admin/layouts/subscription/details_right.php new file mode 100644 index 0000000..1965ecb --- /dev/null +++ b/admin/layouts/subscription/details_right.php @@ -0,0 +1,51 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'comment' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/subscription/index.html b/admin/layouts/subscription/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/layouts/subscription/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/layouts/subscription/publishing.php b/admin/layouts/subscription/publishing.php new file mode 100644 index 0000000..a7d762f --- /dev/null +++ b/admin/layouts/subscription/publishing.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'created', + 'created_by', + 'modified', + 'modified_by' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/layouts/subscription/publlshing.php b/admin/layouts/subscription/publlshing.php new file mode 100644 index 0000000..42f4653 --- /dev/null +++ b/admin/layouts/subscription/publlshing.php @@ -0,0 +1,55 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// get the form +$form = $displayData->getForm(); + +// get the layout fields override method name (from layout path/ID) +$layout_path_array = explode('.', $this->getLayoutId()); +// Since we cannot pass the layout and tab names as parameters to the model method +// this name combination of tab and layout in the method name is the only work around +// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name. +// example of layout name: details_left.php +// example of method name: getFields_details_left() +$fields_tab_layout = 'fields_' . $layout_path_array[1]; + +// get the fields +$fields = $displayData->get($fields_tab_layout) ?: array( + 'published', + 'ordering', + 'version', + 'hits', + 'id' +); + +$hiddenFields = $displayData->get('hidden_fields') ?: array(); + +?> + + + + setFieldAttribute($field, 'type', 'hidden'); ?> + + renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?> + + diff --git a/admin/models/ajax.php b/admin/models/ajax.php new file mode 100644 index 0000000..3dabaae --- /dev/null +++ b/admin/models/ajax.php @@ -0,0 +1,114 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ListModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Ajax List Model + */ +class ExtensiondistributorModelAjax extends ListModel +{ + protected $app_params; + + public function __construct() + { + parent::__construct(); + // get params + $this->app_params = JComponentHelper::getParams('com_extensiondistributor'); + + } + + // Used in maintenance + public function showdir( + $dir, + $folderOnly = false, + $showRoot = false, + $level = 0, // do not use!!! + $ef = '' // do not use!!! + ) { + $html = ''; + if ((int) $level == 0) { + $dir = realpath($dir); + $ef = ($showRoot ? realpath($dir.'/..').'/' : $dir.'/'); + } + if (!file_exists($dir)) + return ''; + + if ($showRoot && (int) $level == 0) { + $html = '
    '; + $subdir = $this->showdir($dir, $folderOnly, $showRoot, $level + 1, $ef); + $name = substr(strrchr($dir, '/'), 1); + $html .= '
  • ' . ($subdir ? '' : '') . '' . $name . '' . $subdir . '
  • '; + $html .= '
'; + } else { + $list = scandir($dir); + if (is_array($list)) { + $list = array_diff($list, array('.', '..')); + if ($list) { + $folders = array(); + $files = array(); + + foreach ($list as $name) { + if (is_dir($dir . DIRECTORY_SEPARATOR . $name)) { + $folders[] = $name; + } else { + $files[] = $name; + } + } + + if (!($folderOnly && !$folders) || !(!$folders || !$files)) { + $html .= ''; + } + + sort($folders); + sort($files); + + foreach ($folders as $name) { + $fpath = $dir.'/'.$name; + $subdir = $this->showdir($fpath, $folderOnly, $showRoot, $level + 1, $ef); + $fpath = str_replace('\\', '/', str_replace($ef, '', $fpath)); + $html .= '
  • ' . ($subdir ? '' : '') . '' . $name . '' . $subdir . '
  • '; + } + + if (!$folderOnly) + foreach ($files as $name) { + $fpath = $dir.'/'.$name; + $fpath = str_replace('\\', '/', str_replace($ef, '', $fpath)); + $ext = substr(strrchr($name, '.'), 1); + $html .= '
  • ' . $name . '
  • '; + } + + if (!($folderOnly && !$folders) || !(!$folders || !$files)) { + $html .= ''; + } + + unset($folders, $files, $fpath, $ext); + } + } + } + + return $html; + } + +} diff --git a/admin/models/download.php b/admin/models/download.php new file mode 100644 index 0000000..7907117 --- /dev/null +++ b/admin/models/download.php @@ -0,0 +1,856 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\AdminModel; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Download Admin Model + */ +class ExtensiondistributorModelDownload extends AdminModel +{ + /** + * The tab layout fields array. + * + * @var array + */ + protected $tabLayoutFields = array( + 'details' => array( + 'left' => array( + 'transaction', + 'package', + 'user', + 'ip', + 'domain', + 'key' + ), + 'right' => array( + 'published' + ) + ) + ); + + /** + * @var string The prefix to use with controller messages. + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + /** + * The type alias for this content type. + * + * @var string + * @since 3.2 + */ + public $typeAlias = 'com_extensiondistributor.download'; + + /** + * Returns a Table object, always creating it + * + * @param type $type The table type to instantiate + * @param string $prefix A prefix for the table class name. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JTable A database object + * + * @since 1.6 + */ + public function getTable($type = 'download', $prefix = 'ExtensiondistributorTable', $config = array()) + { + // add table path for when model gets used from other component + $this->addTablePath(JPATH_ADMINISTRATOR . '/components/com_extensiondistributor/tables'); + // get instance of the table + return JTable::getInstance($type, $prefix, $config); + } + + /** + * Method to get a single record. + * + * @param integer $pk The id of the primary key. + * + * @return mixed Object on success, false on failure. + * + * @since 1.6 + */ + public function getItem($pk = null) + { + if ($item = parent::getItem($pk)) + { + if (!empty($item->params) && !is_array($item->params)) + { + // Convert the params field to an array. + $registry = new Registry; + $registry->loadString($item->params); + $item->params = $registry->toArray(); + } + + if (!empty($item->metadata)) + { + // Convert the metadata field to an array. + $registry = new Registry; + $registry->loadString($item->metadata); + $item->metadata = $registry->toArray(); + } + } + + return $item; + } + + /** + * Method to get the record form. + * + * @param array $data Data for the form. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * @param array $options Optional array of options for the form creation. + * + * @return mixed A JForm object on success, false on failure + * + * @since 1.6 + */ + public function getForm($data = array(), $loadData = true, $options = array('control' => 'jform')) + { + // set load data option + $options['load_data'] = $loadData; + // check if xpath was set in options + $xpath = false; + if (isset($options['xpath'])) + { + $xpath = $options['xpath']; + unset($options['xpath']); + } + // check if clear form was set in options + $clear = false; + if (isset($options['clear'])) + { + $clear = $options['clear']; + unset($options['clear']); + } + + // Get the form. + $form = $this->loadForm('com_extensiondistributor.download', 'download', $options, $clear, $xpath); + + if (empty($form)) + { + return false; + } + + $jinput = JFactory::getApplication()->input; + + // The front end calls this model and uses a_id to avoid id clashes so we need to check for that first. + if ($jinput->get('a_id')) + { + $id = $jinput->get('a_id', 0, 'INT'); + } + // The back end uses id so we use that the rest of the time and set it to 0 by default. + else + { + $id = $jinput->get('id', 0, 'INT'); + } + + $user = JFactory::getUser(); + + // Check for existing item. + // Modify the form based on Edit State access controls. + if ($id != 0 && (!$user->authorise('core.edit.state', 'com_extensiondistributor.download.' . (int) $id)) + || ($id == 0 && !$user->authorise('core.edit.state', 'com_extensiondistributor'))) + { + // Disable fields for display. + $form->setFieldAttribute('ordering', 'disabled', 'true'); + $form->setFieldAttribute('published', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('ordering', 'filter', 'unset'); + $form->setFieldAttribute('published', 'filter', 'unset'); + } + // If this is a new item insure the greated by is set. + if (0 == $id) + { + // Set the created_by to this user + $form->setValue('created_by', null, $user->id); + } + // Modify the form based on Edit Creaded By access controls. + if (!$user->authorise('core.edit.created_by', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created_by', 'disabled', 'true'); + // Disable fields for display. + $form->setFieldAttribute('created_by', 'readonly', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created_by', 'filter', 'unset'); + } + // Modify the form based on Edit Creaded Date access controls. + if (!$user->authorise('core.edit.created', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created', 'filter', 'unset'); + } + // Only load these values if no id is found + if (0 == $id) + { + // Set redirected view name + $redirectedView = $jinput->get('ref', null, 'STRING'); + // Set field name (or fall back to view name) + $redirectedField = $jinput->get('field', $redirectedView, 'STRING'); + // Set redirected view id + $redirectedId = $jinput->get('refid', 0, 'INT'); + // Set field id (or fall back to redirected view id) + $redirectedValue = $jinput->get('field_id', $redirectedId, 'INT'); + if (0 != $redirectedValue && $redirectedField) + { + // Now set the local-redirected field default value + $form->setValue($redirectedField, null, $redirectedValue); + } + } + return $form; + } + + /** + * Method to get the script that have to be included on the form + * + * @return string script files + */ + public function getScript() + { + return 'media/com_extensiondistributor/js/download.js'; + } + + /** + * Method to test whether a record can be deleted. + * + * @param object $record A record object. + * + * @return boolean True if allowed to delete the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canDelete($record) + { + if (!empty($record->id)) + { + if ($record->published != -2) + { + return; + } + + $user = JFactory::getUser(); + // The record has been set. Check the record permissions. + return $user->authorise('core.delete', 'com_extensiondistributor.download.' . (int) $record->id); + } + return false; + } + + /** + * Method to test whether a record can have its state edited. + * + * @param object $record A record object. + * + * @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canEditState($record) + { + $user = JFactory::getUser(); + $recordId = (!empty($record->id)) ? $record->id : 0; + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit.state', 'com_extensiondistributor.download.' . (int) $recordId); + if (!$permission && !is_null($permission)) + { + return false; + } + } + // In the absense of better information, revert to the component permissions. + return parent::canEditState($record); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * @since 2.5 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // Check specific edit permission then general edit permission. + + return JFactory::getUser()->authorise('core.edit', 'com_extensiondistributor.download.'. ((int) isset($data[$key]) ? $data[$key] : 0)) or parent::allowEdit($data, $key); + } + + /** + * Prepare and sanitise the table data prior to saving. + * + * @param JTable $table A JTable object. + * + * @return void + * + * @since 1.6 + */ + protected function prepareTable($table) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if (isset($table->name)) + { + $table->name = htmlspecialchars_decode($table->name, ENT_QUOTES); + } + + if (isset($table->alias) && empty($table->alias)) + { + $table->generateAlias(); + } + + if (empty($table->id)) + { + $table->created = $date->toSql(); + // set the user + if ($table->created_by == 0 || empty($table->created_by)) + { + $table->created_by = $user->id; + } + // Set ordering to the last item if not set + if (empty($table->ordering)) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select('MAX(ordering)') + ->from($db->quoteName('#__extensiondistributor_download')); + $db->setQuery($query); + $max = $db->loadResult(); + + $table->ordering = $max + 1; + } + } + else + { + $table->modified = $date->toSql(); + $table->modified_by = $user->id; + } + + if (!empty($table->id)) + { + // Increment the items version number. + $table->version++; + } + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 1.6 + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = JFactory::getApplication()->getUserState('com_extensiondistributor.edit.download.data', array()); + + if (empty($data)) + { + $data = $this->getItem(); + // run the perprocess of the data + $this->preprocessData('com_extensiondistributor.download', $data); + } + + return $data; + } + + /** + * Method to get the unique fields of this table. + * + * @return mixed An array of field names, boolean false if none is set. + * + * @since 3.0 + */ + protected function getUniqueFields() + { + return false; + } + + /** + * Method to delete one or more records. + * + * @param array &$pks An array of record primary keys. + * + * @return boolean True if successful, false if an error occurs. + * + * @since 12.2 + */ + public function delete(&$pks) + { + if (!parent::delete($pks)) + { + return false; + } + + return true; + } + + /** + * Method to change the published state of one or more records. + * + * @param array &$pks A list of the primary keys to change. + * @param integer $value The value of the published state. + * + * @return boolean True on success. + * + * @since 12.2 + */ + public function publish(&$pks, $value = 1) + { + if (!parent::publish($pks, $value)) + { + return false; + } + + return true; + } + + /** + * Method to perform batch operations on an item or a set of items. + * + * @param array $commands An array of commands to perform. + * @param array $pks An array of item ids. + * @param array $contexts An array of item contexts. + * + * @return boolean Returns true on success, false on failure. + * + * @since 12.2 + */ + public function batch($commands, $pks, $contexts) + { + // Sanitize ids. + $pks = array_unique($pks); + ArrayHelper::toInteger($pks); + + // Remove any values of zero. + if (array_search(0, $pks, true)) + { + unset($pks[array_search(0, $pks, true)]); + } + + if (empty($pks)) + { + $this->setError(JText::_('JGLOBAL_NO_ITEM_SELECTED')); + return false; + } + + $done = false; + + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->contentType = new JUcmType; + $this->type = $this->contentType->getTypeByTable($this->tableClassName); + $this->canDo = ExtensiondistributorHelper::getActions('download'); + $this->batchSet = true; + + if (!$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + return false; + } + + if ($this->type == false) + { + $type = new JUcmType; + $this->type = $type->getTypeByAlias($this->typeAlias); + } + + $this->tagsObserver = $this->table->getObserverOfClass('JTableObserverTags'); + + if (!empty($commands['move_copy'])) + { + $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c'); + + if ($cmd == 'c') + { + $result = $this->batchCopy($commands, $pks, $contexts); + + if (is_array($result)) + { + foreach ($result as $old => $new) + { + $contexts[$new] = $contexts[$old]; + } + $pks = array_values($result); + } + else + { + return false; + } + } + elseif ($cmd == 'm' && !$this->batchMove($commands, $pks, $contexts)) + { + return false; + } + + $done = true; + } + + if (!$done) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + + return false; + } + + // Clear the cache + $this->cleanCache(); + + return true; + } + + /** + * Batch copy items to a new category or current. + * + * @param integer $values The new values. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return mixed An array of new IDs on success, boolean false on failure. + * + * @since 12.2 + */ + protected function batchCopy($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('download'); + } + + if (!$this->canDo->get('core.create') || !$this->canDo->get('core.batch')) + { + return false; + } + + // get list of unique fields + $uniqueFields = $this->getUniqueFields(); + // remove move_copy from array + unset($values['move_copy']); + + // make sure published is set + if (!isset($values['published'])) + { + $values['published'] = 0; + } + elseif (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + $values['published'] = 0; + } + + $newIds = array(); + // Parent exists so let's proceed + while (!empty($pks)) + { + // Pop the first ID off the stack + $pk = array_shift($pks); + + $this->table->reset(); + + // only allow copy if user may edit this item. + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // insert all set values + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + if (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + // update all unique fields + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $this->table->$uniqueField = $this->generateUnique($uniqueField,$this->table->$uniqueField); + } + } + + // Reset the ID because we are making a copy + $this->table->id = 0; + + // TODO: Deal with ordering? + // $this->table->ordering = 1; + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + + // Get the new item ID + $newId = $this->table->get('id'); + + // Add the new ID to the array + $newIds[$pk] = $newId; + } + + // Clean the cache + $this->cleanCache(); + + return $newIds; + } + + /** + * Batch move items to a new category + * + * @param integer $value The new category ID. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 12.2 + */ + protected function batchMove($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('download'); + } + + if (!$this->canDo->get('core.edit') && !$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // make sure published only updates if user has the permission. + if (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + unset($values['published']); + } + // remove move_copy from array + unset($values['move_copy']); + + // Parent exists so we proceed + foreach ($pks as $pk) + { + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // insert all set values. + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + // Do special action for access. + if ('access' === $key && strlen($value) > 0) + { + $this->table->$key = $value; + } + elseif (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + } + + // Clean the cache + $this->cleanCache(); + + return true; + } + + /** + * Method to save the form data. + * + * @param array $data The form data. + * + * @return boolean True on success. + * + * @since 1.6 + */ + public function save($data) + { + $input = JFactory::getApplication()->input; + $filter = JFilterInput::getInstance(); + + // set the metadata to the Item Data + if (isset($data['metadata']) && isset($data['metadata']['author'])) + { + $data['metadata']['author'] = $filter->clean($data['metadata']['author'], 'TRIM'); + + $metadata = new JRegistry; + $metadata->loadArray($data['metadata']); + $data['metadata'] = (string) $metadata; + } + + // Set the Params Items to data + if (isset($data['params']) && is_array($data['params'])) + { + $params = new JRegistry; + $params->loadArray($data['params']); + $data['params'] = (string) $params; + } + + // Alter the unique field for save as copy + if ($input->get('task') === 'save2copy') + { + // Automatic handling of other unique fields + $uniqueFields = $this->getUniqueFields(); + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $data[$uniqueField] = $this->generateUnique($uniqueField,$data[$uniqueField]); + } + } + } + + if (parent::save($data)) + { + return true; + } + return false; + } + + /** + * Method to generate a unique value. + * + * @param string $field name. + * @param string $value data. + * + * @return string New value. + * + * @since 3.0 + */ + protected function generateUnique($field,$value) + { + + // set field value unique + $table = $this->getTable(); + + while ($table->load(array($field => $value))) + { + $value = StringHelper::increment($value); + } + + return $value; + } +} diff --git a/admin/models/downloads.php b/admin/models/downloads.php new file mode 100644 index 0000000..103d721 --- /dev/null +++ b/admin/models/downloads.php @@ -0,0 +1,484 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ListModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Downloads List Model + */ +class ExtensiondistributorModelDownloads extends ListModel +{ + public function __construct($config = array()) + { + if (empty($config['filter_fields'])) + { + $config['filter_fields'] = array( + 'a.id','id', + 'a.published','published', + 'a.ordering','ordering', + 'a.created_by','created_by', + 'a.modified_by','modified_by', + 'a.transaction','transaction', + 'g.name','package', + 'a.user','user', + 'a.ip','ip', + 'a.domain','domain', + 'a.created','created' + ); + } + + parent::__construct($config); + } + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @param string $ordering An optional ordering field. + * @param string $direction An optional direction (asc|desc). + * + * @return void + * + */ + protected function populateState($ordering = null, $direction = null) + { + $app = JFactory::getApplication(); + + // Adjust the context to support modal layouts. + if ($layout = $app->input->get('layout')) + { + $this->context .= '.' . $layout; + } + + // Check if the form was submitted + $formSubmited = $app->input->post->get('form_submited'); + + $access = $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', 0, 'int'); + if ($formSubmited) + { + $access = $app->input->post->get('access'); + $this->setState('filter.access', $access); + } + + $created_by = $this->getUserStateFromRequest($this->context . '.filter.created_by', 'filter_created_by', ''); + $this->setState('filter.created_by', $created_by); + + $sorting = $this->getUserStateFromRequest($this->context . '.filter.sorting', 'filter_sorting', 0, 'int'); + $this->setState('filter.sorting', $sorting); + + $search = $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search'); + $this->setState('filter.search', $search); + + $transaction = $this->getUserStateFromRequest($this->context . '.filter.transaction', 'filter_transaction'); + if ($formSubmited) + { + $transaction = $app->input->post->get('transaction'); + $this->setState('filter.transaction', $transaction); + } + + $package = $this->getUserStateFromRequest($this->context . '.filter.package', 'filter_package'); + if ($formSubmited) + { + $package = $app->input->post->get('package'); + $this->setState('filter.package', $package); + } + + $user = $this->getUserStateFromRequest($this->context . '.filter.user', 'filter_user'); + if ($formSubmited) + { + $user = $app->input->post->get('user'); + $this->setState('filter.user', $user); + } + + $ip = $this->getUserStateFromRequest($this->context . '.filter.ip', 'filter_ip'); + if ($formSubmited) + { + $ip = $app->input->post->get('ip'); + $this->setState('filter.ip', $ip); + } + + $domain = $this->getUserStateFromRequest($this->context . '.filter.domain', 'filter_domain'); + if ($formSubmited) + { + $domain = $app->input->post->get('domain'); + $this->setState('filter.domain', $domain); + } + + $created = $this->getUserStateFromRequest($this->context . '.filter.created', 'filter_created'); + if ($formSubmited) + { + $created = $app->input->post->get('created'); + $this->setState('filter.created', $created); + } + + // List state information. + parent::populateState($ordering, $direction); + } + + /** + * Method to get an array of data items. + * + * @return mixed An array of data items on success, false on failure. + */ + public function getItems() + { + // Check in items + $this->checkInNow(); + + // load parent items + $items = parent::getItems(); + + // set selection value to a translatable value + if (ExtensiondistributorHelper::checkArray($items)) + { + foreach ($items as $nr => &$item) + { + // convert transaction + $item->transaction = $this->selectionTranslation($item->transaction, 'transaction'); + } + } + + + // return items + return $items; + } + + /** + * Method to convert selection values to translatable string. + * + * @return translatable string + */ + public function selectionTranslation($value,$name) + { + // Array of transaction language strings + if ($name === 'transaction') + { + $transactionArray = array( + 'Web' => 'COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_WEBSITE', + 'Joomla' => 'COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_JOOMLA_UPDATER', + 'Watchful' => 'COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_WATCHFUL', + 'Other' => 'COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_OTHER' + ); + // Now check if value is found in this array + if (isset($transactionArray[$value]) && ExtensiondistributorHelper::checkString($transactionArray[$value])) + { + return $transactionArray[$value]; + } + } + return $value; + } + + /** + * Method to build an SQL query to load the list data. + * + * @return string An SQL query + */ + protected function getListQuery() + { + // Get the user object. + $user = JFactory::getUser(); + // Create a new query object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + + // Select some fields + $query->select('a.*'); + + // From the extensiondistributor_item table + $query->from($db->quoteName('#__extensiondistributor_download', 'a')); + + // From the extensiondistributor_package table. + $query->select($db->quoteName('g.name','package_name')); + $query->join('LEFT', $db->quoteName('#__extensiondistributor_package', 'g') . ' ON (' . $db->quoteName('a.package') . ' = ' . $db->quoteName('g.id') . ')'); + + // Filter by published state + $published = $this->getState('filter.published'); + if (is_numeric($published)) + { + $query->where('a.published = ' . (int) $published); + } + elseif ($published === '') + { + $query->where('(a.published = 0 OR a.published = 1)'); + } + // Filter by search. + $search = $this->getState('filter.search'); + if (!empty($search)) + { + if (stripos($search, 'id:') === 0) + { + $query->where('a.id = ' . (int) substr($search, 3)); + } + else + { + $search = $db->quote('%' . $db->escape($search) . '%'); + $query->where('(a.transaction LIKE '.$search.' OR a.package LIKE '.$search.' OR g.name LIKE '.$search.' OR a.user LIKE '.$search.' OR a.ip LIKE '.$search.' OR a.domain LIKE '.$search.' OR a.created LIKE '.$search.' OR a.key LIKE '.$search.')'); + } + } + + // Filter by Transaction. + $_transaction = $this->getState('filter.transaction'); + if (is_numeric($_transaction)) + { + if (is_float($_transaction)) + { + $query->where('a.transaction = ' . (float) $_transaction); + } + else + { + $query->where('a.transaction = ' . (int) $_transaction); + } + } + elseif (ExtensiondistributorHelper::checkString($_transaction)) + { + $query->where('a.transaction = ' . $db->quote($db->escape($_transaction))); + } + elseif (ExtensiondistributorHelper::checkArray($_transaction)) + { + // Secure the array for the query + $_transaction = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_transaction); + // Filter by the Transaction Array. + $query->where('a.transaction IN (' . implode(',', $_transaction) . ')'); + } + // Filter by Package. + $_package = $this->getState('filter.package'); + if (is_numeric($_package)) + { + if (is_float($_package)) + { + $query->where('a.package = ' . (float) $_package); + } + else + { + $query->where('a.package = ' . (int) $_package); + } + } + elseif (ExtensiondistributorHelper::checkString($_package)) + { + $query->where('a.package = ' . $db->quote($db->escape($_package))); + } + elseif (ExtensiondistributorHelper::checkArray($_package)) + { + // Secure the array for the query + $_package = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_package); + // Filter by the Package Array. + $query->where('a.package IN (' . implode(',', $_package) . ')'); + } + // Filter by User. + $_user = $this->getState('filter.user'); + if (is_numeric($_user)) + { + if (is_float($_user)) + { + $query->where('a.user = ' . (float) $_user); + } + else + { + $query->where('a.user = ' . (int) $_user); + } + } + elseif (ExtensiondistributorHelper::checkString($_user)) + { + $query->where('a.user = ' . $db->quote($db->escape($_user))); + } + elseif (ExtensiondistributorHelper::checkArray($_user)) + { + // Secure the array for the query + $_user = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_user); + // Filter by the User Array. + $query->where('a.user IN (' . implode(',', $_user) . ')'); + } + + // Add the list ordering clause. + $orderCol = $this->state->get('list.ordering', 'a.id'); + $orderDirn = $this->state->get('list.direction', 'desc'); + if ($orderCol != '') + { + $query->order($db->escape($orderCol . ' ' . $orderDirn)); + } + + return $query; + } + + /** + * Method to get a store id based on model configuration state. + * + * @return string A store id. + * + */ + protected function getStoreId($id = '') + { + // Compile the store id. + $id .= ':' . $this->getState('filter.id'); + $id .= ':' . $this->getState('filter.search'); + $id .= ':' . $this->getState('filter.ordering'); + $id .= ':' . $this->getState('filter.created_by'); + $id .= ':' . $this->getState('filter.modified_by'); + // Check if the value is an array + $_transaction = $this->getState('filter.transaction'); + if (ExtensiondistributorHelper::checkArray($_transaction)) + { + $id .= ':' . implode(':', $_transaction); + } + // Check if this is only an number or string + elseif (is_numeric($_transaction) + || ExtensiondistributorHelper::checkString($_transaction)) + { + $id .= ':' . $_transaction; + } + // Check if the value is an array + $_package = $this->getState('filter.package'); + if (ExtensiondistributorHelper::checkArray($_package)) + { + $id .= ':' . implode(':', $_package); + } + // Check if this is only an number or string + elseif (is_numeric($_package) + || ExtensiondistributorHelper::checkString($_package)) + { + $id .= ':' . $_package; + } + // Check if the value is an array + $_user = $this->getState('filter.user'); + if (ExtensiondistributorHelper::checkArray($_user)) + { + $id .= ':' . implode(':', $_user); + } + // Check if this is only an number or string + elseif (is_numeric($_user) + || ExtensiondistributorHelper::checkString($_user)) + { + $id .= ':' . $_user; + } + $id .= ':' . $this->getState('filter.ip'); + $id .= ':' . $this->getState('filter.domain'); + $id .= ':' . $this->getState('filter.created'); + + return parent::getStoreId($id); + } + + /** + * Build an SQL query to checkin all items left checked out longer then a set time. + * + * @return a bool + * + */ + protected function checkInNow() + { + // Get set check in time + $time = JComponentHelper::getParams('com_extensiondistributor')->get('check_in'); + + if ($time) + { + + // Get a db connection. + $db = JFactory::getDbo(); + // Reset query. + $query = $db->getQuery(true); + $query->select('*'); + $query->from($db->quoteName('#__extensiondistributor_download')); + // Only select items that are checked out. + $query->where($db->quoteName('checked_out') . '!=0'); + $db->setQuery($query, 0, 1); + $db->execute(); + if ($db->getNumRows()) + { + // Get Yesterdays date. + $date = JFactory::getDate()->modify($time)->toSql(); + // Reset query. + $query = $db->getQuery(true); + + // Fields to update. + $fields = array( + $db->quoteName('checked_out_time') . '=\'0000-00-00 00:00:00\'', + $db->quoteName('checked_out') . '=0' + ); + + // Conditions for which records should be updated. + $conditions = array( + $db->quoteName('checked_out') . '!=0', + $db->quoteName('checked_out_time') . '<\''.$date.'\'' + ); + + // Check table. + $query->update($db->quoteName('#__extensiondistributor_download'))->set($fields)->where($conditions); + + $db->setQuery($query); + + $db->execute(); + } + } + + return false; + } +} diff --git a/admin/models/extension.php b/admin/models/extension.php new file mode 100644 index 0000000..e98c9ee --- /dev/null +++ b/admin/models/extension.php @@ -0,0 +1,1259 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\AdminModel; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Extension Admin Model + */ +class ExtensiondistributorModelExtension extends AdminModel +{ + /** + * The tab layout fields array. + * + * @var array + */ + protected $tabLayoutFields = array( + 'details' => array( + 'left' => array( + 'description', + 'image', + 'version_number', + 'release_date' + ), + 'right' => array( + 'catid', + 'tags', + 'type', + 'element', + 'group', + 'client', + 'keepfilename', + 'newfilename', + 'update' + ), + 'above' => array( + 'name', + 'alias' + ) + ) + ); + + /** + * @var string The prefix to use with controller messages. + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + /** + * The type alias for this content type. + * + * @var string + * @since 3.2 + */ + public $typeAlias = 'com_extensiondistributor.extension'; + + /** + * Returns a Table object, always creating it + * + * @param type $type The table type to instantiate + * @param string $prefix A prefix for the table class name. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JTable A database object + * + * @since 1.6 + */ + public function getTable($type = 'extension', $prefix = 'ExtensiondistributorTable', $config = array()) + { + // add table path for when model gets used from other component + $this->addTablePath(JPATH_ADMINISTRATOR . '/components/com_extensiondistributor/tables'); + // get instance of the table + return JTable::getInstance($type, $prefix, $config); + } + + public function uploadFile($userfile) + { + // Make sure that file uploads are enabled in php. + if (!(bool) ini_get('file_uploads')) + { + $report = JText::_("COM_EXTENSIONDISTRIBUTOR_THE_UPLOAD_ISNT_AVAILABLE"); + } + + // If there is no uploaded file, we have a problem... + if (!is_array($userfile)) + { + $report = JText::_("COM_EXTENSIONDISTRIBUTOR_YOU_DIDNT_SELECT_ANY_FILE"); + } + + // Is the PHP tmp directory missing? + if ($userfile['error'] && ($userfile['error'] == UPLOAD_ERR_NO_TMP_DIR)) + { + $report = JText::_("COM_EXTENSIONDISTRIBUTOR_TEMPORARY_PATH_ISNT_SET"); + } + + // Is the max upload size too small in php.ini? + if ($userfile['error'] && ($userfile['error'] == UPLOAD_ERR_INI_SIZE)) + { + $report = JText::_('COM_EXTENSIONDISTRIBUTOR_THE_FILE_IS_TOO_LARGE'); + } + + // Check if there was a different problem uploading the file. + if ($userfile['error'] || $userfile['size'] < 1) + { + $report = JText::_("COM_EXTENSIONDISTRIBUTOR_UPLOAD_FAILED"); + } + + // Get classes + jimport('joomla.filesystem.file'); + jimport('joomla.filesystem.folder'); + + // Build the appropriate paths. + $config = JFactory::getConfig(); + $tmp_dest = $config->get('tmp_path') . '/' . $userfile['name']; + $tmp_src = $userfile['tmp_name']; + + // Move uploaded file. + JFile::upload($tmp_src, $tmp_dest, false, true); + + // Unpack the downloaded package file. + $package = $this->checkUpload($userfile['name'], $tmp_dest); + + return $package; + } + + protected function checkUpload($archivename, $tmp_dest) + { + // Get file format + $fileFormat = strtolower(pathinfo($archivename, PATHINFO_EXTENSION)); + // Get fileFomat Key + $allowedFormats = array('zip'); + // Check the extension + if (!in_array($fileFormat, $allowedFormats)) + { + // Cleanup the import files + $this->removeFile($tmp_dest); + $check['error'] = JText::_('COM_EXTENSIONDISTRIBUTOR_INVALID_FILENAME'); + } + + // Set package Name + $check['packagename'] = $archivename; + // Set directory + $check['dir'] = $tmp_dest; + // Set the format + $check['format'] = $fileFormat; + + return $check; + } + + protected function removeFile($package) + { + jimport('joomla.filesystem.file'); + + // Is the package a valid file + if (is_file($package)) + { + JFile::delete($package); + } + elseif (is_file(JPath::clean($package))) + { + // It might also be just a base filename + JFile::delete(JPath::clean($package)); + } + } + + + /** + * Method to get a single record. + * + * @param integer $pk The id of the primary key. + * + * @return mixed Object on success, false on failure. + * + * @since 1.6 + */ + public function getItem($pk = null) + { + if ($item = parent::getItem($pk)) + { + if (!empty($item->params) && !is_array($item->params)) + { + // Convert the params field to an array. + $registry = new Registry; + $registry->loadString($item->params); + $item->params = $registry->toArray(); + } + + if (!empty($item->metadata)) + { + // Convert the metadata field to an array. + $registry = new Registry; + $registry->loadString($item->metadata); + $item->metadata = $registry->toArray(); + } + + if (!empty($item->id)) + { + // Get Tag IDs. + $item->tags = new JHelperTags; + $item->tags->getTagIds($item->id, 'com_extensiondistributor.extension'); + } + + if ( + isset($item->alias) && + ExtensiondistributorHelper::checkString($item->alias) + ) + { + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $path = $cparams->get('updates_directory', 'updates'); + $url = JUri::root() . $path . '/' . $item->alias . '.xml'; + $item->update = $url; + } + + } + $this->extensionvvvw = $item->id; + + return $item; + } + + /** + * Method to get list data. + * + * @return mixed An array of data items on success, false on failure. + */ + public function getVvwreleases() + { + // Get the user object. + $user = JFactory::getUser(); + // Create a new query object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + + // Select some fields + $query->select('a.*'); + + // From the extensiondistributor_release table + $query->from($db->quoteName('#__extensiondistributor_release', 'a')); + + // From the extensiondistributor_extension table. + $query->select($db->quoteName('g.name','extension_name')); + $query->join('LEFT', $db->quoteName('#__extensiondistributor_extension', 'g') . ' ON (' . $db->quoteName('a.extension') . ' = ' . $db->quoteName('g.id') . ')'); + + // From the extensiondistributor_package table. + $query->select($db->quoteName('h.name','package_name')); + $query->join('LEFT', $db->quoteName('#__extensiondistributor_package', 'h') . ' ON (' . $db->quoteName('a.package') . ' = ' . $db->quoteName('h.id') . ')'); + + // Filter by extensionvvvw global. + $extensionvvvw = $this->extensionvvvw; + if (is_numeric($extensionvvvw )) + { + $query->where('a.extension = ' . (int) $extensionvvvw ); + } + elseif (is_string($extensionvvvw)) + { + $query->where('a.extension = ' . $db->quote($extensionvvvw)); + } + else + { + $query->where('a.extension = -5'); + } + + // Load the items + $db->setQuery($query); + $db->execute(); + if ($db->getNumRows()) + { + $items = $db->loadObjectList(); + + // Set values to display correctly. + if (ExtensiondistributorHelper::checkArray($items)) + { + // Get the user object if not set. + if (!isset($user) || !ExtensiondistributorHelper::checkObject($user)) + { + $user = JFactory::getUser(); + } + foreach ($items as $nr => &$item) + { + // Not needed + } + } + + // set selection value to a translatable value + if (ExtensiondistributorHelper::checkArray($items)) + { + foreach ($items as $nr => &$item) + { + // convert reltype + $item->reltype = $this->selectionTranslationVvwreleases($item->reltype, 'reltype'); + // convert stability + $item->stability = $this->selectionTranslationVvwreleases($item->stability, 'stability'); + } + } + + return $items; + } + return false; + } + + /** + * Method to convert selection values to translatable string. + * + * @return translatable string + */ + public function selectionTranslationVvwreleases($value,$name) + { + // Array of reltype language strings + if ($name === 'reltype') + { + $reltypeArray = array( + 0 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_PLEASE_SELECT', + 'security' => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_SECURITY', + 'maintenance' => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_MAINTENANCE', + 'newversion' => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_NEW_VERSION' + ); + // Now check if value is found in this array + if (isset($reltypeArray[$value]) && ExtensiondistributorHelper::checkString($reltypeArray[$value])) + { + return $reltypeArray[$value]; + } + } + // Array of stability language strings + if ($name === 'stability') + { + $stabilityArray = array( + 0 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_DEVELOPMENT', + 1 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_ALPHA', + 2 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_BETA', + 3 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_RC', + 4 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_STABLE' + ); + // Now check if value is found in this array + if (isset($stabilityArray[$value]) && ExtensiondistributorHelper::checkString($stabilityArray[$value])) + { + return $stabilityArray[$value]; + } + } + return $value; + } + + /** + * Method to get the record form. + * + * @param array $data Data for the form. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * @param array $options Optional array of options for the form creation. + * + * @return mixed A JForm object on success, false on failure + * + * @since 1.6 + */ + public function getForm($data = array(), $loadData = true, $options = array('control' => 'jform')) + { + // set load data option + $options['load_data'] = $loadData; + // check if xpath was set in options + $xpath = false; + if (isset($options['xpath'])) + { + $xpath = $options['xpath']; + unset($options['xpath']); + } + // check if clear form was set in options + $clear = false; + if (isset($options['clear'])) + { + $clear = $options['clear']; + unset($options['clear']); + } + + // Get the form. + $form = $this->loadForm('com_extensiondistributor.extension', 'extension', $options, $clear, $xpath); + + if (empty($form)) + { + return false; + } + + $jinput = JFactory::getApplication()->input; + + // The front end calls this model and uses a_id to avoid id clashes so we need to check for that first. + if ($jinput->get('a_id')) + { + $id = $jinput->get('a_id', 0, 'INT'); + } + // The back end uses id so we use that the rest of the time and set it to 0 by default. + else + { + $id = $jinput->get('id', 0, 'INT'); + } + + $user = JFactory::getUser(); + + // Check for existing item. + // Modify the form based on Edit State access controls. + if ($id != 0 && (!$user->authorise('core.edit.state', 'com_extensiondistributor.extension.' . (int) $id)) + || ($id == 0 && !$user->authorise('core.edit.state', 'com_extensiondistributor'))) + { + // Disable fields for display. + $form->setFieldAttribute('ordering', 'disabled', 'true'); + $form->setFieldAttribute('published', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('ordering', 'filter', 'unset'); + $form->setFieldAttribute('published', 'filter', 'unset'); + } + // If this is a new item insure the greated by is set. + if (0 == $id) + { + // Set the created_by to this user + $form->setValue('created_by', null, $user->id); + } + // Modify the form based on Edit Creaded By access controls. + if (!$user->authorise('core.edit.created_by', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created_by', 'disabled', 'true'); + // Disable fields for display. + $form->setFieldAttribute('created_by', 'readonly', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created_by', 'filter', 'unset'); + } + // Modify the form based on Edit Creaded Date access controls. + if (!$user->authorise('core.edit.created', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created', 'filter', 'unset'); + } + // Only load these values if no id is found + if (0 == $id) + { + // Set redirected view name + $redirectedView = $jinput->get('ref', null, 'STRING'); + // Set field name (or fall back to view name) + $redirectedField = $jinput->get('field', $redirectedView, 'STRING'); + // Set redirected view id + $redirectedId = $jinput->get('refid', 0, 'INT'); + // Set field id (or fall back to redirected view id) + $redirectedValue = $jinput->get('field_id', $redirectedId, 'INT'); + if (0 != $redirectedValue && $redirectedField) + { + // Now set the local-redirected field default value + $form->setValue($redirectedField, null, $redirectedValue); + } + } + $form->setFieldAttribute('update', 'type', 'hidden'); + + return $form; + } + + /** + * Method to get the script that have to be included on the form + * + * @return string script files + */ + public function getScript() + { + return 'media/com_extensiondistributor/js/extension.js'; + } + + /** + * Method to test whether a record can be deleted. + * + * @param object $record A record object. + * + * @return boolean True if allowed to delete the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canDelete($record) + { + if (!empty($record->id)) + { + if ($record->published != -2) + { + return; + } + + $user = JFactory::getUser(); + // The record has been set. Check the record permissions. + return $user->authorise('core.delete', 'com_extensiondistributor.extension.' . (int) $record->id); + } + return false; + } + + /** + * Method to test whether a record can have its state edited. + * + * @param object $record A record object. + * + * @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canEditState($record) + { + $user = JFactory::getUser(); + $recordId = (!empty($record->id)) ? $record->id : 0; + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit.state', 'com_extensiondistributor.extension.' . (int) $recordId); + if (!$permission && !is_null($permission)) + { + return false; + } + } + // In the absense of better information, revert to the component permissions. + return parent::canEditState($record); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * @since 2.5 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // Check specific edit permission then general edit permission. + + return JFactory::getUser()->authorise('core.edit', 'com_extensiondistributor.extension.'. ((int) isset($data[$key]) ? $data[$key] : 0)) or parent::allowEdit($data, $key); + } + + /** + * Prepare and sanitise the table data prior to saving. + * + * @param JTable $table A JTable object. + * + * @return void + * + * @since 1.6 + */ + protected function prepareTable($table) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if (isset($table->name)) + { + $table->name = htmlspecialchars_decode($table->name, ENT_QUOTES); + } + + if (isset($table->alias) && empty($table->alias)) + { + $table->generateAlias(); + } + + if (empty($table->id)) + { + $table->created = $date->toSql(); + // set the user + if ($table->created_by == 0 || empty($table->created_by)) + { + $table->created_by = $user->id; + } + // Set ordering to the last item if not set + if (empty($table->ordering)) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select('MAX(ordering)') + ->from($db->quoteName('#__extensiondistributor_extension')); + $db->setQuery($query); + $max = $db->loadResult(); + + $table->ordering = $max + 1; + } + } + else + { + $table->modified = $date->toSql(); + $table->modified_by = $user->id; + } + + if (!empty($table->id)) + { + // Increment the items version number. + $table->version++; + } + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 1.6 + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = JFactory::getApplication()->getUserState('com_extensiondistributor.edit.extension.data', array()); + + if (empty($data)) + { + $data = $this->getItem(); + // run the perprocess of the data + $this->preprocessData('com_extensiondistributor.extension', $data); + } + + return $data; + } + + /** + * Method to get the unique fields of this table. + * + * @return mixed An array of field names, boolean false if none is set. + * + * @since 3.0 + */ + protected function getUniqueFields() + { + return false; + } + + /** + * Method to delete one or more records. + * + * @param array &$pks An array of record primary keys. + * + * @return boolean True if successful, false if an error occurs. + * + * @since 12.2 + */ + public function delete(&$pks) + { + if (!parent::delete($pks)) + { + return false; + } + + // We delete the associated releases + $app = JFactory::getApplication(); + foreach ($pks as $pk) { + $releases = ExtensiondistributorHelper::getReleases($pk); + $releases = array_column($releases,'id'); + $model = ExtensiondistributorHelper::getModel('release'); + try + { + $model->publish($releases, -2); + $model->delete($releases); + } + catch (Exception $e) + { + $app->enqueueMessage($e->getMessage(),'error'); + } + } + + + return true; + } + + /** + * Method to change the published state of one or more records. + * + * @param array &$pks A list of the primary keys to change. + * @param integer $value The value of the published state. + * + * @return boolean True on success. + * + * @since 12.2 + */ + public function publish(&$pks, $value = 1) + { + if (!parent::publish($pks, $value)) + { + return false; + } + + return true; + } + + /** + * Method to perform batch operations on an item or a set of items. + * + * @param array $commands An array of commands to perform. + * @param array $pks An array of item ids. + * @param array $contexts An array of item contexts. + * + * @return boolean Returns true on success, false on failure. + * + * @since 12.2 + */ + public function batch($commands, $pks, $contexts) + { + // Sanitize ids. + $pks = array_unique($pks); + ArrayHelper::toInteger($pks); + + // Remove any values of zero. + if (array_search(0, $pks, true)) + { + unset($pks[array_search(0, $pks, true)]); + } + + if (empty($pks)) + { + $this->setError(JText::_('JGLOBAL_NO_ITEM_SELECTED')); + return false; + } + + $done = false; + + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->contentType = new JUcmType; + $this->type = $this->contentType->getTypeByTable($this->tableClassName); + $this->canDo = ExtensiondistributorHelper::getActions('extension'); + $this->batchSet = true; + + if (!$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + return false; + } + + if ($this->type == false) + { + $type = new JUcmType; + $this->type = $type->getTypeByAlias($this->typeAlias); + } + + $this->tagsObserver = $this->table->getObserverOfClass('JTableObserverTags'); + + if (!empty($commands['move_copy'])) + { + $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c'); + + if ($cmd == 'c') + { + $result = $this->batchCopy($commands, $pks, $contexts); + + if (is_array($result)) + { + foreach ($result as $old => $new) + { + $contexts[$new] = $contexts[$old]; + } + $pks = array_values($result); + } + else + { + return false; + } + } + elseif ($cmd == 'm' && !$this->batchMove($commands, $pks, $contexts)) + { + return false; + } + + $done = true; + } + + if (!$done) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + + return false; + } + + // Clear the cache + $this->cleanCache(); + + return true; + } + + /** + * Batch copy items to a new category or current. + * + * @param integer $values The new values. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return mixed An array of new IDs on success, boolean false on failure. + * + * @since 12.2 + */ + protected function batchCopy($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('extension'); + } + + if (!$this->canDo->get('core.create') || !$this->canDo->get('core.batch')) + { + return false; + } + + // get list of unique fields + $uniqueFields = $this->getUniqueFields(); + // remove move_copy from array + unset($values['move_copy']); + + // make sure published is set + if (!isset($values['published'])) + { + $values['published'] = 0; + } + elseif (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + $values['published'] = 0; + } + + if (isset($values['category']) && (int) $values['category'] > 0 && !static::checkCategoryId($values['category'])) + { + return false; + } + elseif (isset($values['category']) && (int) $values['category'] > 0) + { + // move the category value to correct field name + $values['catid'] = $values['category']; + unset($values['category']); + } + elseif (isset($values['category'])) + { + unset($values['category']); + } + + $newIds = array(); + // Parent exists so let's proceed + while (!empty($pks)) + { + // Pop the first ID off the stack + $pk = array_shift($pks); + + $this->table->reset(); + + // only allow copy if user may edit this item. + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + if (isset($values['catid'])) + { + list($this->table->name, $this->table->alias) = $this->generateNewTitle($values['catid'], $this->table->alias, $this->table->name); + } + else + { + list($this->table->name, $this->table->alias) = $this->generateNewTitle($this->table->catid, $this->table->alias, $this->table->name); + } + + // insert all set values + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + if (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + // update all unique fields + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $this->table->$uniqueField = $this->generateUnique($uniqueField,$this->table->$uniqueField); + } + } + + // Reset the ID because we are making a copy + $this->table->id = 0; + + // TODO: Deal with ordering? + // $this->table->ordering = 1; + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + + // Get the new item ID + $newId = $this->table->get('id'); + + // Add the new ID to the array + $newIds[$pk] = $newId; + } + + // Clean the cache + $this->cleanCache(); + + return $newIds; + } + + /** + * Batch move items to a new category + * + * @param integer $value The new category ID. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 12.2 + */ + protected function batchMove($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('extension'); + } + + if (!$this->canDo->get('core.edit') && !$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // make sure published only updates if user has the permission. + if (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + unset($values['published']); + } + // remove move_copy from array + unset($values['move_copy']); + + if (isset($values['category']) && (int) $values['category'] > 0 && !static::checkCategoryId($values['category'])) + { + return false; + } + elseif (isset($values['category']) && (int) $values['category'] > 0) + { + // move the category value to correct field name + $values['catid'] = $values['category']; + unset($values['category']); + } + elseif (isset($values['category'])) + { + unset($values['category']); + } + + + // Parent exists so we proceed + foreach ($pks as $pk) + { + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // insert all set values. + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + // Do special action for access. + if ('access' === $key && strlen($value) > 0) + { + $this->table->$key = $value; + } + elseif (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + } + + // Clean the cache + $this->cleanCache(); + + return true; + } + + /** + * Method to save the form data. + * + * @param array $data The form data. + * + * @return boolean True on success. + * + * @since 1.6 + */ + public function save($data) + { + $input = JFactory::getApplication()->input; + $filter = JFilterInput::getInstance(); + + // set the metadata to the Item Data + if (isset($data['metadata']) && isset($data['metadata']['author'])) + { + $data['metadata']['author'] = $filter->clean($data['metadata']['author'], 'TRIM'); + + $metadata = new JRegistry; + $metadata->loadArray($data['metadata']); + $data['metadata'] = (string) $metadata; + } + + // Set the Params Items to data + if (isset($data['params']) && is_array($data['params'])) + { + $params = new JRegistry; + $params->loadArray($data['params']); + $data['params'] = (string) $params; + } + + // Alter the name for save as copy + if ($input->get('task') === 'save2copy') + { + $origTable = clone $this->getTable(); + $origTable->load($input->getInt('id')); + + if ($data['name'] == $origTable->name) + { + list($name, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['name']); + $data['name'] = $name; + $data['alias'] = $alias; + } + else + { + if ($data['alias'] == $origTable->alias) + { + $data['alias'] = ''; + } + } + + $data['published'] = 0; + } + + // Automatic handling of alias for empty fields + if (in_array($input->get('task'), array('apply', 'save', 'save2new')) && (int) $input->get('id') == 0) + { + if ($data['alias'] == null || empty($data['alias'])) + { + if (JFactory::getConfig()->get('unicodeslugs') == 1) + { + $data['alias'] = JFilterOutput::stringURLUnicodeSlug($data['name']); + } + else + { + $data['alias'] = JFilterOutput::stringURLSafe($data['name']); + } + + $table = JTable::getInstance('extension', 'extensiondistributorTable'); + + if ($table->load(array('alias' => $data['alias'], 'catid' => $data['catid'])) && ($table->id != $data['id'] || $data['id'] == 0)) + { + $msg = JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_SAVE_WARNING'); + } + + list($name, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['name']); + $data['alias'] = $alias; + + if (isset($msg)) + { + JFactory::getApplication()->enqueueMessage($msg, 'warning'); + } + } + } + + // Alter the unique field for save as copy + if ($input->get('task') === 'save2copy') + { + // Automatic handling of other unique fields + $uniqueFields = $this->getUniqueFields(); + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $data[$uniqueField] = $this->generateUnique($uniqueField,$data[$uniqueField]); + } + } + } + + if (parent::save($data)) + { + return true; + } + return false; + } + + /** + * Method to generate a unique value. + * + * @param string $field name. + * @param string $value data. + * + * @return string New value. + * + * @since 3.0 + */ + protected function generateUnique($field,$value) + { + + // set field value unique + $table = $this->getTable(); + + while ($table->load(array($field => $value))) + { + $value = StringHelper::increment($value); + } + + return $value; + } + + /** + * Method to change the title/s & alias. + * + * @param string $alias The alias. + * @param string/array $title The title. + * + * @return array/string Contains the modified title/s and/or alias. + * + */ + protected function _generateNewTitle($alias, $title = null) + { + + // Alter the title/s & alias + $table = $this->getTable(); + + while ($table->load(array('alias' => $alias))) + { + // Check if this is an array of titles + if (ExtensiondistributorHelper::checkArray($title)) + { + foreach($title as $nr => &$_title) + { + $_title = StringHelper::increment($_title); + } + } + // Make sure we have a title + elseif ($title) + { + $title = StringHelper::increment($title); + } + $alias = StringHelper::increment($alias, 'dash'); + } + // Check if this is an array of titles + if (ExtensiondistributorHelper::checkArray($title)) + { + $title[] = $alias; + return $title; + } + // Make sure we have a title + elseif ($title) + { + return array($title, $alias); + } + // We only had an alias + return $alias; + } +} diff --git a/admin/models/extensiondistributor.php b/admin/models/extensiondistributor.php new file mode 100644 index 0000000..7497cd9 --- /dev/null +++ b/admin/models/extensiondistributor.php @@ -0,0 +1,247 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ListModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor List Model + */ +class ExtensiondistributorModelExtensiondistributor extends ListModel +{ + public function getIcons() + { + // load user for access menus + $user = JFactory::getUser(); + // reset icon array + $icons = array(); + // view groups array + $viewGroups = array( + 'main' => array('png.packages', 'png.extensions.catid_qpo0O0oqp_com_extensiondistributor', 'png.extensions', 'png.subscriptions', 'png.keys', 'png.import', 'png.maintenance', 'png.downloads', 'png.statistics') + ); + // view access array + $viewAccess = array( + 'import.submenu' => 'import.submenu', + 'import.dashboard_list' => 'import.dashboard_list', + 'maintenance.submenu' => 'maintenance.submenu', + 'maintenance.dashboard_list' => 'maintenance.dashboard_list', + 'statistics.submenu' => 'statistics.submenu', + 'statistics.dashboard_list' => 'statistics.dashboard_list', + 'packages.access' => 'package.access', + 'package.access' => 'package.access', + 'packages.submenu' => 'package.submenu', + 'packages.dashboard_list' => 'package.dashboard_list', + 'extensions.submenu' => 'extension.submenu', + 'extensions.dashboard_list' => 'extension.dashboard_list', + 'subscriptions.submenu' => 'subscription.submenu', + 'subscriptions.dashboard_list' => 'subscription.dashboard_list', + 'keys.submenu' => 'key.submenu', + 'keys.dashboard_list' => 'key.dashboard_list', + 'downloads.submenu' => 'download.submenu', + 'downloads.dashboard_list' => 'download.dashboard_list'); + // loop over the $views + foreach($viewGroups as $group => $views) + { + $i = 0; + if (ExtensiondistributorHelper::checkArray($views)) + { + foreach($views as $view) + { + $add = false; + // external views (links) + if (strpos($view,'||') !== false) + { + $dwd = explode('||', $view); + if (count($dwd) == 3) + { + list($type, $name, $url) = $dwd; + $viewName = $name; + $alt = $name; + $url = $url; + $image = $name . '.' . $type; + $name = 'COM_EXTENSIONDISTRIBUTOR_DASHBOARD_' . ExtensiondistributorHelper::safeString($name,'U'); + } + } + // internal views + elseif (strpos($view,'.') !== false) + { + $dwd = explode('.', $view); + if (count($dwd) == 3) + { + list($type, $name, $action) = $dwd; + } + elseif (count($dwd) == 2) + { + list($type, $name) = $dwd; + $action = false; + } + if ($action) + { + $viewName = $name; + switch($action) + { + case 'add': + $url = 'index.php?option=com_extensiondistributor&view=' . $name . '&layout=edit'; + $image = $name . '_' . $action. '.' . $type; + $alt = $name . ' ' . $action; + $name = 'COM_EXTENSIONDISTRIBUTOR_DASHBOARD_'.ExtensiondistributorHelper::safeString($name,'U').'_ADD'; + $add = true; + break; + default: + // check for new convention (more stable) + if (strpos($action, '_qpo0O0oqp_') !== false) + { + list($action, $extension) = (array) explode('_qpo0O0oqp_', $action); + $extension = str_replace('_po0O0oq_', '.', $extension); + } + else + { + $extension = 'com_extensiondistributor.' . $name; + } + $url = 'index.php?option=com_categories&view=categories&extension=' . $extension; + $image = $name . '_' . $action . '.' . $type; + $alt = $viewName . ' ' . $action; + $name = 'COM_EXTENSIONDISTRIBUTOR_DASHBOARD_' . ExtensiondistributorHelper::safeString($name,'U') . '_' . ExtensiondistributorHelper::safeString($action,'U'); + break; + } + } + else + { + $viewName = $name; + $alt = $name; + $url = 'index.php?option=com_extensiondistributor&view=' . $name; + $image = $name . '.' . $type; + $name = 'COM_EXTENSIONDISTRIBUTOR_DASHBOARD_' . ExtensiondistributorHelper::safeString($name,'U'); + $hover = false; + } + } + else + { + $viewName = $view; + $alt = $view; + $url = 'index.php?option=com_extensiondistributor&view=' . $view; + $image = $view . '.png'; + $name = ucwords($view).'

    '; + $hover = false; + } + // first make sure the view access is set + if (ExtensiondistributorHelper::checkArray($viewAccess)) + { + // setup some defaults + $dashboard_add = false; + $dashboard_list = false; + $accessTo = ''; + $accessAdd = ''; + // access checking start + $accessCreate = (isset($viewAccess[$viewName.'.create'])) ? ExtensiondistributorHelper::checkString($viewAccess[$viewName.'.create']):false; + $accessAccess = (isset($viewAccess[$viewName.'.access'])) ? ExtensiondistributorHelper::checkString($viewAccess[$viewName.'.access']):false; + // set main controllers + $accessDashboard_add = (isset($viewAccess[$viewName.'.dashboard_add'])) ? ExtensiondistributorHelper::checkString($viewAccess[$viewName.'.dashboard_add']):false; + $accessDashboard_list = (isset($viewAccess[$viewName.'.dashboard_list'])) ? ExtensiondistributorHelper::checkString($viewAccess[$viewName.'.dashboard_list']):false; + // check for adding access + if ($add && $accessCreate) + { + $accessAdd = $viewAccess[$viewName.'.create']; + } + elseif ($add) + { + $accessAdd = 'core.create'; + } + // check if access to view is set + if ($accessAccess) + { + $accessTo = $viewAccess[$viewName.'.access']; + } + // set main access controllers + if ($accessDashboard_add) + { + $dashboard_add = $user->authorise($viewAccess[$viewName.'.dashboard_add'], 'com_extensiondistributor'); + } + if ($accessDashboard_list) + { + $dashboard_list = $user->authorise($viewAccess[$viewName.'.dashboard_list'], 'com_extensiondistributor'); + } + if (ExtensiondistributorHelper::checkString($accessAdd) && ExtensiondistributorHelper::checkString($accessTo)) + { + // check access + if($user->authorise($accessAdd, 'com_extensiondistributor') && $user->authorise($accessTo, 'com_extensiondistributor') && $dashboard_add) + { + $icons[$group][$i] = new StdClass; + $icons[$group][$i]->url = $url; + $icons[$group][$i]->name = $name; + $icons[$group][$i]->image = $image; + $icons[$group][$i]->alt = $alt; + } + } + elseif (ExtensiondistributorHelper::checkString($accessTo)) + { + // check access + if($user->authorise($accessTo, 'com_extensiondistributor') && $dashboard_list) + { + $icons[$group][$i] = new StdClass; + $icons[$group][$i]->url = $url; + $icons[$group][$i]->name = $name; + $icons[$group][$i]->image = $image; + $icons[$group][$i]->alt = $alt; + } + } + elseif (ExtensiondistributorHelper::checkString($accessAdd)) + { + // check access + if($user->authorise($accessAdd, 'com_extensiondistributor') && $dashboard_add) + { + $icons[$group][$i] = new StdClass; + $icons[$group][$i]->url = $url; + $icons[$group][$i]->name = $name; + $icons[$group][$i]->image = $image; + $icons[$group][$i]->alt = $alt; + } + } + else + { + $icons[$group][$i] = new StdClass; + $icons[$group][$i]->url = $url; + $icons[$group][$i]->name = $name; + $icons[$group][$i]->image = $image; + $icons[$group][$i]->alt = $alt; + } + } + else + { + $icons[$group][$i] = new StdClass; + $icons[$group][$i]->url = $url; + $icons[$group][$i]->name = $name; + $icons[$group][$i]->image = $image; + $icons[$group][$i]->alt = $alt; + } + $i++; + } + } + else + { + $icons[$group][$i] = false; + } + } + return $icons; + } +} diff --git a/admin/models/extensions.php b/admin/models/extensions.php new file mode 100644 index 0000000..49023e7 --- /dev/null +++ b/admin/models/extensions.php @@ -0,0 +1,493 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ListModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensions List Model + */ +class ExtensiondistributorModelExtensions extends ListModel +{ + public function __construct($config = array()) + { + if (empty($config['filter_fields'])) + { + $config['filter_fields'] = array( + 'a.id','id', + 'a.published','published', + 'a.ordering','ordering', + 'a.created_by','created_by', + 'a.modified_by','modified_by', + 'a.type','type', + 'a.group','group', + 'a.client','client', + 'a.name','name', + 'a.version_number','version_number', + 'a.release_date','release_date' + ); + } + + parent::__construct($config); + } + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @param string $ordering An optional ordering field. + * @param string $direction An optional direction (asc|desc). + * + * @return void + * + */ + protected function populateState($ordering = null, $direction = null) + { + $app = JFactory::getApplication(); + + // Adjust the context to support modal layouts. + if ($layout = $app->input->get('layout')) + { + $this->context .= '.' . $layout; + } + + // Check if the form was submitted + $formSubmited = $app->input->post->get('form_submited'); + + $access = $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', 0, 'int'); + if ($formSubmited) + { + $access = $app->input->post->get('access'); + $this->setState('filter.access', $access); + } + + $published = $this->getUserStateFromRequest($this->context . '.filter.published', 'filter_published', ''); + $this->setState('filter.published', $published); + + $created_by = $this->getUserStateFromRequest($this->context . '.filter.created_by', 'filter_created_by', ''); + $this->setState('filter.created_by', $created_by); + + $created = $this->getUserStateFromRequest($this->context . '.filter.created', 'filter_created'); + $this->setState('filter.created', $created); + + $sorting = $this->getUserStateFromRequest($this->context . '.filter.sorting', 'filter_sorting', 0, 'int'); + $this->setState('filter.sorting', $sorting); + + $search = $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search'); + $this->setState('filter.search', $search); + + $type = $this->getUserStateFromRequest($this->context . '.filter.type', 'filter_type'); + if ($formSubmited) + { + $type = $app->input->post->get('type'); + $this->setState('filter.type', $type); + } + + $group = $this->getUserStateFromRequest($this->context . '.filter.group', 'filter_group'); + if ($formSubmited) + { + $group = $app->input->post->get('group'); + $this->setState('filter.group', $group); + } + + $client = $this->getUserStateFromRequest($this->context . '.filter.client', 'filter_client'); + if ($formSubmited) + { + $client = $app->input->post->get('client'); + $this->setState('filter.client', $client); + } + + $name = $this->getUserStateFromRequest($this->context . '.filter.name', 'filter_name'); + if ($formSubmited) + { + $name = $app->input->post->get('name'); + $this->setState('filter.name', $name); + } + + $version_number = $this->getUserStateFromRequest($this->context . '.filter.version_number', 'filter_version_number'); + if ($formSubmited) + { + $version_number = $app->input->post->get('version_number'); + $this->setState('filter.version_number', $version_number); + } + + $release_date = $this->getUserStateFromRequest($this->context . '.filter.release_date', 'filter_release_date'); + if ($formSubmited) + { + $release_date = $app->input->post->get('release_date'); + $this->setState('filter.release_date', $release_date); + } + + // List state information. + parent::populateState($ordering, $direction); + } + + /** + * Method to get an array of data items. + * + * @return mixed An array of data items on success, false on failure. + */ + public function getItems() + { + // Check in items + $this->checkInNow(); + + // load parent items + $items = parent::getItems(); + + // Set values to display correctly. + if (ExtensiondistributorHelper::checkArray($items)) + { + // Get the user object if not set. + if (!isset($user) || !ExtensiondistributorHelper::checkObject($user)) + { + $user = JFactory::getUser(); + } + foreach ($items as $nr => &$item) + { + // Add the tags + $item->tags = new JHelperTags; + $item->tags->getTagIds( + $item->id, 'com_extensiondistributor.extension' + ); + if ($item->tags->tags) + { + $item->tags = implode(', ', + $item->tags->getTagNames( + explode(',', $item->tags->tags) + ) + ); + } + else + { + $item->tags = ''; + } + } + } + + // set selection value to a translatable value + if (ExtensiondistributorHelper::checkArray($items)) + { + foreach ($items as $nr => &$item) + { + // convert type + $item->type = $this->selectionTranslation($item->type, 'type'); + // convert client + $item->client = $this->selectionTranslation($item->client, 'client'); + } + } + + + // return items + return $items; + } + + /** + * Method to convert selection values to translatable string. + * + * @return translatable string + */ + public function selectionTranslation($value,$name) + { + // Array of type language strings + if ($name === 'type') + { + $typeArray = array( + 0 => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_PLEASE_SELECT', + 'component' => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_COMPONENT', + 'module' => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_MODULE', + 'plugin' => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_PLUGIN', + 'library' => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_LIBRARY', + 'template' => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_TEMPLATE', + 'language' => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_LANGUAGE', + 'file' => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_FILE' + ); + // Now check if value is found in this array + if (isset($typeArray[$value]) && ExtensiondistributorHelper::checkString($typeArray[$value])) + { + return $typeArray[$value]; + } + } + // Array of client language strings + if ($name === 'client') + { + $clientArray = array( + 0 => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_PLEASE_SELECT', + 'site' => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_SITE', + 'administrator' => 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_ADMINISTRATOR' + ); + // Now check if value is found in this array + if (isset($clientArray[$value]) && ExtensiondistributorHelper::checkString($clientArray[$value])) + { + return $clientArray[$value]; + } + } + return $value; + } + + /** + * Method to build an SQL query to load the list data. + * + * @return string An SQL query + */ + protected function getListQuery() + { + // Get the user object. + $user = JFactory::getUser(); + // Create a new query object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + + // Select some fields + $query->select('a.*'); + $query->select($db->quoteName('c.title','category_title')); + + // From the extensiondistributor_item table + $query->from($db->quoteName('#__extensiondistributor_extension', 'a')); + $query->join('LEFT', $db->quoteName('#__categories', 'c') . ' ON (' . $db->quoteName('a.catid') . ' = ' . $db->quoteName('c.id') . ')'); + + // Filter by published state + $published = $this->getState('filter.published'); + if (is_numeric($published)) + { + $query->where('a.published = ' . (int) $published); + } + elseif ($published === '') + { + $query->where('(a.published = 0 OR a.published = 1)'); + } + // Filter by search. + $search = $this->getState('filter.search'); + if (!empty($search)) + { + if (stripos($search, 'id:') === 0) + { + $query->where('a.id = ' . (int) substr($search, 3)); + } + else + { + $search = $db->quote('%' . $db->escape($search) . '%'); + $query->where('(a.name LIKE '.$search.' OR a.group LIKE '.$search.' OR a.client LIKE '.$search.' OR a.alias LIKE '.$search.' OR a.catid LIKE '.$search.' OR a.description LIKE '.$search.')'); + } + } + + // Filter by Type. + $_type = $this->getState('filter.type'); + if (is_numeric($_type)) + { + if (is_float($_type)) + { + $query->where('a.type = ' . (float) $_type); + } + else + { + $query->where('a.type = ' . (int) $_type); + } + } + elseif (ExtensiondistributorHelper::checkString($_type)) + { + $query->where('a.type = ' . $db->quote($db->escape($_type))); + } + elseif (ExtensiondistributorHelper::checkArray($_type)) + { + // Secure the array for the query + $_type = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_type); + // Filter by the Type Array. + $query->where('a.type IN (' . implode(',', $_type) . ')'); + } + // Filter by Group. + $_group = $this->getState('filter.group'); + if (is_numeric($_group)) + { + if (is_float($_group)) + { + $query->where('a.group = ' . (float) $_group); + } + else + { + $query->where('a.group = ' . (int) $_group); + } + } + elseif (ExtensiondistributorHelper::checkString($_group)) + { + $query->where('a.group = ' . $db->quote($db->escape($_group))); + } + // Filter by Client. + $_client = $this->getState('filter.client'); + if (is_numeric($_client)) + { + if (is_float($_client)) + { + $query->where('a.client = ' . (float) $_client); + } + else + { + $query->where('a.client = ' . (int) $_client); + } + } + elseif (ExtensiondistributorHelper::checkString($_client)) + { + $query->where('a.client = ' . $db->quote($db->escape($_client))); + } + + // Filter by a single or group of categories. + $baselevel = 1; + $categoryId = $this->getState('filter.category_id'); + + if (is_numeric($categoryId)) + { + $cat_tbl = JTable::getInstance('Category', 'JTable'); + $cat_tbl->load($categoryId); + $rgt = $cat_tbl->rgt; + $lft = $cat_tbl->lft; + $baselevel = (int) $cat_tbl->level; + $query->where('c.lft >= ' . (int) $lft) + ->where('c.rgt <= ' . (int) $rgt); + } + elseif (is_array($categoryId)) + { + $categoryId = ArrayHelper::toInteger($categoryId); + $categoryId = implode(',', $categoryId); + $query->where('a.catid IN (' . $categoryId . ')'); + } + + + // Add the list ordering clause. + $orderCol = $this->state->get('list.ordering', 'a.id'); + $orderDirn = $this->state->get('list.direction', 'desc'); + if ($orderCol != '') + { + $query->order($db->escape($orderCol . ' ' . $orderDirn)); + } + + return $query; + } + + /** + * Method to get a store id based on model configuration state. + * + * @return string A store id. + * + */ + protected function getStoreId($id = '') + { + // Compile the store id. + $id .= ':' . $this->getState('filter.id'); + $id .= ':' . $this->getState('filter.search'); + $id .= ':' . $this->getState('filter.published'); + $id .= ':' . $this->getState('filter.ordering'); + $id .= ':' . $this->getState('filter.created_by'); + $id .= ':' . $this->getState('filter.modified_by'); + // Check if the value is an array + $_type = $this->getState('filter.type'); + if (ExtensiondistributorHelper::checkArray($_type)) + { + $id .= ':' . implode(':', $_type); + } + // Check if this is only an number or string + elseif (is_numeric($_type) + || ExtensiondistributorHelper::checkString($_type)) + { + $id .= ':' . $_type; + } + $id .= ':' . $this->getState('filter.group'); + $id .= ':' . $this->getState('filter.client'); + $id .= ':' . $this->getState('filter.name'); + $id .= ':' . $this->getState('filter.version_number'); + $id .= ':' . $this->getState('filter.release_date'); + + return parent::getStoreId($id); + } + + /** + * Build an SQL query to checkin all items left checked out longer then a set time. + * + * @return a bool + * + */ + protected function checkInNow() + { + // Get set check in time + $time = JComponentHelper::getParams('com_extensiondistributor')->get('check_in'); + + if ($time) + { + + // Get a db connection. + $db = JFactory::getDbo(); + // Reset query. + $query = $db->getQuery(true); + $query->select('*'); + $query->from($db->quoteName('#__extensiondistributor_extension')); + // Only select items that are checked out. + $query->where($db->quoteName('checked_out') . '!=0'); + $db->setQuery($query, 0, 1); + $db->execute(); + if ($db->getNumRows()) + { + // Get Yesterdays date. + $date = JFactory::getDate()->modify($time)->toSql(); + // Reset query. + $query = $db->getQuery(true); + + // Fields to update. + $fields = array( + $db->quoteName('checked_out_time') . '=\'0000-00-00 00:00:00\'', + $db->quoteName('checked_out') . '=0' + ); + + // Conditions for which records should be updated. + $conditions = array( + $db->quoteName('checked_out') . '!=0', + $db->quoteName('checked_out_time') . '<\''.$date.'\'' + ); + + // Check table. + $query->update($db->quoteName('#__extensiondistributor_extension'))->set($fields)->where($conditions); + + $db->setQuery($query); + + $db->execute(); + } + } + + return false; + } +} diff --git a/admin/models/fields/downloadsfiltertransaction.php b/admin/models/fields/downloadsfiltertransaction.php new file mode 100644 index 0000000..ef3b10b --- /dev/null +++ b/admin/models/fields/downloadsfiltertransaction.php @@ -0,0 +1,79 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Downloadsfiltertransaction Form Field class for the Extensiondistributor component + */ +class JFormFieldDownloadsfiltertransaction extends JFormFieldList +{ + /** + * The downloadsfiltertransaction field type. + * + * @var string + */ + public $type = 'downloadsfiltertransaction'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('transaction')); + $query->from($db->quoteName('#__extensiondistributor_download')); + $query->order($db->quoteName('transaction') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + + if ($results) + { + // get downloadsmodel + $model = ExtensiondistributorHelper::getModel('downloads'); + $results = array_unique($results); + foreach ($results as $transaction) + { + // Translate the transaction selection + $text = $model->selectionTranslation($transaction,'transaction'); + // Now add the transaction and its text to the options array + $_filter[] = JHtml::_('select.option', $transaction, JText::_($text)); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/downloadsfilteruser.php b/admin/models/fields/downloadsfilteruser.php new file mode 100644 index 0000000..d0f0e1c --- /dev/null +++ b/admin/models/fields/downloadsfilteruser.php @@ -0,0 +1,75 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Downloadsfilteruser Form Field class for the Extensiondistributor component + */ +class JFormFieldDownloadsfilteruser extends JFormFieldList +{ + /** + * The downloadsfilteruser field type. + * + * @var string + */ + public $type = 'downloadsfilteruser'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('user')); + $query->from($db->quoteName('#__extensiondistributor_download')); + $query->order($db->quoteName('user') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + + if ($results) + { + $results = array_unique($results); + foreach ($results as $user) + { + // Now add the user and its text to the options array + $_filter[] = JHtml::_('select.option', $user, JFactory::getUser($user)->name); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/extension.php b/admin/models/fields/extension.php new file mode 100644 index 0000000..7052cad --- /dev/null +++ b/admin/models/fields/extension.php @@ -0,0 +1,186 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Extension Form Field class for the Extensiondistributor component + */ +class JFormFieldExtension extends JFormFieldList +{ + /** + * The extension field type. + * + * @var string + */ + public $type = 'extension'; + + /** + * Override to add new button + * + * @return string The field input markup. + * + * @since 3.2 + */ + protected function getInput() + { + // see if we should add buttons + $set_button = $this->getAttribute('button'); + // get html + $html = parent::getInput(); + // if true set button + if ($set_button === 'true') + { + $button = array(); + $script = array(); + $button_code_name = $this->getAttribute('name'); + // get the input from url + $app = JFactory::getApplication(); + $jinput = $app->input; + // get the view name & id + $values = $jinput->getArray(array( + 'id' => 'int', + 'view' => 'word' + )); + // check if new item + $ref = ''; + $refJ = ''; + if (!is_null($values['id']) && strlen($values['view'])) + { + // only load referral if not new item. + $ref = '&ref=' . $values['view'] . '&refid=' . $values['id']; + $refJ = '&ref=' . $values['view'] . '&refid=' . $values['id']; + // get the return value. + $_uri = (string) JUri::getInstance(); + $_return = urlencode(base64_encode($_uri)); + // load return value. + $ref .= '&return=' . $_return; + $refJ .= '&return=' . $_return; + } + // get button label + $button_label = trim($button_code_name); + $button_label = preg_replace('/_+/', ' ', $button_label); + $button_label = preg_replace('/\s+/', ' ', $button_label); + $button_label = preg_replace("/[^A-Za-z ]/", '', $button_label); + $button_label = ucfirst(strtolower($button_label)); + // get user object + $user = JFactory::getUser(); + // only add if user allowed to create extension + if ($user->authorise('core.create', 'com_extensiondistributor') && $app->isAdmin()) // TODO for now only in admin area. + { + // build Create button + $button[] = ' + '; + } + // only add if user allowed to edit extension + if ($user->authorise('core.edit', 'com_extensiondistributor') && $app->isAdmin()) // TODO for now only in admin area. + { + // build edit button + $button[] = ''; + // build script + $script[] = " + jQuery(document).ready(function() { + jQuery('#adminForm').on('change', '#jform_".$button_code_name."',function (e) { + e.preventDefault(); + var ".$button_code_name."Value = jQuery('#jform_".$button_code_name."').val(); + ".$button_code_name."Button(".$button_code_name."Value); + }); + var ".$button_code_name."Value = jQuery('#jform_".$button_code_name."').val(); + ".$button_code_name."Button(".$button_code_name."Value); + }); + function ".$button_code_name."Button(value) { + if (value > 0) { + // hide the create button + jQuery('#".$button_code_name."Create').hide(); + // show edit button + jQuery('#".$button_code_name."Edit').show(); + var url = 'index.php?option=com_extensiondistributor&view=extensions&task=extension.edit&id='+value+'".$refJ."'; + jQuery('#".$button_code_name."Edit').attr('href', url); + } else { + // show the create button + jQuery('#".$button_code_name."Create').show(); + // hide edit button + jQuery('#".$button_code_name."Edit').hide(); + } + }"; + } + // check if button was created for extension field. + if (is_array($button) && count($button) > 0) + { + // Load the needed script. + $document = JFactory::getDocument(); + $document->addScriptDeclaration(implode(' ',$script)); + // return the button attached to input field. + return '
    ' .$html . implode('',$button).'
    '; + } + } + return $html; + } + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get the user object. + $user = JFactory::getUser(); + // Get the databse object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + $query->select($db->quoteName(array('a.id','a.name'),array('id','extension_name'))); + $query->from($db->quoteName('#__extensiondistributor_extension', 'a')); + $query->where($db->quoteName('a.published') . ' = 1'); + $query->order('a.name ASC'); + // Implement View Level Access (if set in table) + if (!$user->authorise('core.options', 'com_extensiondistributor')) + { + $columns = $db->getTableColumns('#__extensiondistributor_extension'); + if(isset($columns['access'])) + { + $groups = implode(',', $user->getAuthorisedViewLevels()); + $query->where('a.access IN (' . $groups . ')'); + } + } + $db->setQuery((string)$query); + $items = $db->loadObjectList(); + $options = array(); + if ($items) + { + if ($this->multiple === false) + { + $options[] = JHtml::_('select.option', '', JText::_('COM_EXTENSIONDISTRIBUTOR_SELECT_AN_OPTION')); + } + foreach($items as $item) + { + $options[] = JHtml::_('select.option', $item->id, $item->extension_name); + } + } + return $options; + } +} diff --git a/admin/models/fields/extensionkey.php b/admin/models/fields/extensionkey.php new file mode 100644 index 0000000..39f5109 --- /dev/null +++ b/admin/models/fields/extensionkey.php @@ -0,0 +1,82 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Extensionkey Form Field class for the Extensiondistributor component + */ +class JFormFieldExtensionkey extends JFormFieldList +{ + /** + * The extensionkey field type. + * + * @var string + */ + public $type = 'extensionkey'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get the user object. + $user = JFactory::getUser(); + // Get the databse object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + $query->select($db->quoteName(array('a.id','a.name','a.type'),array('id','extension_name','type'))); + $query->from($db->quoteName('#__extensiondistributor_extension', 'a')); + $query->where($db->quoteName('a.published') . ' = 1'); + $query->order('a.name ASC'); + // Implement View Level Access (if set in table) + if (!$user->authorise('core.options', 'com_extensiondistributor')) + { + $columns = $db->getTableColumns('#__extensiondistributor_extension'); + if(isset($columns['access'])) + { + $groups = implode(',', $user->getAuthorisedViewLevels()); + $query->where('a.access IN (' . $groups . ')'); + } + } + $db->setQuery((string)$query); + $items = $db->loadObjectList(); + $options = array(); + if ($items) + { + if ($this->multiple === false) + { + $options[] = JHtml::_('select.option', '', JText::_('COM_EXTENSIONDISTRIBUTOR_SELECT_AN_OPTION')); + } + foreach($items as $item) + { + $options[] = JHtml::_('select.option', $item->id, $item->extension_name . ' ⇒ [' . $item->type . ']'); + } + } + return $options; + } +} diff --git a/admin/models/fields/extensionsfilterclient.php b/admin/models/fields/extensionsfilterclient.php new file mode 100644 index 0000000..bf2bdb0 --- /dev/null +++ b/admin/models/fields/extensionsfilterclient.php @@ -0,0 +1,80 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Extensionsfilterclient Form Field class for the Extensiondistributor component + */ +class JFormFieldExtensionsfilterclient extends JFormFieldList +{ + /** + * The extensionsfilterclient field type. + * + * @var string + */ + public $type = 'extensionsfilterclient'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('client')); + $query->from($db->quoteName('#__extensiondistributor_extension')); + $query->order($db->quoteName('client') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + $_filter[] = JHtml::_('select.option', '', '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_CLIENT') . ' -'); + + if ($results) + { + // get extensionsmodel + $model = ExtensiondistributorHelper::getModel('extensions'); + $results = array_unique($results); + foreach ($results as $client) + { + // Translate the client selection + $text = $model->selectionTranslation($client,'client'); + // Now add the client and its text to the options array + $_filter[] = JHtml::_('select.option', $client, JText::_($text)); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/extensionsfiltergroup.php b/admin/models/fields/extensionsfiltergroup.php new file mode 100644 index 0000000..8b3696b --- /dev/null +++ b/admin/models/fields/extensionsfiltergroup.php @@ -0,0 +1,76 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Extensionsfiltergroup Form Field class for the Extensiondistributor component + */ +class JFormFieldExtensionsfiltergroup extends JFormFieldList +{ + /** + * The extensionsfiltergroup field type. + * + * @var string + */ + public $type = 'extensionsfiltergroup'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('group')); + $query->from($db->quoteName('#__extensiondistributor_extension')); + $query->order($db->quoteName('group') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + $_filter[] = JHtml::_('select.option', '', '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_GROUP') . ' -'); + + if ($results) + { + $results = array_unique($results); + foreach ($results as $group) + { + // Now add the group and its text to the options array + $_filter[] = JHtml::_('select.option', $group, $group); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/extensionsfiltertype.php b/admin/models/fields/extensionsfiltertype.php new file mode 100644 index 0000000..ad149bf --- /dev/null +++ b/admin/models/fields/extensionsfiltertype.php @@ -0,0 +1,79 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Extensionsfiltertype Form Field class for the Extensiondistributor component + */ +class JFormFieldExtensionsfiltertype extends JFormFieldList +{ + /** + * The extensionsfiltertype field type. + * + * @var string + */ + public $type = 'extensionsfiltertype'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('type')); + $query->from($db->quoteName('#__extensiondistributor_extension')); + $query->order($db->quoteName('type') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + + if ($results) + { + // get extensionsmodel + $model = ExtensiondistributorHelper::getModel('extensions'); + $results = array_unique($results); + foreach ($results as $type) + { + // Translate the type selection + $text = $model->selectionTranslation($type,'type'); + // Now add the type and its text to the options array + $_filter[] = JHtml::_('select.option', $type, JText::_($text)); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/fileselect.css b/admin/models/fields/fileselect.css new file mode 100644 index 0000000..622e0c9 --- /dev/null +++ b/admin/models/fields/fileselect.css @@ -0,0 +1,433 @@ +.av-modal { + position: absolute; + display: none; + top: 100%; + left: 0; + right: 0; + padding: 4px 0px 4px 8px; + border-radius: 3px; + background-color: #f8f8f8; + box-shadow: inset 0 0 0 1px #eee; + z-index: 100; +} + +.av-modal.open { + display: block; +} + +.av-folderlist { + display: inline-block; + box-sizing: border-box; + margin: 0; + padding: 0; + width: 100%; + max-width: 100%; + max-height: 250px; + overflow: auto; + list-style: none; + border-radius: 3px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.av-folderlist * { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +.av-folderlist li>ul { + padding-left: 20px; + list-style: none; + height: 0; + opacity: 0; + overflow: hidden; + -webkit-transition: height, opacity .2s; + -moz-transition: height, opacity .2s; + -ms-transition: height, opacity .2s; + transition: height, opacity .2s; +} + +.av-folderlist li.open>ul { + height: auto; + opacity: 1; +} + +.av-folderlist .av-folderlist-item { + display: block; + position: relative; + font-size: 13px; + line-height: 21px; +} + +.av-folderlist .av-folderlist-item:before, +.av-folderlist .av-folderlist-item:after { + content: ''; + display: table; +} + +.av-folderlist .av-folderlist-item:after { + clear: both; +} + +.av-folderlist .av-folderlist-tree { + position: absolute; + display: inline-block; + top: 4px; + left: 0; + width: 13px; + height: 13px; + background-color: #fff; + box-shadow: inset 0 0 0 1px #aaa; + z-index: 1; + cursor: pointer; +} + +.av-folderlist .av-folderlist-tree:before, +.av-folderlist .av-folderlist-tree:after { + content: ''; + position: absolute; + top: 6px; + left: 4px; + width: 5px; + height: 1px; + background-color: #aaa; + -webkit-transition: opacity .2s; + -moz-transition: opacity .2s; + -ms-transition: opacity .2s; + transition: opacity .2s; +} + +.av-folderlist .av-folderlist-tree:after { + transform: rotate(90deg); + opacity: 1; +} + +.av-folderlist .av-folderlist-label { + position: relative; + display: block; + padding-left: 38px; + cursor: pointer; +} + +.av-folderlist .av-folderlist-label:after { + content: ''; + position: absolute; + top: 0; + left: 18px; + bottom: 0; + width: 16px; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAAQCAYAAAA4eCmgAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoyY2ExMTM5Mi02ZDQ3LWI4NDMtOGNmZi01OWQ0N2ZiNjk3OWUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RUQzRjgwQ0U5RjU2MTFFNjhFMEU4RjQwRDI5MjczQjgiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RUQzRjgwQ0Q5RjU2MTFFNjhFMEU4RjQwRDI5MjczQjgiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MmNhMTEzOTItNmQ0Ny1iODQzLThjZmYtNTlkNDdmYjY5NzllIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjJjYTExMzkyLTZkNDctYjg0My04Y2ZmLTU5ZDQ3ZmI2OTc5ZSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp8oY+AAACfuSURBVHja7HwHfBVV9v935vUkL70DKYRmSOggSBWsa8PeexflJ7i2teyy6s+6gO5asa6rYnfVFRWVooJ0iJSElgAhkISUl5fktSn/c+a9eZk3eQFW/f/38///f6OXmTcz9557Tz/nnomgqip+zSEIgn7Zm1oONcsRujRQ20tN+aX9/jXXhZy8QfA2VuP4ma2/Cv5/ev2/Efxfivv/V9b/P/B/BQ/83zz/yuevjfCxgvBQAhSFr4WpsiyfKSvSVHpGl6HVdP+DQNC3mK4RCPoRDHbi9Cc2/Cr4V3y4V78/hmDPL0lxjOuUVHSEZIRkBT5Z9dAzL7VD1GRqLdSaaMZvEuq+fvO8gtBvQLtfS/9fTHvr9Nnvz5h0/MjHmzvVRFUlChDyCd+QtbMKhQijSDKdiVEi95NtoY4t2/bf/cnc8581DJ4TCAS+FUXRTcQT40GXJAmvvPLKMzNmzJhnmEiO3+//1mKx9NiPqI+XX37Z3E875s6di9mzZ0Mfx2q1uum6R/g8zq233jrvMIT4t44L/lqB924b8h+Dr+P+aHC4YMGCHmHPmjVrMwlYITFFw8GDB0tYsI3KgX+HQqGtROMSWsfur7766ph/dw490B//p3DwvxH/v3j9r776arxXVaYXv6+3YDConYkGWuPrlpaWsMb8bdb/S+UXb/5r418URbi0M6g4/VKEX/SF8D+kR1i7CxGlY7cJcNrw2rVnDZ/VMe9GJM568bfknwJq823JWWe7s3vDmZoDgXRWR/OBYZ79228INNTspOd3UPs0Hv2OJL9m+KwbI8exw3OTxs2elBnTpyOopBBeUlr8cu+QJKCNELSnqQP/2tV+Ij2+ndF3tLiPR7vbvzoD80/+rBv+zPJ7BPpH+1ZWVopVVVXazbPPPjs6Rk99rcdOGPWMzZEg5joJIP0nREjPHQ3eQYQZohNK7PDJz9DZaEAsjABChGjsx4B5LLqPjIwMnHvuuTP5+S233KJPxMITJwERzYtlT4KFJT09XevH9yPEC1B7hRqvdNbSZd9jyuSJ2jg6fOPCeRwWwMzMTJx33nkzaZ5G+L9aiZz1xHr8864RGnwiRNz1M3x9Hb81fB2H8XCvr51xz2vn5/EEkN7rR+1N6nOdLvfmg2lIz+fSeKccaQ46DcxzOOecc4x0/C2VeFwcsCLW4ev4N+Jgx44dRvjsCdzJ7/PcqbkjZ69+j8Z7kq4rItfd4H/99dfi/PnzMWXKFIYRxYMZvk5/n88Xd/0TJ07U5s0wemrsPJnhB30+UXrmEYgTpsEyZoKGf35Xx795/Qb8x5XfePgz009RhctOO653ltPppK6WbtGFUZH5CZoshfDxkl1Xsezyvfa5NyBp9kvd6Mdn8/yPwD9EO3mOu2SYK7WoDE7l77BiFcHLgy3xLqT26g9P/Z5+9TvX/zNQV/UkvX+XGX/x5NfIv+b1K5IUVcLJ9nC/fa0ybDSGlX5arIDTLqLQIRIAEVbRjuG9XchPSUh/YVXdXyIG5Kh0Z0+6K2JEjloHxBkj2pfkAWPHjsWqVavQ0dHRjXfMfa0dIVXsaOlgKgNGg6ETXYhjUOi6M6gaFb4QIbhIEwEDiQieds2T0PsmJyfjiiuumEmWHjfccMM8vR+/x40XywdfG5mQ+1155ZUzk2yHkor6eUf3HjC8fN1Hd0+mR49S2/DBBx/kLl++XGSEORwOpKWloVevXtq1Pg7PJw78mh1fDN/oTC4vZeOvr5ONZcC7fZ8stYyjGQR0IdDmI2gatmPgaZXsb9VSaz3t4R+ds1/fLioy9aQQVpEplKYQVgqSoiEv5ckbemt9U1NTNfiMpxtvvHHe9u3bayI4/JbmPlVXDkaPkxlIP/N9at95PJ5pR4P7eDjktTNsXrvBgDgI7jkEM5R9/XzJKTooBKeFk/d4Y9az6J0EXHLrD1aax2SCX2rORJjnoKzfAGHZMliD5DHPvDU6h5SUFFx11VUxc2D8O5LLSln5CAZ3JeDdQfhvHhdxGLpYNBb/GuiJlw8f+s/174hbazei0XsQme48HJNXTm0oCtL7Rkdl/DMObDZblP7UJrEg070CxpFZAeo8yefGxsbLSOmz8FxObblx/V5vu3j3PQ/C6UzEOwvfxYknnohjjjkmLv/r9Cd81/TkcS5atAibN29GZ2dnNBLh/oMHD8aYMWO032b8i3wVkiCmZWgwdX7Q4few/u70M6zZqJDi0a8joJCYOYW/L2uBH83wClXom9MAl80DSVXQ1ObE5p2pSFAGkWLNxO/PKwKJREyQ4n3qOvjn3sShF/ihwsqTGJAbLRTifS9H4ceZv53ai67c/lfl2TbD1vQCpLYcWEopQks+CZb2xejcuUYbO6lXORKGn4DGvRWctnib2kZ9/ayEeP3cdOfAjD8dvr5+ORTS6ZdsobWyOfEGyXiQHRXp2kok0q6ZZ1UZvVIsuPftfXj8kgKs2G3LescE26wDzbyjwzasXcPff315Bu745mxN53DmSJbY+WA9RPokpODJU96Ly390a4+R9v369QMbEd0J1HlIX7tZd1pVAvTIpf3+bXfv2sf2drvXHlCQYLN0KWE686SMysxut2vna665ZiZN4i2D+Y++xwjUf+sH9wt2HMDxI1OuySocju1LH4O3se3lohHFX+RkZ1rLy0rFpKQkrT9bzvr6eqxduxaTJk2KMUhx4NcIqjio9/jXbRqh1AhP07UcaOpbv/FP+xRtQdCiM4s9mfgg1OA98NWd9OJ7oaDiXfhfQwZbLXEjTzR7grjzmW1RRmTG53bttdfOJAK+FWGCyXRvan5+vmYgzDliHQ9a+E/zX7p06VQyIFPp1ncmFzTGaPPveGs3wN6jMyAZhgrq8xkppenr7S8NHmO/Ak65LwkdUI2hGOz6iue2jdrr9K61J77QYP34I/DG32Hr3xuBH9ZDvH3m4eag4b/P+De64V/yH+pbv+lP++iXqkfGOv7bD3yt4X/Gqxfxmi9TJdztC7XguPKTEbT0RkNLHSoPrsKuhkpM6H8CjskZpvXX8W+gfwm1xdnZ2YLU6UXNT9/BW78fbQ0HIAU6w33IILhz8uHOzkfB0GOhZmUVVFdXL6VHnIb4tsvnUnDLtSPQr28Ar7xeH8P/Oi3N6w91KaCYg2WgsrLSmA2KHiTg6vDhwzWHohv+7Q7Ivg4IWzZBHFAalSddCZnXf9bZL9Xs3btWrKr8yv3xJxVYtmwnPbdgd3VzODneJwXjxxViwviCKP+a6ReUmPessBCMoHgQxRn1sNtq0BrwQlYk2BxOlPTJw+5diXA6sqDpR90xpR+Js186rK5puvm0GD1glF9FYQMszEnqPeiq9IEjYd34Lk36RFhrF6Pt0BQkhNag0zMSX1s2YZv9AHIbfsLlmdeicMgJlu1rP7tVueeO60jf3u/x+6fjpRdgY4+dmoUcH4WajZolEITo9yM0ahRsl1wUI78Vta4akcSsf1pbzrJ9MpaT3pkxMh0vbGmDPQRcPcyNJCc5x8TBFiESMkgerKpuxbq9bXXGvUim1U8//aQ7iTFO5AUXXBBPd2nKf95Jnx4Wf5e+PLEb/kw6ICq7u3btikYg8XQw85FRd1rVSAQY5FyeEPaa6X+6q3Jsqu15sFctheiaBCRI18W5brofE77Dmjsk6U/ftcJDnRecnolbvmlFIo1x37gkMipWbSKtra3RsDwrK6ubotSVjNEK88H9Qr4G+PZ/il6DhqBl19vYU7FlQTAkUMgaXF1YWGg1vs8L7dOnDzZu3BhV3HyPlG40LUYKw+DuqX7iFkeg/gXIQfKCRfJCBTecedOQN/pxezgaU5jbw3MVE3M7PvvuQVZgUkCWbGQ87n9jO1p9MgbnOLGjzg83CWFqooiNu33kAYQFj+HrEYa+fp4Lrf1B9gzZ6OlE0qMIYwqgtrZW8yCKi4tZudxH433XTXkb8KmnXYywmSFj1t7l8SZTm0zzkVJt67GkYzXGWuZDlAdgUfN0bGwtJ4a+TCKmvpBaSo+borX7IT//IqxFeQhu2glhSDnYY9HpqKe1Yugfxf/zdCJeIfwLSIIzfxryRz9pD+NdgU4HQXTn7ozgn9oEGv/O8088r8SdWogWrwPNPgdq/UVITM1BtmMVlm9fjBRnmoYTM/7peIquhcTERHz54mPY9eNX3fJ3QiR9y0asZOLJmHLTfejbt69AivwpejQ8in9ImD5+G6w2BRnWdvCeItOEBZHpynsWTBMjDcwOg34wX2hyZY211U6nM/pcf8fMA4IrCYLTpfXVHQmGr+PfxP+OPn1G3VZQMOrJf7y9TrPdDz90Gu5/4PMwz+1rxbv7PHj3/QpceH4Zjp/SV/OkY3iI03nsY4kcqjeS8WiBx+9BQPYjIIWIvO1kOBzkYLqQmTAq7CBEZEmNZICUjnZ0vvM4DUXOT80OiEVlSLpwBsTkFKikwDmT0IP+GElG6s6M9kQIf34FneXFcGAdfM7R6GzNha/lRG0Pt9r9AwrTBmFfaxUCdVvhTsulQCfE0e0fxZJ+f0rr1YcVB0SSFZEiPh/xsd/rhcNJOs/nQwc7L59+BntHJ0LTzzDrr3SfmHJy32QrWlpldAZIdxJZ3YkCXlzlweXDk5FsF7V0FlQL6r1BzF28s41+XWWm3fjx46Myr6cPmV96kp1De7246fkLkJefAV+A8Cxl49uqD5HmzgQ5ZjjU1oD8nLyo/ovD/zHw+/fvr0UgekZI5x1dd5rhWznc0TqTEmRvgGNgVVA1i6mwyBBXyAIZC5G8KUWETQ6/r28eHfNwNYWZHel5mbb5gpWsrC/iKdK4NruAh5e24w+TEpFot0YF2CwUpmqMmNCbFyBIh+Cr+wz5A8rQuucdLP948Tq/V7jH5VKDkiIJtCjx4MGDYCXAi6yrq4umDnQvXA/H+J5R0YalURBVorgctGsbfrJvD0Id9Whv+I68p3yiJgmqGqT5cMogBPfAe1ipFYY9xfDOkM+vwE44K8hxYdX2TvQttsPbTt5MUGUFqa2Z4RvD48gxmZ5N5ec8Lx8xq9H70KM57ud2u7U1csvJydGjkCXxohUdh8b1G3/HMSAhatWkXMrzHMDYjCS0BG9Ae+dwNHlORqU3i+ekUON3cuIOQrCVf7wF68BChCQrbM//jePeKB1YCfCaus1Bx3/ArukWxn9jNTn2m5+J5jgEYy5dEOyiLSk90vvcISWl5fUpbvhDEmo8iahuDOEQOfaJvXbB5vJD2tuBrQcrUJTVvxsO2traStgw857FqbMfwpLUdDTs2AxP3V6EOtvDGtZNCiCnAFn9S9H32OOjaQXitb7GZVRsrkJlxZkU9Z6AkadswhdffI4RI4Z1M+pG/o9nBPQUlp6iSkhI0PpwxKFHHT0ZEK3Y4eAByJ0/wHXm+TFr5bV3x79wM3V58g/3nkh85UBNdROys5LwyMNnaNdZ2Ul0307XzVjw6krYaB7TppWYgWo0snAKmPRCQA7BF/LDLwUQVEJaWgdCUCvC4X0BxZASUkPhNXa+9Shcp90AITUTnvsugbRxBdpIaaX+/nEo/lBUkTH+jM5iSArdnFk6ziJ8vRPq4FJIi76B/48Xw5pWABcbKc7/12/HVG8x1sh7MbGjD2Q/RQfJvcgpDubCJQwirU9wM4DBZWyhIaxcCXnrVkgcMVL/AMliW04OEunatWYdhAvONa7+oiH9E17LTRCdTc0yrhmdDF7R6cUJ+KSyHZlJZETWtmJMqgPHlSZp9QSSlWgblIutdnuzMcJknHDWRKetXiyhK31eN/Op8UjKcGBC+olIJUMbytsLpT4VDtuF8KIZ+xr24YlLXsWd71wd1T+H0wH8fPfu3TjuuOO0SMj4ns67Zt1pVXSvWvMgRI24FjVSOIHwtShYwoQnIyJbIgZEjhLxohHlWa/lEAK9HgVzTgw7pw+OS8aDSz1IcgiY9XUbLiiy4aQhCYhXHdDTYngBgY59aNvzORmPweg4+DEqV+7GSx81/fc3WyWtBOX99+02Uky8CaUJ2t69ezUvjZWtLuhmhGlemmDMtitORSLF3cl2oh0hbxsZiSvhzDgm6n92ecCEJ2sqVFnUtIAaySKwX9hJxmLlJg8pWhl1B4NoapFxXHkiflrjCRvCOAaMGORBtujMLHr+leevC4k5CuH7rETKy8s5P36f0YAYU4dGHOpr1o1yvIMMQ5DmxsZh8KnZ/WAXEiEnlaA9uQFC/t+hkAJ4PrwXU02vjohrP3aTM7F9GwKHPLD94R72SmLmYNxcjyW22oX/UDuadn+OXhOeJfwP6hH/O98dp495qiMjBTWddRhhycf66nYkZwbQ3msFdio1qK+14KTsY1B1YDPOHH5hN/hVVVVt06ZN4w1ELVKbcMVt0fny3kPM3lfkaGxs1MP7Q/q9ESNGDNizpwbX3nInOJvpTE7DySc/qo15zz33RMcwOjV6BKILtzEaYX4w/jY6GIczINK+PWQ8fCSrVoS2V8I2YFC3VKYJ/3MYp9nZbi3quO3WSZBJ+P/70a8oEjldmxffn3nbZAwpz8NHn2yhdQ3s7jhE9qecyCan4yA52nb4lQ4ts2GzuNDhdSPJnk9euEVLSOoz0A0IJzTYeIjkHKlkMBS6r2zZGH6HIhBdfvSoumuTX5rqTMmBf5QXWF4B6ZxyONL6YO+m73Bw93pkFgxGUdkUvLMlDYuacjBY8OB560rSaQIZNslF8C9V9+9vErztM8TCQiakZnSELVvCGZm0NHSSM5q4voKUdCoscx6AGknj8VFW4nyxyG1x7m4KYhYZD9XGDiKQkWDFyQUJ+Hx3G/q4rVixvwNj+idqLCyxc0iIkeKkIEeNGmVCbVckYl67xgcBCZdOvh6Pf3gvenmK0Jy6Dc6URCxaugzP3bQQA3uVkVxJUfyZszvmo6SkhGUiqiONvMPGrJsB0SMQRASLH6uRjWJNZijikAWN5nSLlJAegURSWOX9XC8Wuq3O3YcC8n3jUyw5KWHvKsttwcyRiXj0Rw96JVmwcKsf08oSowsxV3iZD0/rIdTv2wRL45soHnMufIcWYdPin1B2xlv45qZhtV25YtXS0tJiNXpmZu9OR1yM0TCWqEpkIF3ZSB10bTS85tSeEtwPqfUbMhYerUFqI0b3wtnrfsAf7i8HlQghVdR1ykj1SkgnT6yqjsL8BAUnjcvBjytbogrcHHGxkuA9G1ZKvAael7H6Rjca+j09KmMDGS8HHi+qM26s93RwCovmclZubu7QRXfUdlOanFqg5wP4HWrJcUuFP/8C1j55sJUUwHfPHyCefDIc998Tg/+4BiQkavhnnmuq+hj9zluqRcFKoBaS51vyail8lzwROrRr+DfMbYBsk9DkbcePTR1oaSNP1RlCumMYgg1F6Dy0AgklDjS01cUYMv0g3llB0dz52h5ee7uWJuRqIz3/q89XN9ycZuTnzFt077M77rhD8x+WLl16F29s61thnEI966yzMGfOHFx22WVaQUe3yDcOvXSa6qW7+san2RPUCyrMR+tz8+GYchJcU05A85N/RvrvH4CtqDgGtgn/bhb0jg4yuu1BbXxfJ/NkkAxoUDPgfM192r1BirRD0X2QmAhE1UqJkGTphdqD9Uh11lHkIsIpSPA2J+LgwXzkp5TAKgoxtTq8z6Dxzq4qeO66KLx5HggRvSWtECVsQELd5LdL/qVci9UOYWAZpNxMyC37tY3jA7vW8XceQt3ONWph6UR8L2VgXGEuVu4l3PraNTmXpaBLqyR65m+3bT33nIz+Pv9FVoqYkZcHig7gIoc0SI6ade1GpKVlwPH0U5DtsWtfu9Z7o1qe9Fp2osX53PcenDMiFSmk+1pJaf9rWxvSSfftrg9ibLYLDjtFhzS39g56qKItHg/8/PPPGm+ZK+50HtRTXFEHJCCjcv9m/KviA2QkZuKqKTOx2b0G44ZNwcJlL+GBi+cj5I/FXzw+1Pl8z549WoEGR0LxskHmw6oY9jKi5XMGQ6LaSLjZAqrhPLBuu+SIFVuzpu1GuSzxtewki/Opb1vx2JkZyE6zot4jYe4yD1IcImoIgZeWJlD4K2jfqhxJmdXu3YFdlRXEAC2wNbWRgp+PttYgisY/hvQ+ZeZowm8uN9TXYUwV6ffYk+tmgUM0G9kPxfdzRFG1acpKCTaTLSHDEWoJnyMKjMMOIVIAI3NdIpcE+UjZ0PXJEzOxYZsHHjK0U0anIiM1nMLXBV9XTPr6ab5T9TJPnm9FRUWM5Td6HXxdUFCgvccVEdy3exZJ7XZtLGjo6aCx2ohx/7lr1y6p9KHPR7oiVVicgbsu80X0ShQwYtxPlaTYPqP3zjX3/1NR3wHS4sWwDBuAzq9XhEPYOAwaNwoKsXQEcOinf6D43IcQal0UNhaEd4UDTTYgciuNSXSR2lHz3tXIKJ+xOVJavl3slAcmkOKqPkhG1paK6v12tK8DEh2ZyE2vQ1NbI3JT8qN4NcJ/5513XpkxY4ZmQNi7ZxyzITHiX6+K0xsrUE6T0jj3V23fxa9NHTy4rHzgwIExKUeuwuKSXk6v9u7dO25xSDz66aWbpgiDaf0G72szDzQ1NfFcmmMMePMh+HfvQurtd0Pt7CDZdaD2ojNgze8FS3YuLJnZcI06FgmnTzdCfJ2QeHVxUSYeefh0bNlSh8aGcOqO01ZTpvTX9kS2bDmA3dVNmDq1Xw/OHysaIMmahV6u4di3/lukJtVCJP1i82WjsOQyJLnyw/xs/E7EH16j2HsQQj+v1lJgXEWmkGdtLQqnytSg1K0op8vxCb1Ss+bzWxPScuFKSkdC9gDN+GT2PgZ1u9epmaQvuPrqbnU93tzTiVnyLiQWj0Zzwx7uW2X5y/xwJPHRx/P9zz53Uej5F2C75GJYCccqGf32dZuQ5kqE6+knobic2pd2Jvot3F2xfWtjStam/n1T8Y9lTbj19GwsXEO6L0lEa2MQ103OgNtlhZN866Z2oqs/WG9z2OMyAWcW4vGEOSOhH50dPpzzl/EY3HsoQkIIcz99AFdPm4WkYg8a6xrx6Ad3w+8L9Yg/s65gnvvhhx+i1atm/WHua5V0JjV86ANDuS4bE+YMixaMqFEPSw5FSwgX/rhk/+I++fYlQwdnld/1XgNevzEff/isCU6nCLlNwvPnZyHZJcaURPZ0VO3Zi8UrK1Fe3AfDCgegbm8Gtq1+Hf2GnIVeZSfFEzlNKbFQc8qBz3qOWffmjAo7Lnx2/InRlWBj2ICQsVBJcYWNSbPB+w0bEG0vxB/BQ1DL1GiRSEmmHadPztUMiJvemTo2i/AlaCkBo/cY59uKaNMrZuKdjVEIK7Ke8ufxDEk82CYDYqdWTM3yQ+BRjLFfCadcrBmRXd4clDq/Yngiv0PztEcr7044BZ2KnCP5g3fZS/vC/2MFHH+bDyExgV3nmBRcj3MI8PZbJJVBNFAiBkMNUVNaIpFfq4b/xn0OvcI8wgzCF4cONA4MuA+QhzcGHR43RSDJcKXUw+Fah5KUALbsqcaUslPiCuCGDRs0T1CPAvX9BnPaUC+x5MiCeYxw0EzraX/1nt64+4U9CVU7d5Lek7TUiG58Nm76Wdsjy87O6bb+OEo4aiAoCtTmQn2aI1Uzt1MUMo+jGE538hgrVqxg3vnMOEDjX+fCNmQYfJs3Qab+7gsvQ+pNM+H97COE9u2DaqV5d7Sb4d5Hc0mvrmk66777PzdU1gpY8MoKFBalRzbUBfJMC3DRBcN7UHAqKpuu0xxLbgMCB3FcQYmmM6rJgH7RfhfZfzmy/iUGxgvzsPuq2fA880eEqsgvkBRS4MVIu/fJaJRyGP65zdNQ83Fz3Y6psiKPVOTQhILBk5OKyiajsHSClo7y79uM8VItxqs1cOb0g2KxoqZqJfk40p+jkQwBEG+4HtKLC+B/+TU4hpbB+90yJCWnasaDwilNF5rh54RWw+ZwBNPZj/W6aersLYqQSD+21Qu48oQsuOyi5kQmWkUcavWRrgjUUYsrh7yBrRe7GGVe50Ouvov5zsMvY/bv5iAlMZXTeeibMxBrt3+P7bs2I0gGRQzxpwTyUemAJUuWUECwBlwNyrqU+Y33kg/X16pvhlv0ek6tlLJ73lf72oQtkaE+nI/qvwzQqlUbEktvH/rEom99oXDfAJ0d5EU8dlEWeR5i3Eoh81FddwArKnZBTO9HoaYfLy5tI28mAfde/BxSM/Li9mloaPioqqrqbvb+WNginrkWgrEyMEc78eCrAUUlNU+GgoyF3BJJmehpE/J8ZW/kTMZD8YUxFQluBFl1NbUE8cTsgRqT+PwS7riqJGq1G5sDxDBdczDDZwNiDFMHDRoUk+/UIytjaktf0+EMSDxDcjjc0zxsNB4bECFchbUKYy1Pd1VhtZQRvPdFfoeaMY7/wSXJuSSAhUpbO8STplEUMiTu/k2Pcwiq6p7XzkHBWbcQzpsixjuCf4XPbVHjrexMQ8ZxMz5Vuxb40c79NdOGDxg4JCPbjmoSzIBCnpK7Dv0z96G9SUJeegHKe4+IC9/v9ys7d+7c2Ldv32FcWcTRh7H237h5yN8WsWfW3NzMzx+KrKv44ctCV19832as/Gk9Jk0YE/6YbPMHePvNl5CRloT09JSYCCyOIbudeHUew+f0GCuPlStXagaCePqWvLy8eRx18N4YP6upqdFSHTSvN9anOuETxdQtAelU/9Zt6LPgdViSU2Jg2GffG5bZ1hYIJE9CLA4OSKHAjddf/+Tm008/6b4NG/cTnbsUxZw/L8KoUQUYM7oAI0b0RkqyM84HxuGI4tQRv4/CtZZ5SAk1wVG3EZnH3YzLLbauD5UjeybhCD6chhNT05H24F/j83BQOhIPf6eXtIekYPmODV9uyO59jKVj52rIPo+2wSJanEjoP5Y89lbsXPs59uxe/bQgiG93U4g3Xo/g3KfR+c1SWFPS4Jz3BFTSIzDIr2n9maFA4KHxg0txybRe+HZbEM1eGddMyoHDqsJGvgg3OylYNw2zs9bHeyD1PckhV0GZo9HDHaGAgutOuD3m3pj+E2N+j7i++Kh0wP79+1FUVKSlsXge27Zt0/SR+TMBUwpLjSlXFAwfD8Z8Y9A1im6xY3ORHVvbX7ixV/T3P67OjevxxcuB6/eeXfgt6WUb6ZM2dHiDyEnNwjmTByItM7/HRZPAPUQKQNiyZcs55J2VR3LTIKHjr1Z78vbMKZQWqbMpIdRhd0NJFlWF9KOaTIvykdIKaCkrVaCzJUgEICXu96tqUAmEvSMV02es3hry+539ClL7Kkq4tp03jcKGgBlO7XEO+lemhq+fo8bC6AEblTE3Xp/hQ7K4VVjGD6COVLxAePNQe5fazblchZXuDldh+SJVWB1aFRYbmXfJiF3XZXwDw0jAE5yjBwuBnfuR8Mjj3b5CPtzmvba+gNxisal5oXabIqhuUVVJ2Shu6pxFxoPRHIQqBrQyUUFYpX38bzRghN6nVlVs+WNOVntJn0QfRGTCntyCytpDyE8pxLSy32meWQ/fW1SQ83HTRx999Fppaekx7HlxBGDce+KIxOVyacaD01HU+BsQLffRWrPuqSRL46nnT0rBju3bMGrkUOroR82Pf8Vpg/Zj7MV5UPc/Cwy8tyc83EL0n8f05MiGnR8WYDYQbW1tb9DvMq6M4fs8h61bt2ofF3q93tvIuLArP8mlKKcOtAin5Nz7QNR4mHlAa2np3db/z49v0Pwwap9+v/yJ+8xfj8ejm5mXRYQLbozP5YRUrQUyS8LbijDKgEGJBUJouHBieDOdDBdvnmtpLN4LoUhE5WIdWTmyDBu2EaRQ8OP9u9afl1c8HIHaSjjyBmjf6NTsWIWNqz+iAEm+n4Z7rKcB7LP/CyEy2I5pU0jrJx0WmBwMTqMVn7ZmWw1a/DZ4/Qovg5YlIEjzD/Ea6L90cmxvPjEHB5s6IQUCjYfbgzA6i0avn+/radIo+nwhTP3DYMhS5OPBSOMiJ61CNHLucf/RAJ/5i+HPmjULb731luaAm/Fu/m2trd7z3k3zQhcokcnyV9NavW9EEWsKiyy4Koe/A1E1odIMzXtx80mmD5/iET5yz/gH/bR+T82+7IibjKqqKsYvWHmcq6++eg6dvyCF+hO9IxzlH3jr+jMePt8T1S+cNoZGPIFm71Zj6v9jgjIKY5n/lwTJgr4Zea3fioWT+NXRKwl+vA+/Dgff5/N9Sccp+qaoXqqp59v1FJdelWMq6/syNthQhcOVRsdZu2owZENp/Eto/KJpGUWqQ0xA0NUHHUmkW7Jfpg4hrAkG+xDMsdSGdhmQYCILf/03KzfnfvjOYGSkC2ZD1gMfdNHf738iFKodU/venBPoNTdMeOez6Ex3svea2Hf8p4GDP0cHefaahZq/MvHy4c5TZpz50uba1Wj01iM7OQ/Hl/4OQwtGoTir/+Ho76P1rJ4+ffro5cuXv0HG4hwyIgJHAsboj+vwKysrGccfUJ8L9EG8lgFfwt983PgBrsQPNn/nfu6FNgQ6PSgm/+LUCcXIzXCg4YCI5EGCuRJNg0/RjIWiIC3y4QiavT42EmQ8biODxQZiCUXYWzZs2PAE0YhL1teGK6dQF97nUy4nZBX8HFQ+GT9sxFAz/xmNlokflPgBqyr0xD8muYr2d9oEj6qEUoZkH9v1gWAk2lDC3+B2KQftb6oF+evsqJjlvPeDJj+6/HYrcomv+LrNv6XlgH75yJZ1n56dkppvkZxu1FWuxL6adWg6VPOpqqj3s5HpKWDX8Wc77+weFa6RfnJIGnHe1CGuey85NtawcLUV/aMFT3T9ty/rkGgH2to7qU+oKR7ieejCwsIj6T8j7bQ+Sx7dGsWfuSw4nrMeD38MPzk5WRg2bBi++OILLRLhSLsHwx3ta/1k7vkX0vlC/LpDq7MkgfNRmMMVrWK8v4NjnAC9y5/5yloWyNDvCOkYY79u8OmgaF484jgm+OQcVs3/rdd/tPAvvvjiU38N4IULF+JoYBvowHjqhkNSTmSjQgtIab361d31JrpxFOXk708SSbG9T89OM31nh4LKTaMC/UqWsT7hdPKR1m+cw2+B/+/f3LDhm5cv7Jw+6pJ/m/4R+vG3YucR3/Y5cOBAdV1dncVUOkld5GLir32msRZwG3FT1ahAoGJZFw3uPVo+/isZpi0ej+cJciYEMuJrCWbUQBhSNKN6GO56/ufWoDTqelm+y8wDPckfKRvz+rvxULyCjHj4s1uFdz9aWj0uIKvlUUerh7JyTelYSEGL2n7PUclPPOPRgx7Qj42kpO9aumjeHFmWdlLEsYzamzSpdf+O/PbkiBrpR9FO9YFDHrz1TSVSk1xIdTvo7ERKogPuBLv2hyMdVhGXTMwhPAGNzV42OrW/RHdE5mOmXY/69zCOtBF/0b5ut9vFf88sJfLtFjs1R5Id4Tf8c8x9EP6TwOJRdpUjoTN+YT+zIP8i+LT+ff/J9f9G8H8p7vfp9C8tLa0kA9KXFFjF2LFjR8XzXD788MMquj+A2i7+44uaB37sBCSv/vH/W/yvf/cqjLzojf8Y/LXJDoz2Bn8VD/yP/Pxy+OWXPO2jOfAmBIcO/AfIMkhzp9F1GhmsFJfdbnO5bGRQXEhLTsD6bXu30/1LmXQ8yOa3b/8taPdrx+iTkJDQ+4EHHniopqYm1VDF6l+wYMHsw+H+fwkwALkq7axrxq6mAAAAAElFTkSuQmCC'); + background-repeat: no-repeat; + background-position: 0 center; +} + +.av-folderlist .av-folderlist-label.selected { + font-weight: bold; +} + +.av-folderlist .av-folderlist-label:hover { + text-decoration: underline; +} + +.av-folderlist .av-folderlist-dir .av-folderlist-label:after { + background-position: -64px center; +} + +.av-folderlist .av-folderlist-file .av-folderlist-label:after { + background-position: -112px center; +} + +.av-folderlist .av-folderlist-file-3gp .av-folderlist-label:after { + background-position: -128px center; +} + +.av-folderlist .av-folderlist-file-afp .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-afpa .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-asp .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-aspx .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-avi .av-folderlist-label:after { + background-position: -128px center; +} + +.av-folderlist .av-folderlist-file-bat .av-folderlist-label:after { + background-position: 0px center; +} + +.av-folderlist .av-folderlist-file-bmp .av-folderlist-label:after { + background-position: -272px center; +} + +.av-folderlist .av-folderlist-file-bpg .av-folderlist-label:after { + background-position: -272px center; +} + +.av-folderlist .av-folderlist-file-c .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-cfm .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-cgi .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-com .av-folderlist-label:after { + background-position: 0px center; +} + +.av-folderlist .av-folderlist-file-cpp .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-css .av-folderlist-label:after { + background-position: -32px center; +} + +.av-folderlist .av-folderlist-file-less .av-folderlist-label:after { + background-position: -32px center; +} + +.av-folderlist .av-folderlist-file-sass .av-folderlist-label:after { + background-position: -32px center; +} + +.av-folderlist .av-folderlist-file-scss .av-folderlist-label:after { + background-position: -32px center; +} + +.av-folderlist .av-folderlist-file-doc .av-folderlist-label:after { + background-position: -80px center; +} + +.av-folderlist .av-folderlist-file-docx .av-folderlist-label:after { + background-position: -80px center; +} + +.av-folderlist .av-folderlist-file-exe .av-folderlist-label:after { + background-position: 0px center; +} + +.av-folderlist .av-folderlist-file-gif .av-folderlist-label:after { + background-position: -272px center; +} + +.av-folderlist .av-folderlist-file-fla .av-folderlist-label:after { + background-position: -144px center; +} + +.av-folderlist .av-folderlist-file-h .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-htm .av-folderlist-label:after { + background-position: -176px center; +} + +.av-folderlist .av-folderlist-file-html .av-folderlist-label:after { + background-position: -176px center; +} + +.av-folderlist .av-folderlist-file-jar .av-folderlist-label:after { + background-position: -192px center; +} + +.av-folderlist .av-folderlist-file-jpg .av-folderlist-label:after { + background-position: -272px center; +} + +.av-folderlist .av-folderlist-file-jpeg .av-folderlist-label:after { + background-position: -272px center; +} + +.av-folderlist .av-folderlist-file-js .av-folderlist-label:after { + background-position: -336px center; +} + +.av-folderlist .av-folderlist-file-lasso .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-log .av-folderlist-label:after { + background-position: -352px center; +} + +.av-folderlist .av-folderlist-file-m4p .av-folderlist-label:after { + background-position: -224px center; +} + +.av-folderlist .av-folderlist-file-mov .av-folderlist-label:after { + background-position: -128px center; +} + +.av-folderlist .av-folderlist-file-mp3 .av-folderlist-label:after { + background-position: -224px center; +} + +.av-folderlist .av-folderlist-file-mp4 .av-folderlist-label:after { + background-position: -128px center; +} + +.av-folderlist .av-folderlist-file-mpg .av-folderlist-label:after { + background-position: -128px center; +} + +.av-folderlist .av-folderlist-file-mpeg .av-folderlist-label:after { + background-position: -128px center; +} + +.av-folderlist .av-folderlist-file-ogg .av-folderlist-label:after { + background-position: -224px center; +} + +.av-folderlist .av-folderlist-file-pcx .av-folderlist-label:after { + background-position: -272px center; +} + +.av-folderlist .av-folderlist-file-pdf .av-folderlist-label:after { + background-position: -240px center; +} + +.av-folderlist .av-folderlist-file-php .av-folderlist-label:after { + background-position: -256px center; +} + +.av-folderlist .av-folderlist-file-png .av-folderlist-label:after { + background-position: -272px center; +} + +.av-folderlist .av-folderlist-file-ppt .av-folderlist-label:after { + background-position: -288px center; +} + +.av-folderlist .av-folderlist-file-pptx .av-folderlist-label:after { + background-position: -288px center; +} + +.av-folderlist .av-folderlist-file-psd .av-folderlist-label:after { + background-position: -288px center; +} + +.av-folderlist .av-folderlist-file-cdr .av-folderlist-label:after { + background-position: -288px center; +} + +.av-folderlist .av-folderlist-file-ai .av-folderlist-label:after { + background-position: -288px center; +} + +.av-folderlist .av-folderlist-file-pl .av-folderlist-label:after { + background-position: -336px center; +} + +.av-folderlist .av-folderlist-file-py .av-folderlist-label:after { + background-position: -336px center; +} + +.av-folderlist .av-folderlist-file-rb .av-folderlist-label:after { + background-position: -320px center; +} + +.av-folderlist .av-folderlist-file-rbx .av-folderlist-label:after { + background-position: -320px center; +} + +.av-folderlist .av-folderlist-file-rhtml .av-folderlist-label:after { + background-position: -320px center; +} + +.av-folderlist .av-folderlist-file-rpm .av-folderlist-label:after { + background-position: -208px center; +} + +.av-folderlist .av-folderlist-file-ruby .av-folderlist-label:after { + background-position: -320px center; +} + +.av-folderlist .av-folderlist-file-sql .av-folderlist-label:after { + background-position: -48px center; +} + +.av-folderlist .av-folderlist-file-swf .av-folderlist-label:after { + background-position: -144px center; +} + +.av-folderlist .av-folderlist-file-tif .av-folderlist-label:after { + background-position: -272px center; +} + +.av-folderlist .av-folderlist-file-tiff .av-folderlist-label:after { + background-position: -272px center; +} + +.av-folderlist .av-folderlist-file-txt .av-folderlist-label:after { + background-position: -352px center; +} + +.av-folderlist .av-folderlist-file-vb .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-wav .av-folderlist-label:after { + background-position: -224px center; +} + +.av-folderlist .av-folderlist-file-wmv .av-folderlist-label:after { + background-position: -128px center; +} + +.av-folderlist .av-folderlist-file-xls .av-folderlist-label:after { + background-position: -368px center; +} + +.av-folderlist .av-folderlist-file-xlsx .av-folderlist-label:after { + background-position: -368px center; +} + +.av-folderlist .av-folderlist-file-xml .av-folderlist-label:after { + background-position: -16px center; +} + +.av-folderlist .av-folderlist-file-zip .av-folderlist-label:after { + background-position: -384px center; +} + +.av-folderlist .av-folderlist-file-gzip .av-folderlist-label:after { + background-position: -384px center; +} + +.av-folderlist .av-folderlist-file-7z .av-folderlist-label:after { + background-position: -384px center; +} + +.av-folderlist .av-folderlist-file-tar .av-folderlist-label:after { + background-position: -384px center; +} + +.av-folderlist .av-folderlist-file-rar .av-folderlist-label:after { + background-position: -384px center; +} + +.av-folderlist li.open>.av-folderlist-tree:after { + opacity: 0; +} + +.av-folderlist li.open.av-folderlist-dir>.av-folderlist-label:after { + background-position: -160px center; +} diff --git a/admin/models/fields/fileselect.php b/admin/models/fields/fileselect.php new file mode 100644 index 0000000..6499952 --- /dev/null +++ b/admin/models/fields/fileselect.php @@ -0,0 +1,167 @@ +uid . '" class="av-folderlist level-0' . '">'; + $subdir = $this->showdir($dir, $folderOnly, $showRoot, $level + 1, $ef); + $name = substr(strrchr($dir, '/'), 1); + $html .= '
  • ' . ($subdir ? '' : '') . '' . $name . '' . $subdir . '
  • '; + $html .= ''; + } else { + $list = scandir($dir); + if (is_array($list)) { + $list = array_diff($list, array('.', '..')); + if ($list) { + $folders = array(); + $files = array(); + + foreach ($list as $name) { + if (is_dir($dir . DIRECTORY_SEPARATOR . $name)) { + $folders[] = $name; + } else { + $files[] = $name; + } + } + + if (!($folderOnly && !$folders) || !(!$folders || !$files)) { + $html .= ''; + } + + sort($folders); + sort($files); + + foreach ($folders as $name) { + $fpath = $dir.'/'.$name; + $subdir = $this->showdir($fpath, $folderOnly, $showRoot, $level + 1, $ef); + $fpath = str_replace('\\', '/', str_replace($ef, '', $fpath)); + $html .= '
  • ' . ($subdir ? '' : '') . '' . $name . '' . $subdir . '
  • '; + } + + if (!$folderOnly) + foreach ($files as $name) { + $fpath = $dir.'/'.$name; + $fpath = str_replace('\\', '/', str_replace($ef, '', $fpath)); + $ext = substr(strrchr($name, '.'), 1); + $html .= '
  • ' . $name . '
  • '; + } + + if (!($folderOnly && !$folders) || !(!$folders || !$files)) { + $html .= ''; + } + + unset($folders, $files, $fpath, $ext); + } + } + } + + return $html; + } + + protected function getInput() + { + // include css + $path = str_replace('\\', '/', str_replace(JPATH_ROOT.'/', '', __DIR__)); + JHtml::_('stylesheet', $path.'/fileselect.css'); + + // get attributes + $folder = $this->getAttribute('folder'); + $folder = ($folder && file_exists(realpath(JPATH_ROOT.'/'.$folder)) ? $folder : 'images'); + + $folderOnly = $this->getAttribute('folderonly'); + $folderOnly = ($folderOnly && (strtolower($folderOnly) === 'true' || strtolower($folderOnly) === 'folderonly') ? true : false); + + $showRoot = $this->getAttribute('showroot'); + $showRoot = ($showRoot && (strtolower($showRoot) === 'true' || strtolower($showRoot) === 'showroot') ? true : false); + + $color = $this->getAttribute('color'); + + // get uniq id + $this->uid = uniqid('avfl'); + + // make html + $html = '
    '; + + // input + $html .= '
    '; + $html .= 'class ? ' class="' . $this->class . '"' : '') . ' value="' . $this->value . '" placeholder="' . JText::_('Select file or folder') . '" readonly ' . ($this->required ? 'required' : '') . '/>'; + if (!$this->readonly) + $html .= ''; + + // modal + $html .= '
    '; + $html .= $this->showdir(JPATH_ROOT.'/'.$folder, $folderOnly, $showRoot); + $html .= '
    '; + $html .= '
    '; + + $script = " + document.addEventListener('DOMContentLoaded', function() { + + document.getElementById('" . $this->uid . "btn').addEventListener('click', function(e) { + document.getElementById('" . $this->uid . "modal').classList.toggle('open'); + }); + + document.querySelectorAll('#" . $this->uid . " .av-folderlist-tree').forEach(function(el) { + el.addEventListener('click', function(e) { + e.target.parentNode.classList.toggle('open'); + }); + }); + + document.querySelectorAll('#" . $this->uid . " .av-folderlist-label').forEach(function(el) { + el.addEventListener('click', function(e) { + var + target = e.target; + list = e.target.closest('.av-folderlist'); + + list.querySelectorAll('.av-folderlist-label').forEach(function(elm) { + elm.classList.remove('selected'); + }); + + document.getElementById('" . $this->id . "').value = target.getAttribute('path'); + target.classList.add('selected'); + if (!target.parentNode.classList.contains('open')) { + target.parentNode.classList.add('open'); + } + }); + }); + + }); + "; + JFactory::getDocument()->addScriptDeclaration($script); + + $html .= '
    '; + + return $html; + } +} diff --git a/admin/models/fields/index.html b/admin/models/fields/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/models/fields/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/models/fields/keysfilteruser.php b/admin/models/fields/keysfilteruser.php new file mode 100644 index 0000000..bd38059 --- /dev/null +++ b/admin/models/fields/keysfilteruser.php @@ -0,0 +1,75 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Keysfilteruser Form Field class for the Extensiondistributor component + */ +class JFormFieldKeysfilteruser extends JFormFieldList +{ + /** + * The keysfilteruser field type. + * + * @var string + */ + public $type = 'keysfilteruser'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('user')); + $query->from($db->quoteName('#__extensiondistributor_key')); + $query->order($db->quoteName('user') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + + if ($results) + { + $results = array_unique($results); + foreach ($results as $user) + { + // Now add the user and its text to the options array + $_filter[] = JHtml::_('select.option', $user, JFactory::getUser($user)->name); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/packagekey.php b/admin/models/fields/packagekey.php new file mode 100644 index 0000000..a2c59fc --- /dev/null +++ b/admin/models/fields/packagekey.php @@ -0,0 +1,186 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Packagekey Form Field class for the Extensiondistributor component + */ +class JFormFieldPackagekey extends JFormFieldList +{ + /** + * The packagekey field type. + * + * @var string + */ + public $type = 'packagekey'; + + /** + * Override to add new button + * + * @return string The field input markup. + * + * @since 3.2 + */ + protected function getInput() + { + // see if we should add buttons + $set_button = $this->getAttribute('button'); + // get html + $html = parent::getInput(); + // if true set button + if ($set_button === 'true') + { + $button = array(); + $script = array(); + $button_code_name = $this->getAttribute('name'); + // get the input from url + $app = JFactory::getApplication(); + $jinput = $app->input; + // get the view name & id + $values = $jinput->getArray(array( + 'id' => 'int', + 'view' => 'word' + )); + // check if new item + $ref = ''; + $refJ = ''; + if (!is_null($values['id']) && strlen($values['view'])) + { + // only load referral if not new item. + $ref = '&ref=' . $values['view'] . '&refid=' . $values['id']; + $refJ = '&ref=' . $values['view'] . '&refid=' . $values['id']; + // get the return value. + $_uri = (string) JUri::getInstance(); + $_return = urlencode(base64_encode($_uri)); + // load return value. + $ref .= '&return=' . $_return; + $refJ .= '&return=' . $_return; + } + // get button label + $button_label = trim($button_code_name); + $button_label = preg_replace('/_+/', ' ', $button_label); + $button_label = preg_replace('/\s+/', ' ', $button_label); + $button_label = preg_replace("/[^A-Za-z ]/", '', $button_label); + $button_label = ucfirst(strtolower($button_label)); + // get user object + $user = JFactory::getUser(); + // only add if user allowed to create package + if ($user->authorise('core.create', 'com_extensiondistributor') && $app->isAdmin()) // TODO for now only in admin area. + { + // build Create button + $button[] = ' + '; + } + // only add if user allowed to edit package + if ($user->authorise('core.edit', 'com_extensiondistributor') && $app->isAdmin()) // TODO for now only in admin area. + { + // build edit button + $button[] = ''; + // build script + $script[] = " + jQuery(document).ready(function() { + jQuery('#adminForm').on('change', '#jform_".$button_code_name."',function (e) { + e.preventDefault(); + var ".$button_code_name."Value = jQuery('#jform_".$button_code_name."').val(); + ".$button_code_name."Button(".$button_code_name."Value); + }); + var ".$button_code_name."Value = jQuery('#jform_".$button_code_name."').val(); + ".$button_code_name."Button(".$button_code_name."Value); + }); + function ".$button_code_name."Button(value) { + if (value > 0) { + // hide the create button + jQuery('#".$button_code_name."Create').hide(); + // show edit button + jQuery('#".$button_code_name."Edit').show(); + var url = 'index.php?option=com_extensiondistributor&view=packages&task=package.edit&id='+value+'".$refJ."'; + jQuery('#".$button_code_name."Edit').attr('href', url); + } else { + // show the create button + jQuery('#".$button_code_name."Create').show(); + // hide edit button + jQuery('#".$button_code_name."Edit').hide(); + } + }"; + } + // check if button was created for package field. + if (is_array($button) && count($button) > 0) + { + // Load the needed script. + $document = JFactory::getDocument(); + $document->addScriptDeclaration(implode(' ',$script)); + // return the button attached to input field. + return '
    ' .$html . implode('',$button).'
    '; + } + } + return $html; + } + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get the user object. + $user = JFactory::getUser(); + // Get the databse object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + $query->select($db->quoteName(array('a.id','a.name'),array('id','package_name'))); + $query->from($db->quoteName('#__extensiondistributor_package', 'a')); + $query->where($db->quoteName('a.published') . ' = 1'); + $query->order('a.name ASC'); + // Implement View Level Access (if set in table) + if (!$user->authorise('core.options', 'com_extensiondistributor')) + { + $columns = $db->getTableColumns('#__extensiondistributor_package'); + if(isset($columns['access'])) + { + $groups = implode(',', $user->getAuthorisedViewLevels()); + $query->where('a.access IN (' . $groups . ')'); + } + } + $db->setQuery((string)$query); + $items = $db->loadObjectList(); + $options = array(); + if ($items) + { + if ($this->multiple === false) + { + $options[] = JHtml::_('select.option', '', JText::_('COM_EXTENSIONDISTRIBUTOR_SELECT_AN_OPTION')); + } + foreach($items as $item) + { + $options[] = JHtml::_('select.option', $item->id, $item->package_name); + } + } + return $options; + } +} diff --git a/admin/models/fields/packagekeys.php b/admin/models/fields/packagekeys.php new file mode 100644 index 0000000..d7960ac --- /dev/null +++ b/admin/models/fields/packagekeys.php @@ -0,0 +1,83 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Packagekeys Form Field class for the Extensiondistributor component + */ +class JFormFieldPackagekeys extends JFormFieldList +{ + /** + * The packagekeys field type. + * + * @var string + */ + public $type = 'packagekeys'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get the user object. + $user = JFactory::getUser(); + // Get the databse object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + $query->select($db->quoteName(array('a.id','a.name'),array('id','package_name'))); + $query->from($db->quoteName('#__extensiondistributor_package', 'a')); + $query->where($db->quoteName('a.published') . ' = 1'); + $query->order('a.name ASC'); + // Implement View Level Access (if set in table) + if (!$user->authorise('core.options', 'com_extensiondistributor')) + { + $columns = $db->getTableColumns('#__extensiondistributor_package'); + if(isset($columns['access'])) + { + $groups = implode(',', $user->getAuthorisedViewLevels()); + $query->where('a.access IN (' . $groups . ')'); + } + } + $db->setQuery((string)$query); + $items = $db->loadObjectList(); + $options = array(); + if ($items) + { + if ($this->multiple === false) + { + $options[] = JHtml::_('select.option', '', JText::_('COM_EXTENSIONDISTRIBUTOR_SELECT_AN_OPTION')); + $options[] = JHtml::_('select.option', 'All', JText::_('COM_EXTENSIONDISTRIBUTOR_ALL_PACKAGES')); + } + foreach($items as $item) + { + $options[] = JHtml::_('select.option', $item->id, $item->package_name); + } + } + return $options; + } +} diff --git a/admin/models/fields/packagelog.php b/admin/models/fields/packagelog.php new file mode 100644 index 0000000..9d4e6e6 --- /dev/null +++ b/admin/models/fields/packagelog.php @@ -0,0 +1,82 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Packagelog Form Field class for the Extensiondistributor component + */ +class JFormFieldPackagelog extends JFormFieldList +{ + /** + * The packagelog field type. + * + * @var string + */ + public $type = 'packagelog'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get the user object. + $user = JFactory::getUser(); + // Get the databse object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + $query->select($db->quoteName(array('a.id','a.name'),array('id','package_name'))); + $query->from($db->quoteName('#__extensiondistributor_package', 'a')); + $query->where($db->quoteName('a.published') . ' = 1'); + $query->order('a.name ASC'); + // Implement View Level Access (if set in table) + if (!$user->authorise('core.options', 'com_extensiondistributor')) + { + $columns = $db->getTableColumns('#__extensiondistributor_package'); + if(isset($columns['access'])) + { + $groups = implode(',', $user->getAuthorisedViewLevels()); + $query->where('a.access IN (' . $groups . ')'); + } + } + $db->setQuery((string)$query); + $items = $db->loadObjectList(); + $options = array(); + if ($items) + { + if ($this->multiple === false) + { + $options[] = JHtml::_('select.option', '', JText::_('COM_EXTENSIONDISTRIBUTOR_SELECT_AN_OPTION')); + } + foreach($items as $item) + { + $options[] = JHtml::_('select.option', $item->id, $item->package_name); + } + } + return $options; + } +} diff --git a/admin/models/fields/packagesfilterfree.php b/admin/models/fields/packagesfilterfree.php new file mode 100644 index 0000000..112085e --- /dev/null +++ b/admin/models/fields/packagesfilterfree.php @@ -0,0 +1,80 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Packagesfilterfree Form Field class for the Extensiondistributor component + */ +class JFormFieldPackagesfilterfree extends JFormFieldList +{ + /** + * The packagesfilterfree field type. + * + * @var string + */ + public $type = 'packagesfilterfree'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('free')); + $query->from($db->quoteName('#__extensiondistributor_package')); + $query->order($db->quoteName('free') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + $_filter[] = JHtml::_('select.option', '', '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_FREE') . ' -'); + + if ($results) + { + // get packagesmodel + $model = ExtensiondistributorHelper::getModel('packages'); + $results = array_unique($results); + foreach ($results as $free) + { + // Translate the free selection + $text = $model->selectionTranslation($free,'free'); + // Now add the free and its text to the options array + $_filter[] = JHtml::_('select.option', $free, JText::_($text)); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/releasesfilterreltype.php b/admin/models/fields/releasesfilterreltype.php new file mode 100644 index 0000000..d942321 --- /dev/null +++ b/admin/models/fields/releasesfilterreltype.php @@ -0,0 +1,79 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Releasesfilterreltype Form Field class for the Extensiondistributor component + */ +class JFormFieldReleasesfilterreltype extends JFormFieldList +{ + /** + * The releasesfilterreltype field type. + * + * @var string + */ + public $type = 'releasesfilterreltype'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('reltype')); + $query->from($db->quoteName('#__extensiondistributor_release')); + $query->order($db->quoteName('reltype') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + + if ($results) + { + // get releasesmodel + $model = ExtensiondistributorHelper::getModel('releases'); + $results = array_unique($results); + foreach ($results as $reltype) + { + // Translate the reltype selection + $text = $model->selectionTranslation($reltype,'reltype'); + // Now add the reltype and its text to the options array + $_filter[] = JHtml::_('select.option', $reltype, JText::_($text)); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/releasesfilterstability.php b/admin/models/fields/releasesfilterstability.php new file mode 100644 index 0000000..c3b1d18 --- /dev/null +++ b/admin/models/fields/releasesfilterstability.php @@ -0,0 +1,79 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Releasesfilterstability Form Field class for the Extensiondistributor component + */ +class JFormFieldReleasesfilterstability extends JFormFieldList +{ + /** + * The releasesfilterstability field type. + * + * @var string + */ + public $type = 'releasesfilterstability'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('stability')); + $query->from($db->quoteName('#__extensiondistributor_release')); + $query->order($db->quoteName('stability') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + + if ($results) + { + // get releasesmodel + $model = ExtensiondistributorHelper::getModel('releases'); + $results = array_unique($results); + foreach ($results as $stability) + { + // Translate the stability selection + $text = $model->selectionTranslation($stability,'stability'); + // Now add the stability and its text to the options array + $_filter[] = JHtml::_('select.option', $stability, JText::_($text)); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/subscriptionsfilteractive.php b/admin/models/fields/subscriptionsfilteractive.php new file mode 100644 index 0000000..1c439ae --- /dev/null +++ b/admin/models/fields/subscriptionsfilteractive.php @@ -0,0 +1,80 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Subscriptionsfilteractive Form Field class for the Extensiondistributor component + */ +class JFormFieldSubscriptionsfilteractive extends JFormFieldList +{ + /** + * The subscriptionsfilteractive field type. + * + * @var string + */ + public $type = 'subscriptionsfilteractive'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('active')); + $query->from($db->quoteName('#__extensiondistributor_subscription')); + $query->order($db->quoteName('active') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + $_filter[] = JHtml::_('select.option', '', '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_ACTIVE') . ' -'); + + if ($results) + { + // get subscriptionsmodel + $model = ExtensiondistributorHelper::getModel('subscriptions'); + $results = array_unique($results); + foreach ($results as $active) + { + // Translate the active selection + $text = $model->selectionTranslation($active,'active'); + // Now add the active and its text to the options array + $_filter[] = JHtml::_('select.option', $active, JText::_($text)); + } + } + return $_filter; + } +} diff --git a/admin/models/fields/subscriptionsfilteruser.php b/admin/models/fields/subscriptionsfilteruser.php new file mode 100644 index 0000000..6f0e378 --- /dev/null +++ b/admin/models/fields/subscriptionsfilteruser.php @@ -0,0 +1,75 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +// import the list field type +jimport('joomla.form.helper'); +JFormHelper::loadFieldClass('list'); + +/** + * Subscriptionsfilteruser Form Field class for the Extensiondistributor component + */ +class JFormFieldSubscriptionsfilteruser extends JFormFieldList +{ + /** + * The subscriptionsfilteruser field type. + * + * @var string + */ + public $type = 'subscriptionsfilteruser'; + + /** + * Method to get a list of options for a list input. + * + * @return array An array of JHtml options. + */ + protected function getOptions() + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Select the text. + $query->select($db->quoteName('user')); + $query->from($db->quoteName('#__extensiondistributor_subscription')); + $query->order($db->quoteName('user') . ' ASC'); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + + $results = $db->loadColumn(); + $_filter = array(); + + if ($results) + { + $results = array_unique($results); + foreach ($results as $user) + { + // Now add the user and its text to the options array + $_filter[] = JHtml::_('select.option', $user, JFactory::getUser($user)->name); + } + } + return $_filter; + } +} diff --git a/admin/models/forms/download.xml b/admin/models/forms/download.xml new file mode 100644 index 0000000..456009a --- /dev/null +++ b/admin/models/forms/download.xml @@ -0,0 +1,157 @@ + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + + +
    +
    \ No newline at end of file diff --git a/admin/models/forms/extension.xml b/admin/models/forms/extension.xml new file mode 100644 index 0000000..5f17550 --- /dev/null +++ b/admin/models/forms/extension.xml @@ -0,0 +1,275 @@ + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + + +
    +
    \ No newline at end of file diff --git a/admin/models/forms/filter_downloads.xml b/admin/models/forms/filter_downloads.xml new file mode 100644 index 0000000..c7247b2 --- /dev/null +++ b/admin/models/forms/filter_downloads.xml @@ -0,0 +1,81 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/admin/models/forms/filter_extensions.xml b/admin/models/forms/filter_extensions.xml new file mode 100644 index 0000000..22116dc --- /dev/null +++ b/admin/models/forms/filter_extensions.xml @@ -0,0 +1,97 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/admin/models/forms/filter_keys.xml b/admin/models/forms/filter_keys.xml new file mode 100644 index 0000000..bcc5e45 --- /dev/null +++ b/admin/models/forms/filter_keys.xml @@ -0,0 +1,64 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/admin/models/forms/filter_packages.xml b/admin/models/forms/filter_packages.xml new file mode 100644 index 0000000..266bb58 --- /dev/null +++ b/admin/models/forms/filter_packages.xml @@ -0,0 +1,85 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/admin/models/forms/filter_releases.xml b/admin/models/forms/filter_releases.xml new file mode 100644 index 0000000..bf0be6f --- /dev/null +++ b/admin/models/forms/filter_releases.xml @@ -0,0 +1,98 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/admin/models/forms/filter_subscriptions.xml b/admin/models/forms/filter_subscriptions.xml new file mode 100644 index 0000000..a9a9e59 --- /dev/null +++ b/admin/models/forms/filter_subscriptions.xml @@ -0,0 +1,85 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    \ No newline at end of file diff --git a/admin/models/forms/index.html b/admin/models/forms/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/models/forms/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/models/forms/key.xml b/admin/models/forms/key.xml new file mode 100644 index 0000000..2de58e5 --- /dev/null +++ b/admin/models/forms/key.xml @@ -0,0 +1,137 @@ + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + + +
    +
    \ No newline at end of file diff --git a/admin/models/forms/package.xml b/admin/models/forms/package.xml new file mode 100644 index 0000000..cef5a70 --- /dev/null +++ b/admin/models/forms/package.xml @@ -0,0 +1,649 @@ + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + + + + + + +
    +
    + + +
    + + + + +
    + \ No newline at end of file diff --git a/admin/models/forms/release.xml b/admin/models/forms/release.xml new file mode 100644 index 0000000..0e45c13 --- /dev/null +++ b/admin/models/forms/release.xml @@ -0,0 +1,296 @@ + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + + +
    + \ No newline at end of file diff --git a/admin/models/forms/subscription.xml b/admin/models/forms/subscription.xml new file mode 100644 index 0000000..6cd5471 --- /dev/null +++ b/admin/models/forms/subscription.xml @@ -0,0 +1,166 @@ + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + + +
    +
    \ No newline at end of file diff --git a/admin/models/import.php b/admin/models/import.php new file mode 100644 index 0000000..0a7080d --- /dev/null +++ b/admin/models/import.php @@ -0,0 +1,159 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ItemModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Import Item Model + */ +class ExtensiondistributorModelImport extends ItemModel +{ + /** + * Model context string. + * + * @var string + */ + protected $_context = 'com_extensiondistributor.import'; + + /** + * Model user data. + * + * @var strings + */ + protected $user; + protected $userId; + protected $guest; + protected $groups; + protected $levels; + protected $app; + protected $input; + protected $uikitComp; + + /** + * @var object item + */ + protected $item; + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @since 1.6 + * + * @return void + */ + protected function populateState() + { + $this->app = JFactory::getApplication(); + $this->input = $this->app->input; + // Get the item main id + $id = $this->input->getInt('id', null); + $this->setState('import.id', $id); + + // Load the parameters. + parent::populateState(); + } + + /** + * Method to get article data. + * + * @param integer $pk The id of the article. + * + * @return mixed Menu item data object on success, false on failure. + */ + public function getItem($pk = null) + { + $this->user = JFactory::getUser(); + // check if this user has permission to access item + if (!$this->user->authorise('import.access', 'com_extensiondistributor')) + { + $app = JFactory::getApplication(); + $app->enqueueMessage(JText::_('Not authorised!'), 'error'); + // redirect away if not a correct to cPanel/default view + $app->redirect('index.php?option=com_extensiondistributor'); + return false; + } + $this->userId = $this->user->get('id'); + $this->guest = $this->user->get('guest'); + $this->groups = $this->user->get('groups'); + $this->authorisedGroups = $this->user->getAuthorisedGroups(); + $this->levels = $this->user->getAuthorisedViewLevels(); + $this->initSet = true; + + $pk = (!empty($pk)) ? $pk : (int) $this->getState('import.id'); + + if ($this->_item === null) + { + $this->_item = array(); + } + + if (!isset($this->_item[$pk])) + { + try + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Get data +return false; + + // Reset the query using our newly populated query object. + $db->setQuery($query); + // Load the results as a stdClass object. + $data = $db->loadObject(); + + if (empty($data)) + { + $app = JFactory::getApplication(); + // If no data is found redirect to default page and show warning. + $app->enqueueMessage(JText::_('COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED'), 'warning'); + $app->redirect('index.php?option=com_extensiondistributor'); + return false; + } + + // set data object to item. + $this->_item[$pk] = $data; + } + catch (Exception $e) + { + if ($e->getCode() == 404) + { + // Need to go thru the error handler to allow Redirect to work. + JError::raiseWarning(404, $e->getMessage()); + } + else + { + $this->setError($e); + $this->_item[$pk] = false; + } + } + } + + return $this->_item[$pk]; + } +} diff --git a/admin/models/index.html b/admin/models/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/models/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/models/key.php b/admin/models/key.php new file mode 100644 index 0000000..c3278e7 --- /dev/null +++ b/admin/models/key.php @@ -0,0 +1,885 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\AdminModel; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Key Admin Model + */ +class ExtensiondistributorModelKey extends AdminModel +{ + /** + * The tab layout fields array. + * + * @var array + */ + protected $tabLayoutFields = array( + 'details' => array( + 'left' => array( + 'user', + 'website', + 'key' + ) + ) + ); + + /** + * @var string The prefix to use with controller messages. + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + /** + * The type alias for this content type. + * + * @var string + * @since 3.2 + */ + public $typeAlias = 'com_extensiondistributor.key'; + + /** + * Returns a Table object, always creating it + * + * @param type $type The table type to instantiate + * @param string $prefix A prefix for the table class name. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JTable A database object + * + * @since 1.6 + */ + public function getTable($type = 'key', $prefix = 'ExtensiondistributorTable', $config = array()) + { + // add table path for when model gets used from other component + $this->addTablePath(JPATH_ADMINISTRATOR . '/components/com_extensiondistributor/tables'); + // get instance of the table + return JTable::getInstance($type, $prefix, $config); + } + + /** + * Method to get a single record. + * + * @param integer $pk The id of the primary key. + * + * @return mixed Object on success, false on failure. + * + * @since 1.6 + */ + public function getItem($pk = null) + { + if ($item = parent::getItem($pk)) + { + if (!empty($item->params) && !is_array($item->params)) + { + // Convert the params field to an array. + $registry = new Registry; + $registry->loadString($item->params); + $item->params = $registry->toArray(); + } + + if (!empty($item->metadata)) + { + // Convert the metadata field to an array. + $registry = new Registry; + $registry->loadString($item->metadata); + $item->metadata = $registry->toArray(); + } + + if (empty($item->key)) { + $item->key = ExtensiondistributorHelper::randomkey(40); + } + + } + + return $item; + } + + /** + * Method to get the record form. + * + * @param array $data Data for the form. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * @param array $options Optional array of options for the form creation. + * + * @return mixed A JForm object on success, false on failure + * + * @since 1.6 + */ + public function getForm($data = array(), $loadData = true, $options = array('control' => 'jform')) + { + // set load data option + $options['load_data'] = $loadData; + // check if xpath was set in options + $xpath = false; + if (isset($options['xpath'])) + { + $xpath = $options['xpath']; + unset($options['xpath']); + } + // check if clear form was set in options + $clear = false; + if (isset($options['clear'])) + { + $clear = $options['clear']; + unset($options['clear']); + } + + // Get the form. + $form = $this->loadForm('com_extensiondistributor.key', 'key', $options, $clear, $xpath); + + if (empty($form)) + { + return false; + } + + $jinput = JFactory::getApplication()->input; + + // The front end calls this model and uses a_id to avoid id clashes so we need to check for that first. + if ($jinput->get('a_id')) + { + $id = $jinput->get('a_id', 0, 'INT'); + } + // The back end uses id so we use that the rest of the time and set it to 0 by default. + else + { + $id = $jinput->get('id', 0, 'INT'); + } + + $user = JFactory::getUser(); + + // Check for existing item. + // Modify the form based on Edit State access controls. + if ($id != 0 && (!$user->authorise('core.edit.state', 'com_extensiondistributor.key.' . (int) $id)) + || ($id == 0 && !$user->authorise('core.edit.state', 'com_extensiondistributor'))) + { + // Disable fields for display. + $form->setFieldAttribute('ordering', 'disabled', 'true'); + $form->setFieldAttribute('published', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('ordering', 'filter', 'unset'); + $form->setFieldAttribute('published', 'filter', 'unset'); + } + // If this is a new item insure the greated by is set. + if (0 == $id) + { + // Set the created_by to this user + $form->setValue('created_by', null, $user->id); + } + // Modify the form based on Edit Creaded By access controls. + if (!$user->authorise('core.edit.created_by', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created_by', 'disabled', 'true'); + // Disable fields for display. + $form->setFieldAttribute('created_by', 'readonly', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created_by', 'filter', 'unset'); + } + // Modify the form based on Edit Creaded Date access controls. + if (!$user->authorise('core.edit.created', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created', 'filter', 'unset'); + } + // Only load these values if no id is found + if (0 == $id) + { + // Set redirected view name + $redirectedView = $jinput->get('ref', null, 'STRING'); + // Set field name (or fall back to view name) + $redirectedField = $jinput->get('field', $redirectedView, 'STRING'); + // Set redirected view id + $redirectedId = $jinput->get('refid', 0, 'INT'); + // Set field id (or fall back to redirected view id) + $redirectedValue = $jinput->get('field_id', $redirectedId, 'INT'); + if (0 != $redirectedValue && $redirectedField) + { + // Now set the local-redirected field default value + $form->setValue($redirectedField, null, $redirectedValue); + } + } + $form->setFieldAttribute('key', 'readonly', 'true'); + + return $form; + } + + /** + * Method to get the script that have to be included on the form + * + * @return string script files + */ + public function getScript() + { + return 'media/com_extensiondistributor/js/key.js'; + } + + /** + * Method to test whether a record can be deleted. + * + * @param object $record A record object. + * + * @return boolean True if allowed to delete the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canDelete($record) + { + if (!empty($record->id)) + { + if ($record->published != -2) + { + return; + } + + $user = JFactory::getUser(); + // The record has been set. Check the record permissions. + return $user->authorise('core.delete', 'com_extensiondistributor.key.' . (int) $record->id); + } + return false; + } + + /** + * Method to test whether a record can have its state edited. + * + * @param object $record A record object. + * + * @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canEditState($record) + { + $user = JFactory::getUser(); + $recordId = (!empty($record->id)) ? $record->id : 0; + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit.state', 'com_extensiondistributor.key.' . (int) $recordId); + if (!$permission && !is_null($permission)) + { + return false; + } + } + // In the absense of better information, revert to the component permissions. + return parent::canEditState($record); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * @since 2.5 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // Check specific edit permission then general edit permission. + + return JFactory::getUser()->authorise('core.edit', 'com_extensiondistributor.key.'. ((int) isset($data[$key]) ? $data[$key] : 0)) or parent::allowEdit($data, $key); + } + + /** + * Prepare and sanitise the table data prior to saving. + * + * @param JTable $table A JTable object. + * + * @return void + * + * @since 1.6 + */ + protected function prepareTable($table) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if (isset($table->name)) + { + $table->name = htmlspecialchars_decode($table->name, ENT_QUOTES); + } + + if (isset($table->alias) && empty($table->alias)) + { + $table->generateAlias(); + } + + if (empty($table->id)) + { + $table->created = $date->toSql(); + // set the user + if ($table->created_by == 0 || empty($table->created_by)) + { + $table->created_by = $user->id; + } + // Set ordering to the last item if not set + if (empty($table->ordering)) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select('MAX(ordering)') + ->from($db->quoteName('#__extensiondistributor_key')); + $db->setQuery($query); + $max = $db->loadResult(); + + $table->ordering = $max + 1; + } + } + else + { + $table->modified = $date->toSql(); + $table->modified_by = $user->id; + } + + if (!empty($table->id)) + { + // Increment the items version number. + $table->version++; + } + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 1.6 + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = JFactory::getApplication()->getUserState('com_extensiondistributor.edit.key.data', array()); + + if (empty($data)) + { + $data = $this->getItem(); + // run the perprocess of the data + $this->preprocessData('com_extensiondistributor.key', $data); + } + + return $data; + } + + /** + * Method to get the unique fields of this table. + * + * @return mixed An array of field names, boolean false if none is set. + * + * @since 3.0 + */ + protected function getUniqueFields() + { + return false; + } + + /** + * Method to delete one or more records. + * + * @param array &$pks An array of record primary keys. + * + * @return boolean True if successful, false if an error occurs. + * + * @since 12.2 + */ + public function delete(&$pks) + { + if (!parent::delete($pks)) + { + return false; + } + + return true; + } + + /** + * Method to change the published state of one or more records. + * + * @param array &$pks A list of the primary keys to change. + * @param integer $value The value of the published state. + * + * @return boolean True on success. + * + * @since 12.2 + */ + public function publish(&$pks, $value = 1) + { + if (!parent::publish($pks, $value)) + { + return false; + } + + return true; + } + + /** + * Method to perform batch operations on an item or a set of items. + * + * @param array $commands An array of commands to perform. + * @param array $pks An array of item ids. + * @param array $contexts An array of item contexts. + * + * @return boolean Returns true on success, false on failure. + * + * @since 12.2 + */ + public function batch($commands, $pks, $contexts) + { + // Sanitize ids. + $pks = array_unique($pks); + ArrayHelper::toInteger($pks); + + // Remove any values of zero. + if (array_search(0, $pks, true)) + { + unset($pks[array_search(0, $pks, true)]); + } + + if (empty($pks)) + { + $this->setError(JText::_('JGLOBAL_NO_ITEM_SELECTED')); + return false; + } + + $done = false; + + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->contentType = new JUcmType; + $this->type = $this->contentType->getTypeByTable($this->tableClassName); + $this->canDo = ExtensiondistributorHelper::getActions('key'); + $this->batchSet = true; + + if (!$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + return false; + } + + if ($this->type == false) + { + $type = new JUcmType; + $this->type = $type->getTypeByAlias($this->typeAlias); + } + + $this->tagsObserver = $this->table->getObserverOfClass('JTableObserverTags'); + + if (!empty($commands['move_copy'])) + { + $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c'); + + if ($cmd == 'c') + { + $result = $this->batchCopy($commands, $pks, $contexts); + + if (is_array($result)) + { + foreach ($result as $old => $new) + { + $contexts[$new] = $contexts[$old]; + } + $pks = array_values($result); + } + else + { + return false; + } + } + elseif ($cmd == 'm' && !$this->batchMove($commands, $pks, $contexts)) + { + return false; + } + + $done = true; + } + + if (!$done) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + + return false; + } + + // Clear the cache + $this->cleanCache(); + + return true; + } + + /** + * Batch copy items to a new category or current. + * + * @param integer $values The new values. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return mixed An array of new IDs on success, boolean false on failure. + * + * @since 12.2 + */ + protected function batchCopy($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('key'); + } + + if (!$this->canDo->get('core.create') || !$this->canDo->get('core.batch')) + { + return false; + } + + // get list of unique fields + $uniqueFields = $this->getUniqueFields(); + // remove move_copy from array + unset($values['move_copy']); + + // make sure published is set + if (!isset($values['published'])) + { + $values['published'] = 0; + } + elseif (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + $values['published'] = 0; + } + + $newIds = array(); + // Parent exists so let's proceed + while (!empty($pks)) + { + // Pop the first ID off the stack + $pk = array_shift($pks); + + $this->table->reset(); + + // only allow copy if user may edit this item. + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // Only for strings + if (ExtensiondistributorHelper::checkString($this->table->key) && !is_numeric($this->table->key)) + { + $this->table->key = $this->generateUnique('key',$this->table->key); + } + + // insert all set values + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + if (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + // update all unique fields + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $this->table->$uniqueField = $this->generateUnique($uniqueField,$this->table->$uniqueField); + } + } + + // Reset the ID because we are making a copy + $this->table->id = 0; + + // TODO: Deal with ordering? + // $this->table->ordering = 1; + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + + // Get the new item ID + $newId = $this->table->get('id'); + + // Add the new ID to the array + $newIds[$pk] = $newId; + } + + // Clean the cache + $this->cleanCache(); + + return $newIds; + } + + /** + * Batch move items to a new category + * + * @param integer $value The new category ID. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 12.2 + */ + protected function batchMove($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('key'); + } + + if (!$this->canDo->get('core.edit') && !$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // make sure published only updates if user has the permission. + if (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + unset($values['published']); + } + // remove move_copy from array + unset($values['move_copy']); + + // Parent exists so we proceed + foreach ($pks as $pk) + { + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // insert all set values. + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + // Do special action for access. + if ('access' === $key && strlen($value) > 0) + { + $this->table->$key = $value; + } + elseif (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + } + + // Clean the cache + $this->cleanCache(); + + return true; + } + + /** + * Method to save the form data. + * + * @param array $data The form data. + * + * @return boolean True on success. + * + * @since 1.6 + */ + public function save($data) + { + $input = JFactory::getApplication()->input; + $filter = JFilterInput::getInstance(); + + // set the metadata to the Item Data + if (isset($data['metadata']) && isset($data['metadata']['author'])) + { + $data['metadata']['author'] = $filter->clean($data['metadata']['author'], 'TRIM'); + + $metadata = new JRegistry; + $metadata->loadArray($data['metadata']); + $data['metadata'] = (string) $metadata; + } + + // Set the Params Items to data + if (isset($data['params']) && is_array($data['params'])) + { + $params = new JRegistry; + $params->loadArray($data['params']); + $data['params'] = (string) $params; + } + + // Alter the unique field for save as copy + if ($input->get('task') === 'save2copy') + { + // Automatic handling of other unique fields + $uniqueFields = $this->getUniqueFields(); + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $data[$uniqueField] = $this->generateUnique($uniqueField,$data[$uniqueField]); + } + } + } + + if (parent::save($data)) + { + return true; + } + return false; + } + + /** + * Method to generate a unique value. + * + * @param string $field name. + * @param string $value data. + * + * @return string New value. + * + * @since 3.0 + */ + protected function generateUnique($field,$value) + { + + // set field value unique + $table = $this->getTable(); + + while ($table->load(array($field => $value))) + { + $value = StringHelper::increment($value); + } + + return $value; + } + + /** + * Method to change the title + * + * @param string $title The title. + * + * @return array Contains the modified title and alias. + * + */ + protected function _generateNewTitle($title) + { + + // Alter the title + $table = $this->getTable(); + + while ($table->load(array('title' => $title))) + { + $title = StringHelper::increment($title); + } + + return $title; + } +} diff --git a/admin/models/keys.php b/admin/models/keys.php new file mode 100644 index 0000000..6b6e732 --- /dev/null +++ b/admin/models/keys.php @@ -0,0 +1,304 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ListModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Keys List Model + */ +class ExtensiondistributorModelKeys extends ListModel +{ + public function __construct($config = array()) + { + if (empty($config['filter_fields'])) + { + $config['filter_fields'] = array( + 'a.id','id', + 'a.published','published', + 'a.ordering','ordering', + 'a.created_by','created_by', + 'a.modified_by','modified_by', + 'a.user','user' + ); + } + + parent::__construct($config); + } + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @param string $ordering An optional ordering field. + * @param string $direction An optional direction (asc|desc). + * + * @return void + * + */ + protected function populateState($ordering = null, $direction = null) + { + $app = JFactory::getApplication(); + + // Adjust the context to support modal layouts. + if ($layout = $app->input->get('layout')) + { + $this->context .= '.' . $layout; + } + + // Check if the form was submitted + $formSubmited = $app->input->post->get('form_submited'); + + $access = $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', 0, 'int'); + if ($formSubmited) + { + $access = $app->input->post->get('access'); + $this->setState('filter.access', $access); + } + + $published = $this->getUserStateFromRequest($this->context . '.filter.published', 'filter_published', ''); + $this->setState('filter.published', $published); + + $created_by = $this->getUserStateFromRequest($this->context . '.filter.created_by', 'filter_created_by', ''); + $this->setState('filter.created_by', $created_by); + + $created = $this->getUserStateFromRequest($this->context . '.filter.created', 'filter_created'); + $this->setState('filter.created', $created); + + $sorting = $this->getUserStateFromRequest($this->context . '.filter.sorting', 'filter_sorting', 0, 'int'); + $this->setState('filter.sorting', $sorting); + + $search = $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search'); + $this->setState('filter.search', $search); + + $user = $this->getUserStateFromRequest($this->context . '.filter.user', 'filter_user'); + if ($formSubmited) + { + $user = $app->input->post->get('user'); + $this->setState('filter.user', $user); + } + + // List state information. + parent::populateState($ordering, $direction); + } + + /** + * Method to get an array of data items. + * + * @return mixed An array of data items on success, false on failure. + */ + public function getItems() + { + // Check in items + $this->checkInNow(); + + // load parent items + $items = parent::getItems(); + + // return items + return $items; + } + + /** + * Method to build an SQL query to load the list data. + * + * @return string An SQL query + */ + protected function getListQuery() + { + // Get the user object. + $user = JFactory::getUser(); + // Create a new query object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + + // Select some fields + $query->select('a.*'); + + // From the extensiondistributor_item table + $query->from($db->quoteName('#__extensiondistributor_key', 'a')); + + // Filter by published state + $published = $this->getState('filter.published'); + if (is_numeric($published)) + { + $query->where('a.published = ' . (int) $published); + } + elseif ($published === '') + { + $query->where('(a.published = 0 OR a.published = 1)'); + } + // Filter by search. + $search = $this->getState('filter.search'); + if (!empty($search)) + { + if (stripos($search, 'id:') === 0) + { + $query->where('a.id = ' . (int) substr($search, 3)); + } + else + { + $search = $db->quote('%' . $db->escape($search) . '%'); + $query->where('(a.key LIKE '.$search.' OR a.website LIKE '.$search.' OR a.user LIKE '.$search.')'); + } + } + + // Filter by User. + $_user = $this->getState('filter.user'); + if (is_numeric($_user)) + { + if (is_float($_user)) + { + $query->where('a.user = ' . (float) $_user); + } + else + { + $query->where('a.user = ' . (int) $_user); + } + } + elseif (ExtensiondistributorHelper::checkString($_user)) + { + $query->where('a.user = ' . $db->quote($db->escape($_user))); + } + elseif (ExtensiondistributorHelper::checkArray($_user)) + { + // Secure the array for the query + $_user = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_user); + // Filter by the User Array. + $query->where('a.user IN (' . implode(',', $_user) . ')'); + } + + // Add the list ordering clause. + $orderCol = $this->state->get('list.ordering', 'a.id'); + $orderDirn = $this->state->get('list.direction', 'desc'); + if ($orderCol != '') + { + $query->order($db->escape($orderCol . ' ' . $orderDirn)); + } + + return $query; + } + + /** + * Method to get a store id based on model configuration state. + * + * @return string A store id. + * + */ + protected function getStoreId($id = '') + { + // Compile the store id. + $id .= ':' . $this->getState('filter.id'); + $id .= ':' . $this->getState('filter.search'); + $id .= ':' . $this->getState('filter.published'); + $id .= ':' . $this->getState('filter.ordering'); + $id .= ':' . $this->getState('filter.created_by'); + $id .= ':' . $this->getState('filter.modified_by'); + // Check if the value is an array + $_user = $this->getState('filter.user'); + if (ExtensiondistributorHelper::checkArray($_user)) + { + $id .= ':' . implode(':', $_user); + } + // Check if this is only an number or string + elseif (is_numeric($_user) + || ExtensiondistributorHelper::checkString($_user)) + { + $id .= ':' . $_user; + } + + return parent::getStoreId($id); + } + + /** + * Build an SQL query to checkin all items left checked out longer then a set time. + * + * @return a bool + * + */ + protected function checkInNow() + { + // Get set check in time + $time = JComponentHelper::getParams('com_extensiondistributor')->get('check_in'); + + if ($time) + { + + // Get a db connection. + $db = JFactory::getDbo(); + // Reset query. + $query = $db->getQuery(true); + $query->select('*'); + $query->from($db->quoteName('#__extensiondistributor_key')); + // Only select items that are checked out. + $query->where($db->quoteName('checked_out') . '!=0'); + $db->setQuery($query, 0, 1); + $db->execute(); + if ($db->getNumRows()) + { + // Get Yesterdays date. + $date = JFactory::getDate()->modify($time)->toSql(); + // Reset query. + $query = $db->getQuery(true); + + // Fields to update. + $fields = array( + $db->quoteName('checked_out_time') . '=\'0000-00-00 00:00:00\'', + $db->quoteName('checked_out') . '=0' + ); + + // Conditions for which records should be updated. + $conditions = array( + $db->quoteName('checked_out') . '!=0', + $db->quoteName('checked_out_time') . '<\''.$date.'\'' + ); + + // Check table. + $query->update($db->quoteName('#__extensiondistributor_key'))->set($fields)->where($conditions); + + $db->setQuery($query); + + $db->execute(); + } + } + + return false; + } +} diff --git a/admin/models/maintenance.php b/admin/models/maintenance.php new file mode 100644 index 0000000..9daff90 --- /dev/null +++ b/admin/models/maintenance.php @@ -0,0 +1,280 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ItemModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Maintenance Item Model + */ +class ExtensiondistributorModelMaintenance extends ItemModel +{ + /** + * Model context string. + * + * @var string + */ + protected $_context = 'com_extensiondistributor.maintenance'; + + /** + * Model user data. + * + * @var strings + */ + protected $user; + protected $userId; + protected $guest; + protected $groups; + protected $levels; + protected $app; + protected $input; + protected $uikitComp; + + /** + * @var object item + */ + protected $item; + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @since 1.6 + * + * @return void + */ + protected function populateState() + { + $this->app = JFactory::getApplication(); + $this->input = $this->app->input; + // Get the item main id + $id = $this->input->getInt('id', null); + $this->setState('maintenance.id', $id); + + // Load the parameters. + parent::populateState(); + } + + /** + * Method to get article data. + * + * @param integer $pk The id of the article. + * + * @return mixed Menu item data object on success, false on failure. + */ + public function getItem($pk = null) + { + $this->user = JFactory::getUser(); + // check if this user has permission to access item + if (!$this->user->authorise('maintenance.access', 'com_extensiondistributor')) + { + $app = JFactory::getApplication(); + $app->enqueueMessage(JText::_('Not authorised!'), 'error'); + // redirect away if not a correct to cPanel/default view + $app->redirect('index.php?option=com_extensiondistributor'); + return false; + } + $this->userId = $this->user->get('id'); + $this->guest = $this->user->get('guest'); + $this->groups = $this->user->get('groups'); + $this->authorisedGroups = $this->user->getAuthorisedGroups(); + $this->levels = $this->user->getAuthorisedViewLevels(); + $this->initSet = true; + + $pk = (!empty($pk)) ? $pk : (int) $this->getState('maintenance.id'); + + if ($this->_item === null) + { + $this->_item = array(); + } + + if (!isset($this->_item[$pk])) + { + try + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Get data +return false; + + // Reset the query using our newly populated query object. + $db->setQuery($query); + // Load the results as a stdClass object. + $data = $db->loadObject(); + + if (empty($data)) + { + $app = JFactory::getApplication(); + // If no data is found redirect to default page and show warning. + $app->enqueueMessage(JText::_('COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED'), 'warning'); + $app->redirect('index.php?option=com_extensiondistributor'); + return false; + } + + // set data object to item. + $this->_item[$pk] = $data; + } + catch (Exception $e) + { + if ($e->getCode() == 404) + { + // Need to go thru the error handler to allow Redirect to work. + JError::raiseWarning(404, $e->getMessage()); + } + else + { + $this->setError($e); + $this->_item[$pk] = false; + } + } + } + + return $this->_item[$pk]; + } + + public function getDuplicates() + { + // Get files associated with releaseID + $files = ExtensiondistributorHelper::getReleaseToFile(); + // Find Duplicates + $duplicates = array(); + foreach(array_count_values($files) as $val => $c) { + if($c > 1) $duplicates[] = $val; + } + + return $duplicates; + } + + public function getOrphans() + { + global $cparams; + $path = JPATH_SITE . '/' . $cparams->get('releases_directory','packages') . '/'; + $path = JPath::clean($path); + $orphans = array(); + // Get files associated with releaseID + $files = ExtensiondistributorHelper::getReleaseToFile(); + // Find Orphans + $allfiles = JFolder::files($path,'.zip'); + if (ExtensiondistributorHelper::checkArray($allfiles)) { + $orphans = array_diff($allfiles,$files); + } + + return $orphans; + } + + public function getMissing() + { + global $cparams; + $path = JPATH_SITE . '/' . $cparams->get('releases_directory','packages') . '/'; + $path = JPath::clean($path); + $missing = array(); + // Get files associated with releaseID + $files = ExtensiondistributorHelper::getReleaseToFile(); + // Find Orphans + $allfiles = JFolder::files($path,'.zip'); + if (ExtensiondistributorHelper::checkArray($allfiles)) { + $missing = array_diff($files,$allfiles); + } + + return $missing; + } + + public function getDirectoryForm() + { + global $cparams; + // start the form + $form = array(); + JFormHelper::addFieldPath(JPATH_COMPONENT_ADMINISTRATOR . '/models/fields'); + // sales attributes + $attributes = array( + 'type' => 'fileselect', + 'name' => 'releases_directory', + 'label' => 'Releases directory', + 'class' => 'input-xlarge', + 'color' => 'default', + 'folder' => '/', + 'folderonly' => 'true', + 'showroot' => 'false' + ); + + $form[] = ExtensiondistributorHelper::getFieldObject($attributes,$cparams->get('releases_directory','packages')); + + $attributes = array( + 'type' => 'fileselect', + 'name' => 'jcb_directory', + 'label' => 'JCB directory', + 'class' => 'input-xlarge', + 'color' => 'default', + 'folder' => '/', + 'folderonly' => 'true', + 'showroot' => 'false' + ); + $form[] = ExtensiondistributorHelper::getFieldObject($attributes,$cparams->get('jcb_directory','jcb')); + + $attributes = array( + 'type' => 'fileselect', + 'name' => 'updates_directory', + 'label' => 'XML updates directory', + 'class' => 'input-xlarge', + 'color' => 'default', + 'folder' => '/', + 'folderonly' => 'true', + 'showroot' => 'false' + ); + $form[] = ExtensiondistributorHelper::getFieldObject($attributes,$cparams->get('updates_directory','updates')); + + $attributes = array( + 'type' => 'fileselect', + 'name' => 'import_directory', + 'label' => 'Import directory', + 'class' => 'input-xlarge', + 'color' => 'default', + 'folder' => '/', + 'folderonly' => 'true', + 'showroot' => 'false' + ); + $form[] = ExtensiondistributorHelper::getFieldObject($attributes,$cparams->get('import_directory','extmanagerimport')); + + $attributes = array( + 'type' => 'radio', + 'name' => 'secure', + 'label' => 'Secure folders', + 'class' => 'btn-group btn-group-yesno span6', + 'description' => 'Add the required files to avoid unauthorized access', + 'default' => '1' + ); + $options = array( + '1' => 'Yes', + '0' => 'No' + ); + $form[] = ExtensiondistributorHelper::getFieldObject($attributes,1,$options); + + return $form; + } + +} diff --git a/admin/models/package.php b/admin/models/package.php new file mode 100644 index 0000000..8b46f43 --- /dev/null +++ b/admin/models/package.php @@ -0,0 +1,1301 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\AdminModel; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Package Admin Model + */ +class ExtensiondistributorModelPackage extends AdminModel +{ + /** + * The tab layout fields array. + * + * @var array + */ + protected $tabLayoutFields = array( + 'details' => array( + 'left' => array( + 'description' + ), + 'right' => array( + 'catid', + 'image', + 'free', + 'blockchilduninstall', + 'useupdate', + 'update', + 'newfilename', + 'jed', + 'note_jcb_package', + 'upload_jcb', + 'file', + 'version_number', + 'release_date' + ), + 'above' => array( + 'name', + 'alias' + ) + ), + 'script' => array( + 'fullwidth' => array( + 'add_php_preflight_install', + 'php_preflight_install', + 'add_php_preflight_update', + 'php_preflight_update', + 'add_php_preflight_uninstall', + 'php_preflight_uninstall', + 'add_php_postflight_install', + 'php_postflight_install', + 'add_php_postflight_update', + 'php_postflight_update', + 'add_php_method_uninstall', + 'php_method_uninstall' + ) + ), + 'configuration' => array( + 'left' => array( + 'packagename', + 'author', + 'authorurl', + 'authoremail', + 'targetplatform', + 'php_minimum' + ), + 'right' => array( + 'packager', + 'packagerurl', + 'license', + 'copyright', + 'infourl' + ) + ), + 'files' => array( + 'left' => array( + 'files' + ) + ) + ); + + /** + * @var string The prefix to use with controller messages. + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + /** + * The type alias for this content type. + * + * @var string + * @since 3.2 + */ + public $typeAlias = 'com_extensiondistributor.package'; + + /** + * Returns a Table object, always creating it + * + * @param type $type The table type to instantiate + * @param string $prefix A prefix for the table class name. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JTable A database object + * + * @since 1.6 + */ + public function getTable($type = 'package', $prefix = 'ExtensiondistributorTable', $config = array()) + { + // add table path for when model gets used from other component + $this->addTablePath(JPATH_ADMINISTRATOR . '/components/com_extensiondistributor/tables'); + // get instance of the table + return JTable::getInstance($type, $prefix, $config); + } + + /** + * Method to get a single record. + * + * @param integer $pk The id of the primary key. + * + * @return mixed Object on success, false on failure. + * + * @since 1.6 + */ + public function getItem($pk = null) + { + if ($item = parent::getItem($pk)) + { + if (!empty($item->params) && !is_array($item->params)) + { + // Convert the params field to an array. + $registry = new Registry; + $registry->loadString($item->params); + $item->params = $registry->toArray(); + } + + if (!empty($item->metadata)) + { + // Convert the metadata field to an array. + $registry = new Registry; + $registry->loadString($item->metadata); + $item->metadata = $registry->toArray(); + } + + if (!empty($item->php_preflight_install)) + { + // base64 Decode php_preflight_install. + $item->php_preflight_install = base64_decode($item->php_preflight_install); + } + + if (!empty($item->php_preflight_update)) + { + // base64 Decode php_preflight_update. + $item->php_preflight_update = base64_decode($item->php_preflight_update); + } + + if (!empty($item->php_preflight_uninstall)) + { + // base64 Decode php_preflight_uninstall. + $item->php_preflight_uninstall = base64_decode($item->php_preflight_uninstall); + } + + if (!empty($item->php_postflight_install)) + { + // base64 Decode php_postflight_install. + $item->php_postflight_install = base64_decode($item->php_postflight_install); + } + + if (!empty($item->php_postflight_update)) + { + // base64 Decode php_postflight_update. + $item->php_postflight_update = base64_decode($item->php_postflight_update); + } + + if (!empty($item->php_method_uninstall)) + { + // base64 Decode php_method_uninstall. + $item->php_method_uninstall = base64_decode($item->php_method_uninstall); + } + + if (!empty($item->files)) + { + // Convert the files field to an array. + $files = new Registry; + $files->loadString($item->files); + $item->files = $files->toArray(); + } + + if ( + empty($item->update) && + isset($item->packagename) && + ExtensiondistributorHelper::checkString($item->packagename) + ) + { + $item->update = 'pkg_'.$item->packagename.'.xml'; + } + + } + $this->packagevvvv = $item->id; + + return $item; + } + + /** + * Method to get list data. + * + * @return mixed An array of data items on success, false on failure. + */ + public function getVvvreleases() + { + // Get the user object. + $user = JFactory::getUser(); + // Create a new query object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + + // Select some fields + $query->select('a.*'); + + // From the extensiondistributor_release table + $query->from($db->quoteName('#__extensiondistributor_release', 'a')); + + // From the extensiondistributor_extension table. + $query->select($db->quoteName('g.name','extension_name')); + $query->join('LEFT', $db->quoteName('#__extensiondistributor_extension', 'g') . ' ON (' . $db->quoteName('a.extension') . ' = ' . $db->quoteName('g.id') . ')'); + + // From the extensiondistributor_package table. + $query->select($db->quoteName('h.name','package_name')); + $query->join('LEFT', $db->quoteName('#__extensiondistributor_package', 'h') . ' ON (' . $db->quoteName('a.package') . ' = ' . $db->quoteName('h.id') . ')'); + + // Filter by packagevvvv global. + $packagevvvv = $this->packagevvvv; + if (is_numeric($packagevvvv )) + { + $query->where('a.package = ' . (int) $packagevvvv ); + } + elseif (is_string($packagevvvv)) + { + $query->where('a.package = ' . $db->quote($packagevvvv)); + } + else + { + $query->where('a.package = -5'); + } + + // Load the items + $db->setQuery($query); + $db->execute(); + if ($db->getNumRows()) + { + $items = $db->loadObjectList(); + + // Set values to display correctly. + if (ExtensiondistributorHelper::checkArray($items)) + { + // Get the user object if not set. + if (!isset($user) || !ExtensiondistributorHelper::checkObject($user)) + { + $user = JFactory::getUser(); + } + foreach ($items as $nr => &$item) + { + // Not needed + } + } + + // set selection value to a translatable value + if (ExtensiondistributorHelper::checkArray($items)) + { + foreach ($items as $nr => &$item) + { + // convert reltype + $item->reltype = $this->selectionTranslationVvvreleases($item->reltype, 'reltype'); + // convert stability + $item->stability = $this->selectionTranslationVvvreleases($item->stability, 'stability'); + } + } + + return $items; + } + return false; + } + + /** + * Method to convert selection values to translatable string. + * + * @return translatable string + */ + public function selectionTranslationVvvreleases($value,$name) + { + // Array of reltype language strings + if ($name === 'reltype') + { + $reltypeArray = array( + 0 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_PLEASE_SELECT', + 'security' => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_SECURITY', + 'maintenance' => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_MAINTENANCE', + 'newversion' => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_NEW_VERSION' + ); + // Now check if value is found in this array + if (isset($reltypeArray[$value]) && ExtensiondistributorHelper::checkString($reltypeArray[$value])) + { + return $reltypeArray[$value]; + } + } + // Array of stability language strings + if ($name === 'stability') + { + $stabilityArray = array( + 0 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_DEVELOPMENT', + 1 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_ALPHA', + 2 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_BETA', + 3 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_RC', + 4 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_STABLE' + ); + // Now check if value is found in this array + if (isset($stabilityArray[$value]) && ExtensiondistributorHelper::checkString($stabilityArray[$value])) + { + return $stabilityArray[$value]; + } + } + return $value; + } + + /** + * Method to get the record form. + * + * @param array $data Data for the form. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * @param array $options Optional array of options for the form creation. + * + * @return mixed A JForm object on success, false on failure + * + * @since 1.6 + */ + public function getForm($data = array(), $loadData = true, $options = array('control' => 'jform')) + { + // set load data option + $options['load_data'] = $loadData; + // check if xpath was set in options + $xpath = false; + if (isset($options['xpath'])) + { + $xpath = $options['xpath']; + unset($options['xpath']); + } + // check if clear form was set in options + $clear = false; + if (isset($options['clear'])) + { + $clear = $options['clear']; + unset($options['clear']); + } + + // Get the form. + $form = $this->loadForm('com_extensiondistributor.package', 'package', $options, $clear, $xpath); + + if (empty($form)) + { + return false; + } + + $jinput = JFactory::getApplication()->input; + + // The front end calls this model and uses a_id to avoid id clashes so we need to check for that first. + if ($jinput->get('a_id')) + { + $id = $jinput->get('a_id', 0, 'INT'); + } + // The back end uses id so we use that the rest of the time and set it to 0 by default. + else + { + $id = $jinput->get('id', 0, 'INT'); + } + + $user = JFactory::getUser(); + + // Check for existing item. + // Modify the form based on Edit State access controls. + if ($id != 0 && (!$user->authorise('core.edit.state', 'com_extensiondistributor.package.' . (int) $id)) + || ($id == 0 && !$user->authorise('core.edit.state', 'com_extensiondistributor'))) + { + // Disable fields for display. + $form->setFieldAttribute('ordering', 'disabled', 'true'); + $form->setFieldAttribute('published', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('ordering', 'filter', 'unset'); + $form->setFieldAttribute('published', 'filter', 'unset'); + } + // If this is a new item insure the greated by is set. + if (0 == $id) + { + // Set the created_by to this user + $form->setValue('created_by', null, $user->id); + } + // Modify the form based on Edit Creaded By access controls. + if (!$user->authorise('core.edit.created_by', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created_by', 'disabled', 'true'); + // Disable fields for display. + $form->setFieldAttribute('created_by', 'readonly', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created_by', 'filter', 'unset'); + } + // Modify the form based on Edit Creaded Date access controls. + if (!$user->authorise('core.edit.created', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created', 'filter', 'unset'); + } + // Only load these values if no id is found + if (0 == $id) + { + // Set redirected view name + $redirectedView = $jinput->get('ref', null, 'STRING'); + // Set field name (or fall back to view name) + $redirectedField = $jinput->get('field', $redirectedView, 'STRING'); + // Set redirected view id + $redirectedId = $jinput->get('refid', 0, 'INT'); + // Set field id (or fall back to redirected view id) + $redirectedValue = $jinput->get('field_id', $redirectedId, 'INT'); + if (0 != $redirectedValue && $redirectedField) + { + // Now set the local-redirected field default value + $form->setValue($redirectedField, null, $redirectedValue); + } + } + // We set default values in the form + global $cparams; + if (0 == $id) + { + $fields = array('author', 'authoremail', 'authorurl', 'packager', 'packagerurl', 'copyright', 'license','infourl','targetplatform','php_minimum'); + foreach ($fields as $field) + { + $form->setValue($field, null, $cparams->get($field)); + } + } + $form->setFieldAttribute('jcb', 'directory', $cparams->get('jcb_directory')); + + return $form; + } + + /** + * Method to get the script that have to be included on the form + * + * @return string script files + */ + public function getScript() + { + return 'media/com_extensiondistributor/js/package.js'; + } + + /** + * Method to test whether a record can be deleted. + * + * @param object $record A record object. + * + * @return boolean True if allowed to delete the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canDelete($record) + { + if (!empty($record->id)) + { + if ($record->published != -2) + { + return; + } + + $user = JFactory::getUser(); + // The record has been set. Check the record permissions. + return $user->authorise('core.delete', 'com_extensiondistributor.package.' . (int) $record->id); + } + return false; + } + + /** + * Method to test whether a record can have its state edited. + * + * @param object $record A record object. + * + * @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canEditState($record) + { + $user = JFactory::getUser(); + $recordId = (!empty($record->id)) ? $record->id : 0; + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit.state', 'com_extensiondistributor.package.' . (int) $recordId); + if (!$permission && !is_null($permission)) + { + return false; + } + } + // In the absense of better information, revert to the component permissions. + return parent::canEditState($record); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * @since 2.5 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // Check specific edit permission then general edit permission. + + return JFactory::getUser()->authorise('core.edit', 'com_extensiondistributor.package.'. ((int) isset($data[$key]) ? $data[$key] : 0)) or parent::allowEdit($data, $key); + } + + /** + * Prepare and sanitise the table data prior to saving. + * + * @param JTable $table A JTable object. + * + * @return void + * + * @since 1.6 + */ + protected function prepareTable($table) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if (isset($table->name)) + { + $table->name = htmlspecialchars_decode($table->name, ENT_QUOTES); + } + + if (isset($table->alias) && empty($table->alias)) + { + $table->generateAlias(); + } + + if (empty($table->id)) + { + $table->created = $date->toSql(); + // set the user + if ($table->created_by == 0 || empty($table->created_by)) + { + $table->created_by = $user->id; + } + // Set ordering to the last item if not set + if (empty($table->ordering)) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select('MAX(ordering)') + ->from($db->quoteName('#__extensiondistributor_package')); + $db->setQuery($query); + $max = $db->loadResult(); + + $table->ordering = $max + 1; + } + } + else + { + $table->modified = $date->toSql(); + $table->modified_by = $user->id; + } + + if (!empty($table->id)) + { + // Increment the items version number. + $table->version++; + } + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 1.6 + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = JFactory::getApplication()->getUserState('com_extensiondistributor.edit.package.data', array()); + + if (empty($data)) + { + $data = $this->getItem(); + // run the perprocess of the data + $this->preprocessData('com_extensiondistributor.package', $data); + } + + return $data; + } + + /** + * Method to get the unique fields of this table. + * + * @return mixed An array of field names, boolean false if none is set. + * + * @since 3.0 + */ + protected function getUniqueFields() + { + return false; + } + + /** + * Method to delete one or more records. + * + * @param array &$pks An array of record primary keys. + * + * @return boolean True if successful, false if an error occurs. + * + * @since 12.2 + */ + public function delete(&$pks) + { + if (!parent::delete($pks)) + { + return false; + } + + // We delete the associated releases + $app = JFactory::getApplication(); + foreach ($pks as $pk) { + $releases = ExtensiondistributorHelper::getReleases($pk,'package'); + $releases = array_column($releases,'id'); + $model = ExtensiondistributorHelper::getModel('release'); + try + { + $model->publish($releases, -2); + $model->delete($releases); + } + catch (Exception $e) + { + $app->enqueueMessage($e->getMessage(),'error'); + } + } + + + return true; + } + + /** + * Method to change the published state of one or more records. + * + * @param array &$pks A list of the primary keys to change. + * @param integer $value The value of the published state. + * + * @return boolean True on success. + * + * @since 12.2 + */ + public function publish(&$pks, $value = 1) + { + if (!parent::publish($pks, $value)) + { + return false; + } + + return true; + } + + /** + * Method to perform batch operations on an item or a set of items. + * + * @param array $commands An array of commands to perform. + * @param array $pks An array of item ids. + * @param array $contexts An array of item contexts. + * + * @return boolean Returns true on success, false on failure. + * + * @since 12.2 + */ + public function batch($commands, $pks, $contexts) + { + // Sanitize ids. + $pks = array_unique($pks); + ArrayHelper::toInteger($pks); + + // Remove any values of zero. + if (array_search(0, $pks, true)) + { + unset($pks[array_search(0, $pks, true)]); + } + + if (empty($pks)) + { + $this->setError(JText::_('JGLOBAL_NO_ITEM_SELECTED')); + return false; + } + + $done = false; + + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->contentType = new JUcmType; + $this->type = $this->contentType->getTypeByTable($this->tableClassName); + $this->canDo = ExtensiondistributorHelper::getActions('package'); + $this->batchSet = true; + + if (!$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + return false; + } + + if ($this->type == false) + { + $type = new JUcmType; + $this->type = $type->getTypeByAlias($this->typeAlias); + } + + $this->tagsObserver = $this->table->getObserverOfClass('JTableObserverTags'); + + if (!empty($commands['move_copy'])) + { + $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c'); + + if ($cmd == 'c') + { + $result = $this->batchCopy($commands, $pks, $contexts); + + if (is_array($result)) + { + foreach ($result as $old => $new) + { + $contexts[$new] = $contexts[$old]; + } + $pks = array_values($result); + } + else + { + return false; + } + } + elseif ($cmd == 'm' && !$this->batchMove($commands, $pks, $contexts)) + { + return false; + } + + $done = true; + } + + if (!$done) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + + return false; + } + + // Clear the cache + $this->cleanCache(); + + return true; + } + + /** + * Batch copy items to a new category or current. + * + * @param integer $values The new values. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return mixed An array of new IDs on success, boolean false on failure. + * + * @since 12.2 + */ + protected function batchCopy($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('package'); + } + + if (!$this->canDo->get('core.create') || !$this->canDo->get('core.batch')) + { + return false; + } + + // get list of unique fields + $uniqueFields = $this->getUniqueFields(); + // remove move_copy from array + unset($values['move_copy']); + + // make sure published is set + if (!isset($values['published'])) + { + $values['published'] = 0; + } + elseif (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + $values['published'] = 0; + } + + if (isset($values['category']) && (int) $values['category'] > 0 && !static::checkCategoryId($values['category'])) + { + return false; + } + elseif (isset($values['category']) && (int) $values['category'] > 0) + { + // move the category value to correct field name + $values['catid'] = $values['category']; + unset($values['category']); + } + elseif (isset($values['category'])) + { + unset($values['category']); + } + + $newIds = array(); + // Parent exists so let's proceed + while (!empty($pks)) + { + // Pop the first ID off the stack + $pk = array_shift($pks); + + $this->table->reset(); + + // only allow copy if user may edit this item. + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + if (isset($values['catid'])) + { + list($this->table->name, $this->table->alias) = $this->generateNewTitle($values['catid'], $this->table->alias, $this->table->name); + } + else + { + list($this->table->name, $this->table->alias) = $this->generateNewTitle($this->table->catid, $this->table->alias, $this->table->name); + } + + // insert all set values + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + if (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + // update all unique fields + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $this->table->$uniqueField = $this->generateUnique($uniqueField,$this->table->$uniqueField); + } + } + + // Reset the ID because we are making a copy + $this->table->id = 0; + + // TODO: Deal with ordering? + // $this->table->ordering = 1; + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + + // Get the new item ID + $newId = $this->table->get('id'); + + // Add the new ID to the array + $newIds[$pk] = $newId; + } + + // Clean the cache + $this->cleanCache(); + + return $newIds; + } + + /** + * Batch move items to a new category + * + * @param integer $value The new category ID. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 12.2 + */ + protected function batchMove($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('package'); + } + + if (!$this->canDo->get('core.edit') && !$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // make sure published only updates if user has the permission. + if (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + unset($values['published']); + } + // remove move_copy from array + unset($values['move_copy']); + + if (isset($values['category']) && (int) $values['category'] > 0 && !static::checkCategoryId($values['category'])) + { + return false; + } + elseif (isset($values['category']) && (int) $values['category'] > 0) + { + // move the category value to correct field name + $values['catid'] = $values['category']; + unset($values['category']); + } + elseif (isset($values['category'])) + { + unset($values['category']); + } + + + // Parent exists so we proceed + foreach ($pks as $pk) + { + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // insert all set values. + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + // Do special action for access. + if ('access' === $key && strlen($value) > 0) + { + $this->table->$key = $value; + } + elseif (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + } + + // Clean the cache + $this->cleanCache(); + + return true; + } + + /** + * Method to save the form data. + * + * @param array $data The form data. + * + * @return boolean True on success. + * + * @since 1.6 + */ + public function save($data) + { + $input = JFactory::getApplication()->input; + $filter = JFilterInput::getInstance(); + + // set the metadata to the Item Data + if (isset($data['metadata']) && isset($data['metadata']['author'])) + { + $data['metadata']['author'] = $filter->clean($data['metadata']['author'], 'TRIM'); + + $metadata = new JRegistry; + $metadata->loadArray($data['metadata']); + $data['metadata'] = (string) $metadata; + } + + // Set the files items to data. + if (isset($data['files']) && is_array($data['files'])) + { + $files = new JRegistry; + $files->loadArray($data['files']); + $data['files'] = (string) $files; + } + elseif (!isset($data['files'])) + { + // Set the empty files to data + $data['files'] = ''; + } + + // Set the php_preflight_install string to base64 string. + if (isset($data['php_preflight_install'])) + { + $data['php_preflight_install'] = base64_encode($data['php_preflight_install']); + } + + // Set the php_preflight_update string to base64 string. + if (isset($data['php_preflight_update'])) + { + $data['php_preflight_update'] = base64_encode($data['php_preflight_update']); + } + + // Set the php_preflight_uninstall string to base64 string. + if (isset($data['php_preflight_uninstall'])) + { + $data['php_preflight_uninstall'] = base64_encode($data['php_preflight_uninstall']); + } + + // Set the php_postflight_install string to base64 string. + if (isset($data['php_postflight_install'])) + { + $data['php_postflight_install'] = base64_encode($data['php_postflight_install']); + } + + // Set the php_postflight_update string to base64 string. + if (isset($data['php_postflight_update'])) + { + $data['php_postflight_update'] = base64_encode($data['php_postflight_update']); + } + + // Set the php_method_uninstall string to base64 string. + if (isset($data['php_method_uninstall'])) + { + $data['php_method_uninstall'] = base64_encode($data['php_method_uninstall']); + } + + // Set the Params Items to data + if (isset($data['params']) && is_array($data['params'])) + { + $params = new JRegistry; + $params->loadArray($data['params']); + $data['params'] = (string) $params; + } + + // Alter the name for save as copy + if ($input->get('task') === 'save2copy') + { + $origTable = clone $this->getTable(); + $origTable->load($input->getInt('id')); + + if ($data['name'] == $origTable->name) + { + list($name, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['name']); + $data['name'] = $name; + $data['alias'] = $alias; + } + else + { + if ($data['alias'] == $origTable->alias) + { + $data['alias'] = ''; + } + } + + $data['published'] = 0; + } + + // Automatic handling of alias for empty fields + if (in_array($input->get('task'), array('apply', 'save', 'save2new')) && (int) $input->get('id') == 0) + { + if ($data['alias'] == null || empty($data['alias'])) + { + if (JFactory::getConfig()->get('unicodeslugs') == 1) + { + $data['alias'] = JFilterOutput::stringURLUnicodeSlug($data['name']); + } + else + { + $data['alias'] = JFilterOutput::stringURLSafe($data['name']); + } + + $table = JTable::getInstance('package', 'extensiondistributorTable'); + + if ($table->load(array('alias' => $data['alias'], 'catid' => $data['catid'])) && ($table->id != $data['id'] || $data['id'] == 0)) + { + $msg = JText::_('COM_EXTENSIONDISTRIBUTOR_PACKAGE_SAVE_WARNING'); + } + + list($name, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['name']); + $data['alias'] = $alias; + + if (isset($msg)) + { + JFactory::getApplication()->enqueueMessage($msg, 'warning'); + } + } + } + + // Alter the unique field for save as copy + if ($input->get('task') === 'save2copy') + { + // Automatic handling of other unique fields + $uniqueFields = $this->getUniqueFields(); + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $data[$uniqueField] = $this->generateUnique($uniqueField,$data[$uniqueField]); + } + } + } + + if (parent::save($data)) + { + return true; + } + return false; + } + + /** + * Method to generate a unique value. + * + * @param string $field name. + * @param string $value data. + * + * @return string New value. + * + * @since 3.0 + */ + protected function generateUnique($field,$value) + { + + // set field value unique + $table = $this->getTable(); + + while ($table->load(array($field => $value))) + { + $value = StringHelper::increment($value); + } + + return $value; + } + + /** + * Method to change the title/s & alias. + * + * @param string $alias The alias. + * @param string/array $title The title. + * + * @return array/string Contains the modified title/s and/or alias. + * + */ + protected function _generateNewTitle($alias, $title = null) + { + + // Alter the title/s & alias + $table = $this->getTable(); + + while ($table->load(array('alias' => $alias))) + { + // Check if this is an array of titles + if (ExtensiondistributorHelper::checkArray($title)) + { + foreach($title as $nr => &$_title) + { + $_title = StringHelper::increment($_title); + } + } + // Make sure we have a title + elseif ($title) + { + $title = StringHelper::increment($title); + } + $alias = StringHelper::increment($alias, 'dash'); + } + // Check if this is an array of titles + if (ExtensiondistributorHelper::checkArray($title)) + { + $title[] = $alias; + return $title; + } + // Make sure we have a title + elseif ($title) + { + return array($title, $alias); + } + // We only had an alias + return $alias; + } +} diff --git a/admin/models/packages.php b/admin/models/packages.php new file mode 100644 index 0000000..66ab21a --- /dev/null +++ b/admin/models/packages.php @@ -0,0 +1,425 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ListModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Packages List Model + */ +class ExtensiondistributorModelPackages extends ListModel +{ + public function __construct($config = array()) + { + if (empty($config['filter_fields'])) + { + $config['filter_fields'] = array( + 'a.id','id', + 'a.published','published', + 'a.access','access', + 'a.ordering','ordering', + 'a.created_by','created_by', + 'a.modified_by','modified_by', + 'a.free','free', + 'c.title','category_title', + 'c.id', 'category_id', + 'a.catid','catid', + 'a.name','name' + ); + } + + parent::__construct($config); + } + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @param string $ordering An optional ordering field. + * @param string $direction An optional direction (asc|desc). + * + * @return void + * + */ + protected function populateState($ordering = null, $direction = null) + { + $app = JFactory::getApplication(); + + // Adjust the context to support modal layouts. + if ($layout = $app->input->get('layout')) + { + $this->context .= '.' . $layout; + } + + // Check if the form was submitted + $formSubmited = $app->input->post->get('form_submited'); + + $access = $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', 0, 'int'); + if ($formSubmited) + { + $access = $app->input->post->get('access'); + $this->setState('filter.access', $access); + } + + $published = $this->getUserStateFromRequest($this->context . '.filter.published', 'filter_published', ''); + $this->setState('filter.published', $published); + + $created_by = $this->getUserStateFromRequest($this->context . '.filter.created_by', 'filter_created_by', ''); + $this->setState('filter.created_by', $created_by); + + $created = $this->getUserStateFromRequest($this->context . '.filter.created', 'filter_created'); + $this->setState('filter.created', $created); + + $sorting = $this->getUserStateFromRequest($this->context . '.filter.sorting', 'filter_sorting', 0, 'int'); + $this->setState('filter.sorting', $sorting); + + $search = $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search'); + $this->setState('filter.search', $search); + + $free = $this->getUserStateFromRequest($this->context . '.filter.free', 'filter_free'); + if ($formSubmited) + { + $free = $app->input->post->get('free'); + $this->setState('filter.free', $free); + } + + $category = $app->getUserStateFromRequest($this->context . '.filter.category', 'filter_category'); + $this->setState('filter.category', $category); + + $categoryId = $this->getUserStateFromRequest($this->context . '.filter.category_id', 'filter_category_id'); + $this->setState('filter.category_id', $categoryId); + + $catid = $this->getUserStateFromRequest($this->context . '.filter.catid', 'filter_catid'); + if ($formSubmited) + { + $catid = $app->input->post->get('catid'); + $this->setState('filter.catid', $catid); + } + + $name = $this->getUserStateFromRequest($this->context . '.filter.name', 'filter_name'); + if ($formSubmited) + { + $name = $app->input->post->get('name'); + $this->setState('filter.name', $name); + } + + // List state information. + parent::populateState($ordering, $direction); + } + + /** + * Method to get an array of data items. + * + * @return mixed An array of data items on success, false on failure. + */ + public function getItems() + { + // Check in items + $this->checkInNow(); + + // load parent items + $items = parent::getItems(); + + // set selection value to a translatable value + if (ExtensiondistributorHelper::checkArray($items)) + { + foreach ($items as $nr => &$item) + { + // convert free + $item->free = $this->selectionTranslation($item->free, 'free'); + } + } + + + // return items + return $items; + } + + /** + * Method to convert selection values to translatable string. + * + * @return translatable string + */ + public function selectionTranslation($value,$name) + { + // Array of free language strings + if ($name === 'free') + { + $freeArray = array( + 1 => 'COM_EXTENSIONDISTRIBUTOR_PACKAGE_YES', + 0 => 'COM_EXTENSIONDISTRIBUTOR_PACKAGE_NO' + ); + // Now check if value is found in this array + if (isset($freeArray[$value]) && ExtensiondistributorHelper::checkString($freeArray[$value])) + { + return $freeArray[$value]; + } + } + return $value; + } + + /** + * Method to build an SQL query to load the list data. + * + * @return string An SQL query + */ + protected function getListQuery() + { + // Get the user object. + $user = JFactory::getUser(); + // Create a new query object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + + // Select some fields + $query->select('a.*'); + $query->select($db->quoteName('c.title','category_title')); + + // From the extensiondistributor_item table + $query->from($db->quoteName('#__extensiondistributor_package', 'a')); + $query->join('LEFT', $db->quoteName('#__categories', 'c') . ' ON (' . $db->quoteName('a.catid') . ' = ' . $db->quoteName('c.id') . ')'); + + // Filter by published state + $published = $this->getState('filter.published'); + if (is_numeric($published)) + { + $query->where('a.published = ' . (int) $published); + } + elseif ($published === '') + { + $query->where('(a.published = 0 OR a.published = 1)'); + } + + // Join over the asset groups. + $query->select('ag.title AS access_level'); + $query->join('LEFT', '#__viewlevels AS ag ON ag.id = a.access'); + // Filter by access level. + $_access = $this->getState('filter.access'); + if ($_access && is_numeric($_access)) + { + $query->where('a.access = ' . (int) $_access); + } + elseif (ExtensiondistributorHelper::checkArray($_access)) + { + // Secure the array for the query + $_access = ArrayHelper::toInteger($_access); + // Filter by the Access Array. + $query->where('a.access IN (' . implode(',', $_access) . ')'); + } + // Implement View Level Access + if (!$user->authorise('core.options', 'com_extensiondistributor')) + { + $groups = implode(',', $user->getAuthorisedViewLevels()); + $query->where('a.access IN (' . $groups . ')'); + } + // Filter by search. + $search = $this->getState('filter.search'); + if (!empty($search)) + { + if (stripos($search, 'id:') === 0) + { + $query->where('a.id = ' . (int) substr($search, 3)); + } + else + { + $search = $db->quote('%' . $db->escape($search) . '%'); + $query->where('(a.name LIKE '.$search.' OR a.description LIKE '.$search.' OR a.catid LIKE '.$search.' OR a.alias LIKE '.$search.')'); + } + } + + // Filter by Free. + $_free = $this->getState('filter.free'); + if (is_numeric($_free)) + { + if (is_float($_free)) + { + $query->where('a.free = ' . (float) $_free); + } + else + { + $query->where('a.free = ' . (int) $_free); + } + } + elseif (ExtensiondistributorHelper::checkString($_free)) + { + $query->where('a.free = ' . $db->quote($db->escape($_free))); + } + + // Filter by a single or group of categories. + $baselevel = 1; + $categoryId = $this->getState('filter.category_id'); + + if (is_numeric($categoryId)) + { + $cat_tbl = JTable::getInstance('Category', 'JTable'); + $cat_tbl->load($categoryId); + $rgt = $cat_tbl->rgt; + $lft = $cat_tbl->lft; + $baselevel = (int) $cat_tbl->level; + $query->where('c.lft >= ' . (int) $lft) + ->where('c.rgt <= ' . (int) $rgt); + } + elseif (is_array($categoryId)) + { + $categoryId = ArrayHelper::toInteger($categoryId); + $categoryId = implode(',', $categoryId); + $query->where('a.catid IN (' . $categoryId . ')'); + } + + + // Add the list ordering clause. + $orderCol = $this->state->get('list.ordering', 'a.id'); + $orderDirn = $this->state->get('list.direction', 'desc'); + if ($orderCol != '') + { + $query->order($db->escape($orderCol . ' ' . $orderDirn)); + } + + return $query; + } + + /** + * Method to get a store id based on model configuration state. + * + * @return string A store id. + * + */ + protected function getStoreId($id = '') + { + // Compile the store id. + $id .= ':' . $this->getState('filter.id'); + $id .= ':' . $this->getState('filter.search'); + $id .= ':' . $this->getState('filter.published'); + // Check if the value is an array + $_access = $this->getState('filter.access'); + if (ExtensiondistributorHelper::checkArray($_access)) + { + $id .= ':' . implode(':', $_access); + } + // Check if this is only an number or string + elseif (is_numeric($_access) + || ExtensiondistributorHelper::checkString($_access)) + { + $id .= ':' . $_access; + } + $id .= ':' . $this->getState('filter.ordering'); + $id .= ':' . $this->getState('filter.created_by'); + $id .= ':' . $this->getState('filter.modified_by'); + $id .= ':' . $this->getState('filter.free'); + // Check if the value is an array + $_category = $this->getState('filter.category'); + if (ExtensiondistributorHelper::checkArray($_category)) + { + $id .= ':' . implode(':', $_category); + } + // Check if this is only an number or string + elseif (is_numeric($_category) + || ExtensiondistributorHelper::checkString($_category)) + { + $id .= ':' . $_category; + } + // Check if the value is an array + $_category_id = $this->getState('filter.category_id'); + if (ExtensiondistributorHelper::checkArray($_category_id)) + { + $id .= ':' . implode(':', $_category_id); + } + // Check if this is only an number or string + elseif (is_numeric($_category_id) + || ExtensiondistributorHelper::checkString($_category_id)) + { + $id .= ':' . $_category_id; + } + // Check if the value is an array + $_catid = $this->getState('filter.catid'); + if (ExtensiondistributorHelper::checkArray($_catid)) + { + $id .= ':' . implode(':', $_catid); + } + // Check if this is only an number or string + elseif (is_numeric($_catid) + || ExtensiondistributorHelper::checkString($_catid)) + { + $id .= ':' . $_catid; + } + $id .= ':' . $this->getState('filter.name'); + + return parent::getStoreId($id); + } + + /** + * Build an SQL query to checkin all items left checked out longer then a set time. + * + * @return a bool + * + */ + protected function checkInNow() + { + // Get set check in time + $time = JComponentHelper::getParams('com_extensiondistributor')->get('check_in'); + + if ($time) + { + + // Get a db connection. + $db = JFactory::getDbo(); + // Reset query. + $query = $db->getQuery(true); + $query->select('*'); + $query->from($db->quoteName('#__extensiondistributor_package')); + // Only select items that are checked out. + $query->where($db->quoteName('checked_out') . '!=0'); + $db->setQuery($query, 0, 1); + $db->execute(); + if ($db->getNumRows()) + { + // Get Yesterdays date. + $date = JFactory::getDate()->modify($time)->toSql(); + // Reset query. + $query = $db->getQuery(true); + + // Fields to update. + $fields = array( + $db->quoteName('checked_out_time') . '=\'0000-00-00 00:00:00\'', + $db->quoteName('checked_out') . '=0' + ); + + // Conditions for which records should be updated. + $conditions = array( + $db->quoteName('checked_out') . '!=0', + $db->quoteName('checked_out_time') . '<\''.$date.'\'' + ); + + // Check table. + $query->update($db->quoteName('#__extensiondistributor_package'))->set($fields)->where($conditions); + + $db->setQuery($query); + + $db->execute(); + } + } + + return false; + } +} diff --git a/admin/models/release.php b/admin/models/release.php new file mode 100644 index 0000000..1a23417 --- /dev/null +++ b/admin/models/release.php @@ -0,0 +1,1060 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\AdminModel; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Release Admin Model + */ +class ExtensiondistributorModelRelease extends AdminModel +{ + /** + * The tab layout fields array. + * + * @var array + */ + protected $tabLayoutFields = array( + 'details' => array( + 'left' => array( + 'description', + 'stability', + 'reltype', + 'xmlpublished' + ), + 'right' => array( + 'upload_package', + 'file', + 'zip_content' + ), + 'above' => array( + 'extension', + 'package', + 'version_number', + 'release_date', + 'forceupdate' + ), + 'under' => array( + 'toolbar' + ) + ), + 'changelog' => array( + 'fullwidth' => array( + 'changelog' + ) + ) + ); + + /** + * @var string The prefix to use with controller messages. + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + /** + * The type alias for this content type. + * + * @var string + * @since 3.2 + */ + public $typeAlias = 'com_extensiondistributor.release'; + + /** + * Returns a Table object, always creating it + * + * @param type $type The table type to instantiate + * @param string $prefix A prefix for the table class name. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JTable A database object + * + * @since 1.6 + */ + public function getTable($type = 'release', $prefix = 'ExtensiondistributorTable', $config = array()) + { + // add table path for when model gets used from other component + $this->addTablePath(JPATH_ADMINISTRATOR . '/components/com_extensiondistributor/tables'); + // get instance of the table + return JTable::getInstance($type, $prefix, $config); + } + + /** + * Method to get a single record. + * + * @param integer $pk The id of the primary key. + * + * @return mixed Object on success, false on failure. + * + * @since 1.6 + */ + public function getItem($pk = null) + { + if ($item = parent::getItem($pk)) + { + if (!empty($item->params) && !is_array($item->params)) + { + // Convert the params field to an array. + $registry = new Registry; + $registry->loadString($item->params); + $item->params = $registry->toArray(); + } + + if (!empty($item->metadata)) + { + // Convert the metadata field to an array. + $registry = new Registry; + $registry->loadString($item->metadata); + $item->metadata = $registry->toArray(); + } + // Not needed + + if (!empty($item->changelog)) + { + // Convert the changelog field to an array. + $changelog = new Registry; + $changelog->loadString($item->changelog); + $item->changelog = $changelog->toArray(); + } + } + + return $item; + } + + /** + * Method to get the record form. + * + * @param array $data Data for the form. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * @param array $options Optional array of options for the form creation. + * + * @return mixed A JForm object on success, false on failure + * + * @since 1.6 + */ + public function getForm($data = array(), $loadData = true, $options = array('control' => 'jform')) + { + // set load data option + $options['load_data'] = $loadData; + // check if xpath was set in options + $xpath = false; + if (isset($options['xpath'])) + { + $xpath = $options['xpath']; + unset($options['xpath']); + } + // check if clear form was set in options + $clear = false; + if (isset($options['clear'])) + { + $clear = $options['clear']; + unset($options['clear']); + } + + // Get the form. + $form = $this->loadForm('com_extensiondistributor.release', 'release', $options, $clear, $xpath); + + if (empty($form)) + { + return false; + } + + $jinput = JFactory::getApplication()->input; + + // The front end calls this model and uses a_id to avoid id clashes so we need to check for that first. + if ($jinput->get('a_id')) + { + $id = $jinput->get('a_id', 0, 'INT'); + } + // The back end uses id so we use that the rest of the time and set it to 0 by default. + else + { + $id = $jinput->get('id', 0, 'INT'); + } + + $user = JFactory::getUser(); + + // Check for existing item. + // Modify the form based on Edit State access controls. + if ($id != 0 && (!$user->authorise('core.edit.state', 'com_extensiondistributor.release.' . (int) $id)) + || ($id == 0 && !$user->authorise('core.edit.state', 'com_extensiondistributor'))) + { + // Disable fields for display. + $form->setFieldAttribute('ordering', 'disabled', 'true'); + $form->setFieldAttribute('published', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('ordering', 'filter', 'unset'); + $form->setFieldAttribute('published', 'filter', 'unset'); + } + // If this is a new item insure the greated by is set. + if (0 == $id) + { + // Set the created_by to this user + $form->setValue('created_by', null, $user->id); + } + // Modify the form based on Edit Creaded By access controls. + if (!$user->authorise('core.edit.created_by', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created_by', 'disabled', 'true'); + // Disable fields for display. + $form->setFieldAttribute('created_by', 'readonly', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created_by', 'filter', 'unset'); + } + // Modify the form based on Edit Creaded Date access controls. + if (!$user->authorise('core.edit.created', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created', 'filter', 'unset'); + } + // Only load these values if no id is found + if (0 == $id) + { + // Set redirected view name + $redirectedView = $jinput->get('ref', null, 'STRING'); + // Set field name (or fall back to view name) + $redirectedField = $jinput->get('field', $redirectedView, 'STRING'); + // Set redirected view id + $redirectedId = $jinput->get('refid', 0, 'INT'); + // Set field id (or fall back to redirected view id) + $redirectedValue = $jinput->get('field_id', $redirectedId, 'INT'); + if (0 != $redirectedValue && $redirectedField) + { + // Now set the local-redirected field default value + $form->setValue($redirectedField, null, $redirectedValue); + } + } + global $cparams; + if (0 == $id) + { + // Get the latest release + $releases = ExtensiondistributorHelper::getReleases($redirectedId,$redirectedView); + if (ExtensiondistributorHelper::checkArray($releases)) + { + $latest = $releases[0]->version_number; + $latest = explode('.',$latest); + $latest[2]++; + $new = implode('.',$latest); + $form->setValue('version_number', null, $new); + } + else + { + $form->setValue('version_number', null, '1.0.0'); + } + } + $ref = $jinput->get('ref', null, 'STRING'); + if ($ref === 'extension' || $form->getValue('extension') > 0) + { + $form->setFieldAttribute('extension', 'readonly', 'true'); + $form->setFieldAttribute('package', 'type', 'hidden'); + $form->setFieldAttribute('forceupdate', 'type', 'hidden'); + } + if ($ref === 'package' || $form->getValue('package') > 0) + { + $form->setFieldAttribute('package', 'readonly', 'true'); + $form->setFieldAttribute('extension', 'type', 'hidden'); + $form->setFieldAttribute('upload_package', 'type', 'hidden'); + $form->setFieldAttribute('file', 'readonly', 'true'); + $form->setFieldAttribute('file', 'directory', $cparams->get('releases_directory')); + } + + return $form; + } + + /** + * Method to get the script that have to be included on the form + * + * @return string script files + */ + public function getScript() + { + return 'media/com_extensiondistributor/js/release.js'; + } + + /** + * Method to test whether a record can be deleted. + * + * @param object $record A record object. + * + * @return boolean True if allowed to delete the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canDelete($record) + { + if (!empty($record->id)) + { + if ($record->published != -2) + { + return; + } + + $user = JFactory::getUser(); + // The record has been set. Check the record permissions. + return $user->authorise('core.delete', 'com_extensiondistributor.release.' . (int) $record->id); + } + return false; + } + + /** + * Method to test whether a record can have its state edited. + * + * @param object $record A record object. + * + * @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canEditState($record) + { + $user = JFactory::getUser(); + $recordId = (!empty($record->id)) ? $record->id : 0; + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit.state', 'com_extensiondistributor.release.' . (int) $recordId); + if (!$permission && !is_null($permission)) + { + return false; + } + } + // In the absense of better information, revert to the component permissions. + return parent::canEditState($record); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * @since 2.5 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // Check specific edit permission then general edit permission. + + return JFactory::getUser()->authorise('core.edit', 'com_extensiondistributor.release.'. ((int) isset($data[$key]) ? $data[$key] : 0)) or parent::allowEdit($data, $key); + } + + /** + * Prepare and sanitise the table data prior to saving. + * + * @param JTable $table A JTable object. + * + * @return void + * + * @since 1.6 + */ + protected function prepareTable($table) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if (isset($table->name)) + { + $table->name = htmlspecialchars_decode($table->name, ENT_QUOTES); + } + + if (isset($table->alias) && empty($table->alias)) + { + $table->generateAlias(); + } + + if (empty($table->id)) + { + $table->created = $date->toSql(); + // set the user + if ($table->created_by == 0 || empty($table->created_by)) + { + $table->created_by = $user->id; + } + // Set ordering to the last item if not set + if (empty($table->ordering)) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select('MAX(ordering)') + ->from($db->quoteName('#__extensiondistributor_release')); + $db->setQuery($query); + $max = $db->loadResult(); + + $table->ordering = $max + 1; + } + } + else + { + $table->modified = $date->toSql(); + $table->modified_by = $user->id; + } + + if (!empty($table->id)) + { + // Increment the items version number. + $table->version++; + } + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 1.6 + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = JFactory::getApplication()->getUserState('com_extensiondistributor.edit.release.data', array()); + + if (empty($data)) + { + $data = $this->getItem(); + // run the perprocess of the data + $this->preprocessData('com_extensiondistributor.release', $data); + } + + return $data; + } + + /** + * Method to get the unique fields of this table. + * + * @return mixed An array of field names, boolean false if none is set. + * + * @since 3.0 + */ + protected function getUniqueFields() + { + return false; + } + + /** + * Method to delete one or more records. + * + * @param array &$pks An array of record primary keys. + * + * @return boolean True if successful, false if an error occurs. + * + * @since 12.2 + */ + public function delete(&$pks) + { + $app = JFactory::getApplication(); + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $path = JPATH_SITE . '/' . $cparams->get('releases_directory','packages') . '/'; + $path = JPath::clean($path); + // Get files associated with releaseID + $files = ExtensiondistributorHelper::getReleaseToFile(); + // Find Orphans + $allfiles = JFolder::files($path); + $orphans = array_diff($allfiles,$files); + // Find Duplicates + $duplicates = array(); + foreach(array_count_values($files) as $val => $c) { + if($c > 1) $duplicates[] = $val; + } + + // We compute an array of files to delete if the parent method is successful + $toDelete = array(); + foreach ($pks as $pk) { + if (JFile::exists($path.$files[$pk])) { + if (!in_array($files[$pk],$duplicates)) { + $toDelete[] = $files[$pk]; + } else { + $app->enqueueMessage(sprintf('The file %s is used by another release.', $files[$pk]),'warning'); + } + } + } + + if (!parent::delete($pks)) + { + return false; + } + + // We delete the associated files + foreach ($toDelete as $f) { + JFile::delete($path.$f); + } + + + return true; + } + + /** + * Method to change the published state of one or more records. + * + * @param array &$pks A list of the primary keys to change. + * @param integer $value The value of the published state. + * + * @return boolean True on success. + * + * @since 12.2 + */ + public function publish(&$pks, $value = 1) + { + if (!parent::publish($pks, $value)) + { + return false; + } + + return true; + } + + /** + * Method to perform batch operations on an item or a set of items. + * + * @param array $commands An array of commands to perform. + * @param array $pks An array of item ids. + * @param array $contexts An array of item contexts. + * + * @return boolean Returns true on success, false on failure. + * + * @since 12.2 + */ + public function batch($commands, $pks, $contexts) + { + // Sanitize ids. + $pks = array_unique($pks); + ArrayHelper::toInteger($pks); + + // Remove any values of zero. + if (array_search(0, $pks, true)) + { + unset($pks[array_search(0, $pks, true)]); + } + + if (empty($pks)) + { + $this->setError(JText::_('JGLOBAL_NO_ITEM_SELECTED')); + return false; + } + + $done = false; + + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->contentType = new JUcmType; + $this->type = $this->contentType->getTypeByTable($this->tableClassName); + $this->canDo = ExtensiondistributorHelper::getActions('release'); + $this->batchSet = true; + + if (!$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + return false; + } + + if ($this->type == false) + { + $type = new JUcmType; + $this->type = $type->getTypeByAlias($this->typeAlias); + } + + $this->tagsObserver = $this->table->getObserverOfClass('JTableObserverTags'); + + if (!empty($commands['move_copy'])) + { + $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c'); + + if ($cmd == 'c') + { + $result = $this->batchCopy($commands, $pks, $contexts); + + if (is_array($result)) + { + foreach ($result as $old => $new) + { + $contexts[$new] = $contexts[$old]; + } + $pks = array_values($result); + } + else + { + return false; + } + } + elseif ($cmd == 'm' && !$this->batchMove($commands, $pks, $contexts)) + { + return false; + } + + $done = true; + } + + if (!$done) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + + return false; + } + + // Clear the cache + $this->cleanCache(); + + return true; + } + + /** + * Batch copy items to a new category or current. + * + * @param integer $values The new values. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return mixed An array of new IDs on success, boolean false on failure. + * + * @since 12.2 + */ + protected function batchCopy($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('release'); + } + + if (!$this->canDo->get('core.create') || !$this->canDo->get('core.batch')) + { + return false; + } + + // get list of unique fields + $uniqueFields = $this->getUniqueFields(); + // remove move_copy from array + unset($values['move_copy']); + + // make sure published is set + if (!isset($values['published'])) + { + $values['published'] = 0; + } + elseif (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + $values['published'] = 0; + } + + $newIds = array(); + // Parent exists so let's proceed + while (!empty($pks)) + { + // Pop the first ID off the stack + $pk = array_shift($pks); + + $this->table->reset(); + + // only allow copy if user may edit this item. + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // Only for strings + if (ExtensiondistributorHelper::checkString($this->table->version_number) && !is_numeric($this->table->version_number)) + { + $this->table->version_number = $this->generateUnique('version_number',$this->table->version_number); + } + + // insert all set values + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + if (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + // update all unique fields + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $this->table->$uniqueField = $this->generateUnique($uniqueField,$this->table->$uniqueField); + } + } + + // Reset the ID because we are making a copy + $this->table->id = 0; + + // TODO: Deal with ordering? + // $this->table->ordering = 1; + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + + // Get the new item ID + $newId = $this->table->get('id'); + + // Add the new ID to the array + $newIds[$pk] = $newId; + } + + // Clean the cache + $this->cleanCache(); + + return $newIds; + } + + /** + * Batch move items to a new category + * + * @param integer $value The new category ID. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 12.2 + */ + protected function batchMove($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('release'); + } + + if (!$this->canDo->get('core.edit') && !$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // make sure published only updates if user has the permission. + if (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + unset($values['published']); + } + // remove move_copy from array + unset($values['move_copy']); + + // Parent exists so we proceed + foreach ($pks as $pk) + { + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // insert all set values. + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + // Do special action for access. + if ('access' === $key && strlen($value) > 0) + { + $this->table->$key = $value; + } + elseif (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + } + + // Clean the cache + $this->cleanCache(); + + return true; + } + + /** + * Method to save the form data. + * + * @param array $data The form data. + * + * @return boolean True on success. + * + * @since 1.6 + */ + public function save($data) + { + $input = JFactory::getApplication()->input; + $filter = JFilterInput::getInstance(); + + // set the metadata to the Item Data + if (isset($data['metadata']) && isset($data['metadata']['author'])) + { + $data['metadata']['author'] = $filter->clean($data['metadata']['author'], 'TRIM'); + + $metadata = new JRegistry; + $metadata->loadArray($data['metadata']); + $data['metadata'] = (string) $metadata; + } + + // Set the changelog items to data. + if (isset($data['changelog']) && is_array($data['changelog'])) + { + $changelog = new JRegistry; + $changelog->loadArray($data['changelog']); + $data['changelog'] = (string) $changelog; + } + elseif (!isset($data['changelog'])) + { + // Set the empty changelog to data + $data['changelog'] = ''; + } + if (isset($data['release_date']) && ($data['release_date'] != '0000-00-00 00:00:00') && ($data['release_date'] != '')) + { + $data['release_date'] = JFactory::getDate($data['release_date'])->toSql(); + } + + + // First we need to know if this belongs to an extension or a package + $app = JFactory::getApplication(); + $object = 'package'; + $objectId = $data['package']; + if ($data['extension'] > 0) { + $object = 'extension'; + $objectId = $data['extension']; + } + // Now we get all releases of the element + $releases = ExtensiondistributorHelper::getReleases($objectId, $object); + $latest = null; + if (ExtensiondistributorHelper::checkArray($releases)) { + $latest = $releases[0]->version_number; + } + + if (version_compare($data['version_number'], $latest, '>=') || $latest === null) + { + $model = ExtensiondistributorHelper::getModel($object); + $element = $model->getItem($objectId); + $element->version_number = $data['version_number']; + $element->release_date = $data['release_date']; + unset($element->tags); + $model->save((array) $element); + } + + // We format the filename according to the filename pattern if needed + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $path = JPath::clean(JPATH_SITE . '/' . $cparams->get('releases_directory') .'/') ; + if ($object === 'package') { + $package = ExtensiondistributorHelper::getPackageObject($objectId); + $newfilename = ExtensiondistributorHelper::createPackageFileName($package, $data['version_number'], $data['stability']); + } else { + $extension = ExtensiondistributorHelper::getExtensionObject($objectId); + $newfilename = ExtensiondistributorHelper::createExtensionFileName($extension, $data['version_number'], $data['stability'], $data['file']); + } + if (JFile::exists($path.$data['file'])) { + if (JFile::move($path.$data['file'],$path.$newfilename)) { + $data['file'] = $newfilename; + } + } + + // We build the package + if ( $object === 'package' && ($data['forceupdate'] || $data['zip_content'] === 'EMPTY') ) { + $release = array( + 'version_number' => $data['version_number'], + 'stability' => $data['stability'], + 'release_date' => $data['release_date'] + ); + try + { + // First remove the old file if it exists + if (JFile::exists($path.$data['file'])) { + JFile::delete($path.$data['file']); + } + $zip = ExtensiondistributorHelper::getPackageContent($objectId); + $file = ExtensiondistributorHelper::createPackage($objectId,(object)$release); + $data['file'] = $file; + $data['zip_content'] = json_encode($zip['files']); + $app->enqueueMessage(JText::sprintf("COM_EXTENSIONDISTRIBUTOR_THE_PACKAGE_FILE_S_HAS_BEEN_SUCCESSFULLY_CREATED", $file)); + } + catch (Exception $e) + { + $app->enqueueMessage($e->getMessage(),"error"); + } + } + + + // Set the Params Items to data + if (isset($data['params']) && is_array($data['params'])) + { + $params = new JRegistry; + $params->loadArray($data['params']); + $data['params'] = (string) $params; + } + + // Alter the unique field for save as copy + if ($input->get('task') === 'save2copy') + { + // Automatic handling of other unique fields + $uniqueFields = $this->getUniqueFields(); + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $data[$uniqueField] = $this->generateUnique($uniqueField,$data[$uniqueField]); + } + } + } + + if (parent::save($data)) + { + return true; + } + return false; + } + + /** + * Method to generate a unique value. + * + * @param string $field name. + * @param string $value data. + * + * @return string New value. + * + * @since 3.0 + */ + protected function generateUnique($field,$value) + { + + // set field value unique + $table = $this->getTable(); + + while ($table->load(array($field => $value))) + { + $value = StringHelper::increment($value); + } + + return $value; + } + + /** + * Method to change the title + * + * @param string $title The title. + * + * @return array Contains the modified title and alias. + * + */ + protected function _generateNewTitle($title) + { + + // Alter the title + $table = $this->getTable(); + + while ($table->load(array('title' => $title))) + { + $title = StringHelper::increment($title); + } + + return $title; + } +} diff --git a/admin/models/releases.php b/admin/models/releases.php new file mode 100644 index 0000000..7fafac0 --- /dev/null +++ b/admin/models/releases.php @@ -0,0 +1,578 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ListModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Releases List Model + */ +class ExtensiondistributorModelReleases extends ListModel +{ + public function __construct($config = array()) + { + if (empty($config['filter_fields'])) + { + $config['filter_fields'] = array( + 'a.id','id', + 'a.published','published', + 'a.ordering','ordering', + 'a.created_by','created_by', + 'a.modified_by','modified_by', + 'g.name','extension', + 'h.name','package', + 'a.reltype','reltype', + 'a.stability','stability', + 'a.version_number','version_number', + 'a.release_date','release_date' + ); + } + + parent::__construct($config); + } + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @param string $ordering An optional ordering field. + * @param string $direction An optional direction (asc|desc). + * + * @return void + * + */ + protected function populateState($ordering = null, $direction = null) + { + $app = JFactory::getApplication(); + + // Adjust the context to support modal layouts. + if ($layout = $app->input->get('layout')) + { + $this->context .= '.' . $layout; + } + + // Check if the form was submitted + $formSubmited = $app->input->post->get('form_submited'); + + $access = $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', 0, 'int'); + if ($formSubmited) + { + $access = $app->input->post->get('access'); + $this->setState('filter.access', $access); + } + + $published = $this->getUserStateFromRequest($this->context . '.filter.published', 'filter_published', ''); + $this->setState('filter.published', $published); + + $created_by = $this->getUserStateFromRequest($this->context . '.filter.created_by', 'filter_created_by', ''); + $this->setState('filter.created_by', $created_by); + + $created = $this->getUserStateFromRequest($this->context . '.filter.created', 'filter_created'); + $this->setState('filter.created', $created); + + $sorting = $this->getUserStateFromRequest($this->context . '.filter.sorting', 'filter_sorting', 0, 'int'); + $this->setState('filter.sorting', $sorting); + + $search = $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search'); + $this->setState('filter.search', $search); + + $extension = $this->getUserStateFromRequest($this->context . '.filter.extension', 'filter_extension'); + if ($formSubmited) + { + $extension = $app->input->post->get('extension'); + $this->setState('filter.extension', $extension); + } + + $package = $this->getUserStateFromRequest($this->context . '.filter.package', 'filter_package'); + if ($formSubmited) + { + $package = $app->input->post->get('package'); + $this->setState('filter.package', $package); + } + + $reltype = $this->getUserStateFromRequest($this->context . '.filter.reltype', 'filter_reltype'); + if ($formSubmited) + { + $reltype = $app->input->post->get('reltype'); + $this->setState('filter.reltype', $reltype); + } + + $stability = $this->getUserStateFromRequest($this->context . '.filter.stability', 'filter_stability'); + if ($formSubmited) + { + $stability = $app->input->post->get('stability'); + $this->setState('filter.stability', $stability); + } + + $version_number = $this->getUserStateFromRequest($this->context . '.filter.version_number', 'filter_version_number'); + if ($formSubmited) + { + $version_number = $app->input->post->get('version_number'); + $this->setState('filter.version_number', $version_number); + } + + $release_date = $this->getUserStateFromRequest($this->context . '.filter.release_date', 'filter_release_date'); + if ($formSubmited) + { + $release_date = $app->input->post->get('release_date'); + $this->setState('filter.release_date', $release_date); + } + + // List state information. + parent::populateState($ordering, $direction); + } + + /** + * Method to get an array of data items. + * + * @return mixed An array of data items on success, false on failure. + */ + public function getItems() + { + // Check in items + $this->checkInNow(); + + // load parent items + $items = parent::getItems(); + + // Set values to display correctly. + if (ExtensiondistributorHelper::checkArray($items)) + { + // Get the user object if not set. + if (!isset($user) || !ExtensiondistributorHelper::checkObject($user)) + { + $user = JFactory::getUser(); + } + foreach ($items as $nr => &$item) + { + // Not needed + } + } + + // set selection value to a translatable value + if (ExtensiondistributorHelper::checkArray($items)) + { + foreach ($items as $nr => &$item) + { + // convert reltype + $item->reltype = $this->selectionTranslation($item->reltype, 'reltype'); + // convert stability + $item->stability = $this->selectionTranslation($item->stability, 'stability'); + } + } + + + // return items + return $items; + } + + /** + * Method to convert selection values to translatable string. + * + * @return translatable string + */ + public function selectionTranslation($value,$name) + { + // Array of reltype language strings + if ($name === 'reltype') + { + $reltypeArray = array( + 0 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_PLEASE_SELECT', + 'security' => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_SECURITY', + 'maintenance' => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_MAINTENANCE', + 'newversion' => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_NEW_VERSION' + ); + // Now check if value is found in this array + if (isset($reltypeArray[$value]) && ExtensiondistributorHelper::checkString($reltypeArray[$value])) + { + return $reltypeArray[$value]; + } + } + // Array of stability language strings + if ($name === 'stability') + { + $stabilityArray = array( + 0 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_DEVELOPMENT', + 1 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_ALPHA', + 2 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_BETA', + 3 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_RC', + 4 => 'COM_EXTENSIONDISTRIBUTOR_RELEASE_STABLE' + ); + // Now check if value is found in this array + if (isset($stabilityArray[$value]) && ExtensiondistributorHelper::checkString($stabilityArray[$value])) + { + return $stabilityArray[$value]; + } + } + return $value; + } + + /** + * Method to build an SQL query to load the list data. + * + * @return string An SQL query + */ + protected function getListQuery() + { + // Get the user object. + $user = JFactory::getUser(); + // Create a new query object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + + // Select some fields + $query->select('a.*'); + + // From the extensiondistributor_item table + $query->from($db->quoteName('#__extensiondistributor_release', 'a')); + + // From the extensiondistributor_extension table. + $query->select($db->quoteName('g.name','extension_name')); + $query->join('LEFT', $db->quoteName('#__extensiondistributor_extension', 'g') . ' ON (' . $db->quoteName('a.extension') . ' = ' . $db->quoteName('g.id') . ')'); + + // From the extensiondistributor_package table. + $query->select($db->quoteName('h.name','package_name')); + $query->join('LEFT', $db->quoteName('#__extensiondistributor_package', 'h') . ' ON (' . $db->quoteName('a.package') . ' = ' . $db->quoteName('h.id') . ')'); + + // Filter by published state + $published = $this->getState('filter.published'); + if (is_numeric($published)) + { + $query->where('a.published = ' . (int) $published); + } + elseif ($published === '') + { + $query->where('(a.published = 0 OR a.published = 1)'); + } + // Filter by search. + $search = $this->getState('filter.search'); + if (!empty($search)) + { + if (stripos($search, 'id:') === 0) + { + $query->where('a.id = ' . (int) substr($search, 3)); + } + else + { + $search = $db->quote('%' . $db->escape($search) . '%'); + $query->where('(a.extension LIKE '.$search.' OR a.package LIKE '.$search.' OR a.version_number LIKE '.$search.' OR a.reltype LIKE '.$search.' OR a.description LIKE '.$search.' OR a.changelog LIKE '.$search.' OR a.stability LIKE '.$search.')'); + } + } + + // Filter by Extension. + $_extension = $this->getState('filter.extension'); + if (is_numeric($_extension)) + { + if (is_float($_extension)) + { + $query->where('a.extension = ' . (float) $_extension); + } + else + { + $query->where('a.extension = ' . (int) $_extension); + } + } + elseif (ExtensiondistributorHelper::checkString($_extension)) + { + $query->where('a.extension = ' . $db->quote($db->escape($_extension))); + } + elseif (ExtensiondistributorHelper::checkArray($_extension)) + { + // Secure the array for the query + $_extension = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_extension); + // Filter by the Extension Array. + $query->where('a.extension IN (' . implode(',', $_extension) . ')'); + } + // Filter by Package. + $_package = $this->getState('filter.package'); + if (is_numeric($_package)) + { + if (is_float($_package)) + { + $query->where('a.package = ' . (float) $_package); + } + else + { + $query->where('a.package = ' . (int) $_package); + } + } + elseif (ExtensiondistributorHelper::checkString($_package)) + { + $query->where('a.package = ' . $db->quote($db->escape($_package))); + } + elseif (ExtensiondistributorHelper::checkArray($_package)) + { + // Secure the array for the query + $_package = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_package); + // Filter by the Package Array. + $query->where('a.package IN (' . implode(',', $_package) . ')'); + } + // Filter by Reltype. + $_reltype = $this->getState('filter.reltype'); + if (is_numeric($_reltype)) + { + if (is_float($_reltype)) + { + $query->where('a.reltype = ' . (float) $_reltype); + } + else + { + $query->where('a.reltype = ' . (int) $_reltype); + } + } + elseif (ExtensiondistributorHelper::checkString($_reltype)) + { + $query->where('a.reltype = ' . $db->quote($db->escape($_reltype))); + } + elseif (ExtensiondistributorHelper::checkArray($_reltype)) + { + // Secure the array for the query + $_reltype = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_reltype); + // Filter by the Reltype Array. + $query->where('a.reltype IN (' . implode(',', $_reltype) . ')'); + } + // Filter by Stability. + $_stability = $this->getState('filter.stability'); + if (is_numeric($_stability)) + { + if (is_float($_stability)) + { + $query->where('a.stability = ' . (float) $_stability); + } + else + { + $query->where('a.stability = ' . (int) $_stability); + } + } + elseif (ExtensiondistributorHelper::checkString($_stability)) + { + $query->where('a.stability = ' . $db->quote($db->escape($_stability))); + } + elseif (ExtensiondistributorHelper::checkArray($_stability)) + { + // Secure the array for the query + $_stability = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_stability); + // Filter by the Stability Array. + $query->where('a.stability IN (' . implode(',', $_stability) . ')'); + } + + // Add the list ordering clause. + $orderCol = $this->state->get('list.ordering', 'a.id'); + $orderDirn = $this->state->get('list.direction', 'desc'); + if ($orderCol != '') + { + $query->order($db->escape($orderCol . ' ' . $orderDirn)); + } + + return $query; + } + + /** + * Method to get a store id based on model configuration state. + * + * @return string A store id. + * + */ + protected function getStoreId($id = '') + { + // Compile the store id. + $id .= ':' . $this->getState('filter.id'); + $id .= ':' . $this->getState('filter.search'); + $id .= ':' . $this->getState('filter.published'); + $id .= ':' . $this->getState('filter.ordering'); + $id .= ':' . $this->getState('filter.created_by'); + $id .= ':' . $this->getState('filter.modified_by'); + // Check if the value is an array + $_extension = $this->getState('filter.extension'); + if (ExtensiondistributorHelper::checkArray($_extension)) + { + $id .= ':' . implode(':', $_extension); + } + // Check if this is only an number or string + elseif (is_numeric($_extension) + || ExtensiondistributorHelper::checkString($_extension)) + { + $id .= ':' . $_extension; + } + // Check if the value is an array + $_package = $this->getState('filter.package'); + if (ExtensiondistributorHelper::checkArray($_package)) + { + $id .= ':' . implode(':', $_package); + } + // Check if this is only an number or string + elseif (is_numeric($_package) + || ExtensiondistributorHelper::checkString($_package)) + { + $id .= ':' . $_package; + } + // Check if the value is an array + $_reltype = $this->getState('filter.reltype'); + if (ExtensiondistributorHelper::checkArray($_reltype)) + { + $id .= ':' . implode(':', $_reltype); + } + // Check if this is only an number or string + elseif (is_numeric($_reltype) + || ExtensiondistributorHelper::checkString($_reltype)) + { + $id .= ':' . $_reltype; + } + // Check if the value is an array + $_stability = $this->getState('filter.stability'); + if (ExtensiondistributorHelper::checkArray($_stability)) + { + $id .= ':' . implode(':', $_stability); + } + // Check if this is only an number or string + elseif (is_numeric($_stability) + || ExtensiondistributorHelper::checkString($_stability)) + { + $id .= ':' . $_stability; + } + $id .= ':' . $this->getState('filter.version_number'); + $id .= ':' . $this->getState('filter.release_date'); + + return parent::getStoreId($id); + } + + /** + * Build an SQL query to checkin all items left checked out longer then a set time. + * + * @return a bool + * + */ + protected function checkInNow() + { + // Get set check in time + $time = JComponentHelper::getParams('com_extensiondistributor')->get('check_in'); + + if ($time) + { + + // Get a db connection. + $db = JFactory::getDbo(); + // Reset query. + $query = $db->getQuery(true); + $query->select('*'); + $query->from($db->quoteName('#__extensiondistributor_release')); + // Only select items that are checked out. + $query->where($db->quoteName('checked_out') . '!=0'); + $db->setQuery($query, 0, 1); + $db->execute(); + if ($db->getNumRows()) + { + // Get Yesterdays date. + $date = JFactory::getDate()->modify($time)->toSql(); + // Reset query. + $query = $db->getQuery(true); + + // Fields to update. + $fields = array( + $db->quoteName('checked_out_time') . '=\'0000-00-00 00:00:00\'', + $db->quoteName('checked_out') . '=0' + ); + + // Conditions for which records should be updated. + $conditions = array( + $db->quoteName('checked_out') . '!=0', + $db->quoteName('checked_out_time') . '<\''.$date.'\'' + ); + + // Check table. + $query->update($db->quoteName('#__extensiondistributor_release'))->set($fields)->where($conditions); + + $db->setQuery($query); + + $db->execute(); + } + } + + return false; + } +} diff --git a/admin/models/rules/code.php b/admin/models/rules/code.php new file mode 100644 index 0000000..dbcf292 --- /dev/null +++ b/admin/models/rules/code.php @@ -0,0 +1,62 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('JPATH_PLATFORM') or die; + +use Joomla\CMS\Form\Form; +use Joomla\CMS\Form\FormRule; +use Joomla\Registry\Registry; + +/** + * Form Rule (Code) class for the Joomla Platform. + */ +class JFormRuleCode extends FormRule +{ + /** + * Method to test the value. + * + * @param \SimpleXMLElement $element The SimpleXMLElement object representing the `` tag for the form field object. + * @param mixed $value The form field value to validate. + * @param string $group The field name group control value. This acts as an array container for the field. + * For example if the field has name="foo" and the group value is set to "bar" then the + * full field name would end up being "bar[foo]". + * @param Registry $input An optional Registry object with the entire data set to validate against the entire form. + * @param Form $form The form object for which the field is being tested. + * + * @return boolean True if the value is valid, false otherwise. + */ + public function test(\SimpleXMLElement $element, $value, $group = null, Registry $input = null, Form $form = null) + { + // This removes all validation (is dangerous) but needed to submit code via JCB + return true; + + /** + * My idea is to add some kind of validation to improve JCB code (per/language) + * + * So at this time this code validation is used for JavaScript,CSS,HTML and PHP. + * We can see what language is being worked on with the syntax property in the $element. (in JCB) + * What complicates things is the placeholders, of both custom code, component, and view names. + * Ideally we could strip them and then validate the code to being syntactically correct. + * But since some of the placeholders form part of the class/function names and the more, it seems like we are pressed for a much more advance solution. + * If you have any ideas to how we can go about to do this, then please open an issue on github and lets begin. (this is a nice to have, so don't break a leg...) + */ + } +} diff --git a/admin/models/rules/index.html b/admin/models/rules/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/models/rules/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/models/statistics.php b/admin/models/statistics.php new file mode 100644 index 0000000..1cbb226 --- /dev/null +++ b/admin/models/statistics.php @@ -0,0 +1,159 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ItemModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Statistics Item Model + */ +class ExtensiondistributorModelStatistics extends ItemModel +{ + /** + * Model context string. + * + * @var string + */ + protected $_context = 'com_extensiondistributor.statistics'; + + /** + * Model user data. + * + * @var strings + */ + protected $user; + protected $userId; + protected $guest; + protected $groups; + protected $levels; + protected $app; + protected $input; + protected $uikitComp; + + /** + * @var object item + */ + protected $item; + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @since 1.6 + * + * @return void + */ + protected function populateState() + { + $this->app = JFactory::getApplication(); + $this->input = $this->app->input; + // Get the item main id + $id = $this->input->getInt('id', null); + $this->setState('statistics.id', $id); + + // Load the parameters. + parent::populateState(); + } + + /** + * Method to get article data. + * + * @param integer $pk The id of the article. + * + * @return mixed Menu item data object on success, false on failure. + */ + public function getItem($pk = null) + { + $this->user = JFactory::getUser(); + // check if this user has permission to access item + if (!$this->user->authorise('statistics.access', 'com_extensiondistributor')) + { + $app = JFactory::getApplication(); + $app->enqueueMessage(JText::_('Not authorised!'), 'error'); + // redirect away if not a correct to cPanel/default view + $app->redirect('index.php?option=com_extensiondistributor'); + return false; + } + $this->userId = $this->user->get('id'); + $this->guest = $this->user->get('guest'); + $this->groups = $this->user->get('groups'); + $this->authorisedGroups = $this->user->getAuthorisedGroups(); + $this->levels = $this->user->getAuthorisedViewLevels(); + $this->initSet = true; + + $pk = (!empty($pk)) ? $pk : (int) $this->getState('statistics.id'); + + if ($this->_item === null) + { + $this->_item = array(); + } + + if (!isset($this->_item[$pk])) + { + try + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Get data +return false; + + // Reset the query using our newly populated query object. + $db->setQuery($query); + // Load the results as a stdClass object. + $data = $db->loadObject(); + + if (empty($data)) + { + $app = JFactory::getApplication(); + // If no data is found redirect to default page and show warning. + $app->enqueueMessage(JText::_('COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED'), 'warning'); + $app->redirect('index.php?option=com_extensiondistributor'); + return false; + } + + // set data object to item. + $this->_item[$pk] = $data; + } + catch (Exception $e) + { + if ($e->getCode() == 404) + { + // Need to go thru the error handler to allow Redirect to work. + JError::raiseWarning(404, $e->getMessage()); + } + else + { + $this->setError($e); + $this->_item[$pk] = false; + } + } + } + + return $this->_item[$pk]; + } +} diff --git a/admin/models/subscription.php b/admin/models/subscription.php new file mode 100644 index 0000000..c5080bf --- /dev/null +++ b/admin/models/subscription.php @@ -0,0 +1,895 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\AdminModel; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Subscription Admin Model + */ +class ExtensiondistributorModelSubscription extends AdminModel +{ + /** + * The tab layout fields array. + * + * @var array + */ + protected $tabLayoutFields = array( + 'details' => array( + 'left' => array( + 'user', + 'package', + 'date_start', + 'date_expire', + 'active' + ), + 'right' => array( + 'comment' + ) + ) + ); + + /** + * @var string The prefix to use with controller messages. + * @since 1.6 + */ + protected $text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + /** + * The type alias for this content type. + * + * @var string + * @since 3.2 + */ + public $typeAlias = 'com_extensiondistributor.subscription'; + + /** + * Returns a Table object, always creating it + * + * @param type $type The table type to instantiate + * @param string $prefix A prefix for the table class name. Optional. + * @param array $config Configuration array for model. Optional. + * + * @return JTable A database object + * + * @since 1.6 + */ + public function getTable($type = 'subscription', $prefix = 'ExtensiondistributorTable', $config = array()) + { + // add table path for when model gets used from other component + $this->addTablePath(JPATH_ADMINISTRATOR . '/components/com_extensiondistributor/tables'); + // get instance of the table + return JTable::getInstance($type, $prefix, $config); + } + + /** + * Method to get a single record. + * + * @param integer $pk The id of the primary key. + * + * @return mixed Object on success, false on failure. + * + * @since 1.6 + */ + public function getItem($pk = null) + { + if ($item = parent::getItem($pk)) + { + if (!empty($item->params) && !is_array($item->params)) + { + // Convert the params field to an array. + $registry = new Registry; + $registry->loadString($item->params); + $item->params = $registry->toArray(); + } + + if (!empty($item->metadata)) + { + // Convert the metadata field to an array. + $registry = new Registry; + $registry->loadString($item->metadata); + $item->metadata = $registry->toArray(); + } + // Not needed + // Not needed + } + + return $item; + } + + /** + * Method to get the record form. + * + * @param array $data Data for the form. + * @param boolean $loadData True if the form is to load its own data (default case), false if not. + * @param array $options Optional array of options for the form creation. + * + * @return mixed A JForm object on success, false on failure + * + * @since 1.6 + */ + public function getForm($data = array(), $loadData = true, $options = array('control' => 'jform')) + { + // set load data option + $options['load_data'] = $loadData; + // check if xpath was set in options + $xpath = false; + if (isset($options['xpath'])) + { + $xpath = $options['xpath']; + unset($options['xpath']); + } + // check if clear form was set in options + $clear = false; + if (isset($options['clear'])) + { + $clear = $options['clear']; + unset($options['clear']); + } + + // Get the form. + $form = $this->loadForm('com_extensiondistributor.subscription', 'subscription', $options, $clear, $xpath); + + if (empty($form)) + { + return false; + } + + $jinput = JFactory::getApplication()->input; + + // The front end calls this model and uses a_id to avoid id clashes so we need to check for that first. + if ($jinput->get('a_id')) + { + $id = $jinput->get('a_id', 0, 'INT'); + } + // The back end uses id so we use that the rest of the time and set it to 0 by default. + else + { + $id = $jinput->get('id', 0, 'INT'); + } + + $user = JFactory::getUser(); + + // Check for existing item. + // Modify the form based on Edit State access controls. + if ($id != 0 && (!$user->authorise('core.edit.state', 'com_extensiondistributor.subscription.' . (int) $id)) + || ($id == 0 && !$user->authorise('core.edit.state', 'com_extensiondistributor'))) + { + // Disable fields for display. + $form->setFieldAttribute('ordering', 'disabled', 'true'); + $form->setFieldAttribute('published', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('ordering', 'filter', 'unset'); + $form->setFieldAttribute('published', 'filter', 'unset'); + } + // If this is a new item insure the greated by is set. + if (0 == $id) + { + // Set the created_by to this user + $form->setValue('created_by', null, $user->id); + } + // Modify the form based on Edit Creaded By access controls. + if (!$user->authorise('core.edit.created_by', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created_by', 'disabled', 'true'); + // Disable fields for display. + $form->setFieldAttribute('created_by', 'readonly', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created_by', 'filter', 'unset'); + } + // Modify the form based on Edit Creaded Date access controls. + if (!$user->authorise('core.edit.created', 'com_extensiondistributor')) + { + // Disable fields for display. + $form->setFieldAttribute('created', 'disabled', 'true'); + // Disable fields while saving. + $form->setFieldAttribute('created', 'filter', 'unset'); + } + // Only load these values if no id is found + if (0 == $id) + { + // Set redirected view name + $redirectedView = $jinput->get('ref', null, 'STRING'); + // Set field name (or fall back to view name) + $redirectedField = $jinput->get('field', $redirectedView, 'STRING'); + // Set redirected view id + $redirectedId = $jinput->get('refid', 0, 'INT'); + // Set field id (or fall back to redirected view id) + $redirectedValue = $jinput->get('field_id', $redirectedId, 'INT'); + if (0 != $redirectedValue && $redirectedField) + { + // Now set the local-redirected field default value + $form->setValue($redirectedField, null, $redirectedValue); + } + } + return $form; + } + + /** + * Method to get the script that have to be included on the form + * + * @return string script files + */ + public function getScript() + { + return 'media/com_extensiondistributor/js/subscription.js'; + } + + /** + * Method to test whether a record can be deleted. + * + * @param object $record A record object. + * + * @return boolean True if allowed to delete the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canDelete($record) + { + if (!empty($record->id)) + { + if ($record->published != -2) + { + return; + } + + $user = JFactory::getUser(); + // The record has been set. Check the record permissions. + return $user->authorise('core.delete', 'com_extensiondistributor.subscription.' . (int) $record->id); + } + return false; + } + + /** + * Method to test whether a record can have its state edited. + * + * @param object $record A record object. + * + * @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component. + * + * @since 1.6 + */ + protected function canEditState($record) + { + $user = JFactory::getUser(); + $recordId = (!empty($record->id)) ? $record->id : 0; + + if ($recordId) + { + // The record has been set. Check the record permissions. + $permission = $user->authorise('core.edit.state', 'com_extensiondistributor.subscription.' . (int) $recordId); + if (!$permission && !is_null($permission)) + { + return false; + } + } + // In the absense of better information, revert to the component permissions. + return parent::canEditState($record); + } + + /** + * Method override to check if you can edit an existing record. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * @since 2.5 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // Check specific edit permission then general edit permission. + + return JFactory::getUser()->authorise('core.edit', 'com_extensiondistributor.subscription.'. ((int) isset($data[$key]) ? $data[$key] : 0)) or parent::allowEdit($data, $key); + } + + /** + * Prepare and sanitise the table data prior to saving. + * + * @param JTable $table A JTable object. + * + * @return void + * + * @since 1.6 + */ + protected function prepareTable($table) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if (isset($table->name)) + { + $table->name = htmlspecialchars_decode($table->name, ENT_QUOTES); + } + + if (isset($table->alias) && empty($table->alias)) + { + $table->generateAlias(); + } + + if (empty($table->id)) + { + $table->created = $date->toSql(); + // set the user + if ($table->created_by == 0 || empty($table->created_by)) + { + $table->created_by = $user->id; + } + // Set ordering to the last item if not set + if (empty($table->ordering)) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select('MAX(ordering)') + ->from($db->quoteName('#__extensiondistributor_subscription')); + $db->setQuery($query); + $max = $db->loadResult(); + + $table->ordering = $max + 1; + } + } + else + { + $table->modified = $date->toSql(); + $table->modified_by = $user->id; + } + + if (!empty($table->id)) + { + // Increment the items version number. + $table->version++; + } + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since 1.6 + */ + protected function loadFormData() + { + // Check the session for previously entered form data. + $data = JFactory::getApplication()->getUserState('com_extensiondistributor.edit.subscription.data', array()); + + if (empty($data)) + { + $data = $this->getItem(); + // run the perprocess of the data + $this->preprocessData('com_extensiondistributor.subscription', $data); + } + + return $data; + } + + /** + * Method to get the unique fields of this table. + * + * @return mixed An array of field names, boolean false if none is set. + * + * @since 3.0 + */ + protected function getUniqueFields() + { + return false; + } + + /** + * Method to delete one or more records. + * + * @param array &$pks An array of record primary keys. + * + * @return boolean True if successful, false if an error occurs. + * + * @since 12.2 + */ + public function delete(&$pks) + { + if (!parent::delete($pks)) + { + return false; + } + + return true; + } + + /** + * Method to change the published state of one or more records. + * + * @param array &$pks A list of the primary keys to change. + * @param integer $value The value of the published state. + * + * @return boolean True on success. + * + * @since 12.2 + */ + public function publish(&$pks, $value = 1) + { + if (!parent::publish($pks, $value)) + { + return false; + } + + return true; + } + + /** + * Method to perform batch operations on an item or a set of items. + * + * @param array $commands An array of commands to perform. + * @param array $pks An array of item ids. + * @param array $contexts An array of item contexts. + * + * @return boolean Returns true on success, false on failure. + * + * @since 12.2 + */ + public function batch($commands, $pks, $contexts) + { + // Sanitize ids. + $pks = array_unique($pks); + ArrayHelper::toInteger($pks); + + // Remove any values of zero. + if (array_search(0, $pks, true)) + { + unset($pks[array_search(0, $pks, true)]); + } + + if (empty($pks)) + { + $this->setError(JText::_('JGLOBAL_NO_ITEM_SELECTED')); + return false; + } + + $done = false; + + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->contentType = new JUcmType; + $this->type = $this->contentType->getTypeByTable($this->tableClassName); + $this->canDo = ExtensiondistributorHelper::getActions('subscription'); + $this->batchSet = true; + + if (!$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + return false; + } + + if ($this->type == false) + { + $type = new JUcmType; + $this->type = $type->getTypeByAlias($this->typeAlias); + } + + $this->tagsObserver = $this->table->getObserverOfClass('JTableObserverTags'); + + if (!empty($commands['move_copy'])) + { + $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c'); + + if ($cmd == 'c') + { + $result = $this->batchCopy($commands, $pks, $contexts); + + if (is_array($result)) + { + foreach ($result as $old => $new) + { + $contexts[$new] = $contexts[$old]; + } + $pks = array_values($result); + } + else + { + return false; + } + } + elseif ($cmd == 'm' && !$this->batchMove($commands, $pks, $contexts)) + { + return false; + } + + $done = true; + } + + if (!$done) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); + + return false; + } + + // Clear the cache + $this->cleanCache(); + + return true; + } + + /** + * Batch copy items to a new category or current. + * + * @param integer $values The new values. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return mixed An array of new IDs on success, boolean false on failure. + * + * @since 12.2 + */ + protected function batchCopy($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('subscription'); + } + + if (!$this->canDo->get('core.create') || !$this->canDo->get('core.batch')) + { + return false; + } + + // get list of unique fields + $uniqueFields = $this->getUniqueFields(); + // remove move_copy from array + unset($values['move_copy']); + + // make sure published is set + if (!isset($values['published'])) + { + $values['published'] = 0; + } + elseif (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + $values['published'] = 0; + } + + $newIds = array(); + // Parent exists so let's proceed + while (!empty($pks)) + { + // Pop the first ID off the stack + $pk = array_shift($pks); + + $this->table->reset(); + + // only allow copy if user may edit this item. + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // Only for strings + if (ExtensiondistributorHelper::checkString($this->table->date_start) && !is_numeric($this->table->date_start)) + { + $this->table->date_start = $this->generateUnique('date_start',$this->table->date_start); + } + + // insert all set values + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + if (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + // update all unique fields + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $this->table->$uniqueField = $this->generateUnique($uniqueField,$this->table->$uniqueField); + } + } + + // Reset the ID because we are making a copy + $this->table->id = 0; + + // TODO: Deal with ordering? + // $this->table->ordering = 1; + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + + // Get the new item ID + $newId = $this->table->get('id'); + + // Add the new ID to the array + $newIds[$pk] = $newId; + } + + // Clean the cache + $this->cleanCache(); + + return $newIds; + } + + /** + * Batch move items to a new category + * + * @param integer $value The new category ID. + * @param array $pks An array of row IDs. + * @param array $contexts An array of item contexts. + * + * @return boolean True if successful, false otherwise and internal error is set. + * + * @since 12.2 + */ + protected function batchMove($values, $pks, $contexts) + { + if (empty($this->batchSet)) + { + // Set some needed variables. + $this->user = JFactory::getUser(); + $this->table = $this->getTable(); + $this->tableClassName = get_class($this->table); + $this->canDo = ExtensiondistributorHelper::getActions('subscription'); + } + + if (!$this->canDo->get('core.edit') && !$this->canDo->get('core.batch')) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // make sure published only updates if user has the permission. + if (isset($values['published']) && !$this->canDo->get('core.edit.state')) + { + unset($values['published']); + } + // remove move_copy from array + unset($values['move_copy']); + + // Parent exists so we proceed + foreach ($pks as $pk) + { + if (!$this->user->authorise('core.edit', $contexts[$pk])) + { + $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); + return false; + } + + // Check that the row actually exists + if (!$this->table->load($pk)) + { + if ($error = $this->table->getError()) + { + // Fatal error + $this->setError($error); + return false; + } + else + { + // Not fatal error + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); + continue; + } + } + + // insert all set values. + if (ExtensiondistributorHelper::checkArray($values)) + { + foreach ($values as $key => $value) + { + // Do special action for access. + if ('access' === $key && strlen($value) > 0) + { + $this->table->$key = $value; + } + elseif (strlen($value) > 0 && isset($this->table->$key)) + { + $this->table->$key = $value; + } + } + } + + + // Check the row. + if (!$this->table->check()) + { + $this->setError($this->table->getError()); + + return false; + } + + if (!empty($this->type)) + { + $this->createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table); + } + + // Store the row. + if (!$this->table->store()) + { + $this->setError($this->table->getError()); + + return false; + } + } + + // Clean the cache + $this->cleanCache(); + + return true; + } + + /** + * Method to save the form data. + * + * @param array $data The form data. + * + * @return boolean True on success. + * + * @since 1.6 + */ + public function save($data) + { + $input = JFactory::getApplication()->input; + $filter = JFilterInput::getInstance(); + + // set the metadata to the Item Data + if (isset($data['metadata']) && isset($data['metadata']['author'])) + { + $data['metadata']['author'] = $filter->clean($data['metadata']['author'], 'TRIM'); + + $metadata = new JRegistry; + $metadata->loadArray($data['metadata']); + $data['metadata'] = (string) $metadata; + } + if (isset($data['date_start']) && ($data['date_start'] != '0000-00-00 00:00:00') && ($data['date_start'] != '')) + { + $data['date_start'] = JFactory::getDate($data['date_start'])->toSql(); + } + + if (isset($data['date_expire']) && ($data['date_expire'] != '0000-00-00 00:00:00') && ($data['date_expire'] != '')) + { + $data['date_expire'] = JFactory::getDate($data['date_expire'])->toSql(); + } + + + // Set the Params Items to data + if (isset($data['params']) && is_array($data['params'])) + { + $params = new JRegistry; + $params->loadArray($data['params']); + $data['params'] = (string) $params; + } + + // Alter the unique field for save as copy + if ($input->get('task') === 'save2copy') + { + // Automatic handling of other unique fields + $uniqueFields = $this->getUniqueFields(); + if (ExtensiondistributorHelper::checkArray($uniqueFields)) + { + foreach ($uniqueFields as $uniqueField) + { + $data[$uniqueField] = $this->generateUnique($uniqueField,$data[$uniqueField]); + } + } + } + + if (parent::save($data)) + { + return true; + } + return false; + } + + /** + * Method to generate a unique value. + * + * @param string $field name. + * @param string $value data. + * + * @return string New value. + * + * @since 3.0 + */ + protected function generateUnique($field,$value) + { + + // set field value unique + $table = $this->getTable(); + + while ($table->load(array($field => $value))) + { + $value = StringHelper::increment($value); + } + + return $value; + } + + /** + * Method to change the title + * + * @param string $title The title. + * + * @return array Contains the modified title and alias. + * + */ + protected function _generateNewTitle($title) + { + + // Alter the title + $table = $this->getTable(); + + while ($table->load(array('title' => $title))) + { + $title = StringHelper::increment($title); + } + + return $title; + } +} diff --git a/admin/models/subscriptions.php b/admin/models/subscriptions.php new file mode 100644 index 0000000..168400a --- /dev/null +++ b/admin/models/subscriptions.php @@ -0,0 +1,461 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ListModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Subscriptions List Model + */ +class ExtensiondistributorModelSubscriptions extends ListModel +{ + public function __construct($config = array()) + { + if (empty($config['filter_fields'])) + { + $config['filter_fields'] = array( + 'a.id','id', + 'a.published','published', + 'a.ordering','ordering', + 'a.created_by','created_by', + 'a.modified_by','modified_by', + 'a.user','user', + 'g.name','package', + 'a.active','active', + 'a.date_start','date_start', + 'a.date_expire','date_expire' + ); + } + + parent::__construct($config); + } + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @param string $ordering An optional ordering field. + * @param string $direction An optional direction (asc|desc). + * + * @return void + * + */ + protected function populateState($ordering = null, $direction = null) + { + $app = JFactory::getApplication(); + + // Adjust the context to support modal layouts. + if ($layout = $app->input->get('layout')) + { + $this->context .= '.' . $layout; + } + + // Check if the form was submitted + $formSubmited = $app->input->post->get('form_submited'); + + $access = $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access', 0, 'int'); + if ($formSubmited) + { + $access = $app->input->post->get('access'); + $this->setState('filter.access', $access); + } + + $published = $this->getUserStateFromRequest($this->context . '.filter.published', 'filter_published', ''); + $this->setState('filter.published', $published); + + $created_by = $this->getUserStateFromRequest($this->context . '.filter.created_by', 'filter_created_by', ''); + $this->setState('filter.created_by', $created_by); + + $created = $this->getUserStateFromRequest($this->context . '.filter.created', 'filter_created'); + $this->setState('filter.created', $created); + + $sorting = $this->getUserStateFromRequest($this->context . '.filter.sorting', 'filter_sorting', 0, 'int'); + $this->setState('filter.sorting', $sorting); + + $search = $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search'); + $this->setState('filter.search', $search); + + $user = $this->getUserStateFromRequest($this->context . '.filter.user', 'filter_user'); + if ($formSubmited) + { + $user = $app->input->post->get('user'); + $this->setState('filter.user', $user); + } + + $package = $this->getUserStateFromRequest($this->context . '.filter.package', 'filter_package'); + if ($formSubmited) + { + $package = $app->input->post->get('package'); + $this->setState('filter.package', $package); + } + + $active = $this->getUserStateFromRequest($this->context . '.filter.active', 'filter_active'); + if ($formSubmited) + { + $active = $app->input->post->get('active'); + $this->setState('filter.active', $active); + } + + $date_start = $this->getUserStateFromRequest($this->context . '.filter.date_start', 'filter_date_start'); + if ($formSubmited) + { + $date_start = $app->input->post->get('date_start'); + $this->setState('filter.date_start', $date_start); + } + + $date_expire = $this->getUserStateFromRequest($this->context . '.filter.date_expire', 'filter_date_expire'); + if ($formSubmited) + { + $date_expire = $app->input->post->get('date_expire'); + $this->setState('filter.date_expire', $date_expire); + } + + // List state information. + parent::populateState($ordering, $direction); + } + + /** + * Method to get an array of data items. + * + * @return mixed An array of data items on success, false on failure. + */ + public function getItems() + { + // Check in items + $this->checkInNow(); + + // load parent items + $items = parent::getItems(); + + // Set values to display correctly. + if (ExtensiondistributorHelper::checkArray($items)) + { + // Get the user object if not set. + if (!isset($user) || !ExtensiondistributorHelper::checkObject($user)) + { + $user = JFactory::getUser(); + } + foreach ($items as $nr => &$item) + { + // Not needed + // Not needed + } + } + + // set selection value to a translatable value + if (ExtensiondistributorHelper::checkArray($items)) + { + foreach ($items as $nr => &$item) + { + // convert active + $item->active = $this->selectionTranslation($item->active, 'active'); + } + } + + + // return items + return $items; + } + + /** + * Method to convert selection values to translatable string. + * + * @return translatable string + */ + public function selectionTranslation($value,$name) + { + // Array of active language strings + if ($name === 'active') + { + $activeArray = array( + 1 => 'COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_YES', + 0 => 'COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_NO' + ); + // Now check if value is found in this array + if (isset($activeArray[$value]) && ExtensiondistributorHelper::checkString($activeArray[$value])) + { + return $activeArray[$value]; + } + } + return $value; + } + + /** + * Method to build an SQL query to load the list data. + * + * @return string An SQL query + */ + protected function getListQuery() + { + // Get the user object. + $user = JFactory::getUser(); + // Create a new query object. + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + + // Select some fields + $query->select('a.*'); + + // From the extensiondistributor_item table + $query->from($db->quoteName('#__extensiondistributor_subscription', 'a')); + + // From the extensiondistributor_package table. + $query->select($db->quoteName('g.name','package_name')); + $query->join('LEFT', $db->quoteName('#__extensiondistributor_package', 'g') . ' ON (' . $db->quoteName('a.package') . ' = ' . $db->quoteName('g.id') . ')'); + + // Filter by published state + $published = $this->getState('filter.published'); + if (is_numeric($published)) + { + $query->where('a.published = ' . (int) $published); + } + elseif ($published === '') + { + $query->where('(a.published = 0 OR a.published = 1)'); + } + // Filter by search. + $search = $this->getState('filter.search'); + if (!empty($search)) + { + if (stripos($search, 'id:') === 0) + { + $query->where('a.id = ' . (int) substr($search, 3)); + } + else + { + $search = $db->quote('%' . $db->escape($search) . '%'); + $query->where('(a.user LIKE '.$search.' OR a.package LIKE '.$search.' OR g.name LIKE '.$search.' OR a.date_start LIKE '.$search.' OR a.date_expire LIKE '.$search.' OR a.active LIKE '.$search.' OR a.comment LIKE '.$search.')'); + } + } + + // Filter by User. + $_user = $this->getState('filter.user'); + if (is_numeric($_user)) + { + if (is_float($_user)) + { + $query->where('a.user = ' . (float) $_user); + } + else + { + $query->where('a.user = ' . (int) $_user); + } + } + elseif (ExtensiondistributorHelper::checkString($_user)) + { + $query->where('a.user = ' . $db->quote($db->escape($_user))); + } + elseif (ExtensiondistributorHelper::checkArray($_user)) + { + // Secure the array for the query + $_user = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_user); + // Filter by the User Array. + $query->where('a.user IN (' . implode(',', $_user) . ')'); + } + // Filter by Package. + $_package = $this->getState('filter.package'); + if (is_numeric($_package)) + { + if (is_float($_package)) + { + $query->where('a.package = ' . (float) $_package); + } + else + { + $query->where('a.package = ' . (int) $_package); + } + } + elseif (ExtensiondistributorHelper::checkString($_package)) + { + $query->where('a.package = ' . $db->quote($db->escape($_package))); + } + elseif (ExtensiondistributorHelper::checkArray($_package)) + { + // Secure the array for the query + $_package = array_map( function ($val) use(&$db) { + if (is_numeric($val)) + { + if (is_float($val)) + { + return (float) $val; + } + else + { + return (int) $val; + } + } + elseif (ExtensiondistributorHelper::checkString($val)) + { + return $db->quote($db->escape($val)); + } + }, $_package); + // Filter by the Package Array. + $query->where('a.package IN (' . implode(',', $_package) . ')'); + } + // Filter by Active. + $_active = $this->getState('filter.active'); + if (is_numeric($_active)) + { + if (is_float($_active)) + { + $query->where('a.active = ' . (float) $_active); + } + else + { + $query->where('a.active = ' . (int) $_active); + } + } + elseif (ExtensiondistributorHelper::checkString($_active)) + { + $query->where('a.active = ' . $db->quote($db->escape($_active))); + } + + // Add the list ordering clause. + $orderCol = $this->state->get('list.ordering', 'a.id'); + $orderDirn = $this->state->get('list.direction', 'desc'); + if ($orderCol != '') + { + $query->order($db->escape($orderCol . ' ' . $orderDirn)); + } + + return $query; + } + + /** + * Method to get a store id based on model configuration state. + * + * @return string A store id. + * + */ + protected function getStoreId($id = '') + { + // Compile the store id. + $id .= ':' . $this->getState('filter.id'); + $id .= ':' . $this->getState('filter.search'); + $id .= ':' . $this->getState('filter.published'); + $id .= ':' . $this->getState('filter.ordering'); + $id .= ':' . $this->getState('filter.created_by'); + $id .= ':' . $this->getState('filter.modified_by'); + // Check if the value is an array + $_user = $this->getState('filter.user'); + if (ExtensiondistributorHelper::checkArray($_user)) + { + $id .= ':' . implode(':', $_user); + } + // Check if this is only an number or string + elseif (is_numeric($_user) + || ExtensiondistributorHelper::checkString($_user)) + { + $id .= ':' . $_user; + } + // Check if the value is an array + $_package = $this->getState('filter.package'); + if (ExtensiondistributorHelper::checkArray($_package)) + { + $id .= ':' . implode(':', $_package); + } + // Check if this is only an number or string + elseif (is_numeric($_package) + || ExtensiondistributorHelper::checkString($_package)) + { + $id .= ':' . $_package; + } + $id .= ':' . $this->getState('filter.active'); + $id .= ':' . $this->getState('filter.date_start'); + $id .= ':' . $this->getState('filter.date_expire'); + + return parent::getStoreId($id); + } + + /** + * Build an SQL query to checkin all items left checked out longer then a set time. + * + * @return a bool + * + */ + protected function checkInNow() + { + // Get set check in time + $time = JComponentHelper::getParams('com_extensiondistributor')->get('check_in'); + + if ($time) + { + + // Get a db connection. + $db = JFactory::getDbo(); + // Reset query. + $query = $db->getQuery(true); + $query->select('*'); + $query->from($db->quoteName('#__extensiondistributor_subscription')); + // Only select items that are checked out. + $query->where($db->quoteName('checked_out') . '!=0'); + $db->setQuery($query, 0, 1); + $db->execute(); + if ($db->getNumRows()) + { + // Get Yesterdays date. + $date = JFactory::getDate()->modify($time)->toSql(); + // Reset query. + $query = $db->getQuery(true); + + // Fields to update. + $fields = array( + $db->quoteName('checked_out_time') . '=\'0000-00-00 00:00:00\'', + $db->quoteName('checked_out') . '=0' + ); + + // Conditions for which records should be updated. + $conditions = array( + $db->quoteName('checked_out') . '!=0', + $db->quoteName('checked_out_time') . '<\''.$date.'\'' + ); + + // Check table. + $query->update($db->quoteName('#__extensiondistributor_subscription'))->set($fields)->where($conditions); + + $db->setQuery($query); + + $db->execute(); + } + } + + return false; + } +} diff --git a/admin/sql/index.html b/admin/sql/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/sql/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/sql/install.mysql.utf8.sql b/admin/sql/install.mysql.utf8.sql new file mode 100644 index 0000000..0bb2443 --- /dev/null +++ b/admin/sql/install.mysql.utf8.sql @@ -0,0 +1,243 @@ +CREATE TABLE IF NOT EXISTS `#__extensiondistributor_package` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `asset_id` INT(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to the #__assets table.', + `add_php_method_uninstall` TINYINT(1) NOT NULL DEFAULT 0, + `add_php_postflight_install` TINYINT(1) NOT NULL DEFAULT 0, + `add_php_postflight_update` TINYINT(1) NOT NULL DEFAULT 0, + `add_php_preflight_install` TINYINT(1) NOT NULL DEFAULT 0, + `add_php_preflight_uninstall` TINYINT(1) NOT NULL DEFAULT 0, + `add_php_preflight_update` TINYINT(1) NOT NULL DEFAULT 0, + `alias` VARCHAR(255) NOT NULL DEFAULT '', + `author` VARCHAR(50) NOT NULL DEFAULT '', + `authoremail` VARCHAR(100) NOT NULL DEFAULT '', + `authorurl` VARCHAR(100) NOT NULL DEFAULT '', + `blockchilduninstall` TINYINT(1) NOT NULL DEFAULT 0, + `catid` INT(11) NOT NULL DEFAULT 0, + `copyright` VARCHAR(50) NOT NULL DEFAULT '', + `description` TEXT NOT NULL, + `file` VARCHAR(255) NOT NULL DEFAULT '', + `files` TEXT NOT NULL, + `free` TINYINT(1) NOT NULL DEFAULT 0, + `image` VARCHAR(255) NOT NULL DEFAULT '', + `infourl` VARCHAR(255) NOT NULL DEFAULT '', + `jed` VARCHAR(255) NOT NULL DEFAULT '', + `license` VARCHAR(50) NOT NULL DEFAULT '', + `name` VARCHAR(255) NOT NULL DEFAULT '', + `newfilename` VARCHAR(100) NOT NULL DEFAULT '', + `packagename` VARCHAR(50) NOT NULL DEFAULT '', + `packager` VARCHAR(50) NOT NULL DEFAULT '', + `packagerurl` VARCHAR(100) NOT NULL DEFAULT '', + `php_method_uninstall` MEDIUMTEXT NOT NULL, + `php_minimum` VARCHAR(64) NOT NULL DEFAULT '', + `php_postflight_install` MEDIUMTEXT NOT NULL, + `php_postflight_update` MEDIUMTEXT NOT NULL, + `php_preflight_install` MEDIUMTEXT NOT NULL, + `php_preflight_uninstall` MEDIUMTEXT NOT NULL, + `php_preflight_update` MEDIUMTEXT NOT NULL, + `release_date` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `targetplatform` VARCHAR(64) NOT NULL DEFAULT '', + `update` VARCHAR(255) NOT NULL DEFAULT '', + `useupdate` TINYINT(1) NOT NULL DEFAULT 0, + `version_number` VARCHAR(11) NOT NULL DEFAULT '', + `params` text NULL, + `published` TINYINT(3) NOT NULL DEFAULT 1, + `created_by` INT(10) unsigned NOT NULL DEFAULT 0, + `modified_by` INT(10) unsigned NOT NULL DEFAULT 0, + `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `checked_out` int(11) unsigned NOT NULL DEFAULT 0, + `checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `version` INT(10) unsigned NOT NULL DEFAULT 1, + `hits` INT(10) unsigned NOT NULL DEFAULT 0, + `access` INT(10) unsigned NOT NULL DEFAULT 0, + `ordering` INT(11) NOT NULL DEFAULT 0, + `metakey` TEXT NOT NULL, + `metadesc` TEXT NOT NULL, + `metadata` TEXT NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_name` (`name`), + KEY `idx_free` (`free`), + KEY `idx_add_php_method_uninstall` (`add_php_method_uninstall`), + KEY `idx_add_php_postflight_update` (`add_php_postflight_update`), + KEY `idx_add_php_postflight_install` (`add_php_postflight_install`), + KEY `idx_add_php_preflight_uninstall` (`add_php_preflight_uninstall`), + KEY `idx_useupdate` (`useupdate`), + KEY `idx_catid` (`catid`), + KEY `idx_blockchilduninstall` (`blockchilduninstall`), + KEY `idx_add_php_preflight_install` (`add_php_preflight_install`), + KEY `idx_add_php_preflight_update` (`add_php_preflight_update`), + KEY `idx_alias` (`alias`), + KEY `idx_access` (`access`), + KEY `idx_checkout` (`checked_out`), + KEY `idx_createdby` (`created_by`), + KEY `idx_modifiedby` (`modified_by`), + KEY `idx_state` (`published`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +CREATE TABLE IF NOT EXISTS `#__extensiondistributor_extension` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `asset_id` INT(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to the #__assets table.', + `alias` VARCHAR(255) NOT NULL DEFAULT '', + `catid` INT(11) NOT NULL DEFAULT 0, + `client` VARCHAR(50) NOT NULL DEFAULT '', + `description` TEXT NOT NULL, + `element` VARCHAR(50) NOT NULL DEFAULT '', + `group` VARCHAR(50) NOT NULL DEFAULT '', + `image` VARCHAR(255) NOT NULL DEFAULT '', + `keepfilename` TINYINT(1) NOT NULL DEFAULT 0, + `name` VARCHAR(255) NOT NULL DEFAULT '', + `newfilename` VARCHAR(100) NOT NULL DEFAULT '', + `release_date` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `tags` VARCHAR(1024) NOT NULL DEFAULT '', + `type` VARCHAR(11) NOT NULL DEFAULT '', + `update` VARCHAR(255) NOT NULL DEFAULT '', + `version_number` VARCHAR(11) NOT NULL DEFAULT '', + `params` text NULL, + `published` TINYINT(3) NOT NULL DEFAULT 1, + `created_by` INT(10) unsigned NOT NULL DEFAULT 0, + `modified_by` INT(10) unsigned NOT NULL DEFAULT 0, + `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `checked_out` int(11) unsigned NOT NULL DEFAULT 0, + `checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `version` INT(10) unsigned NOT NULL DEFAULT 1, + `hits` INT(10) unsigned NOT NULL DEFAULT 0, + `ordering` INT(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_name` (`name`), + KEY `idx_type` (`type`), + KEY `idx_alias` (`alias`), + KEY `idx_keepfilename` (`keepfilename`), + KEY `idx_catid` (`catid`), + KEY `idx_checkout` (`checked_out`), + KEY `idx_createdby` (`created_by`), + KEY `idx_modifiedby` (`modified_by`), + KEY `idx_state` (`published`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +CREATE TABLE IF NOT EXISTS `#__extensiondistributor_release` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `asset_id` INT(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to the #__assets table.', + `changelog` TEXT NOT NULL, + `description` TEXT NOT NULL, + `extension` INT(11) NOT NULL DEFAULT 0, + `file` VARCHAR(255) NOT NULL DEFAULT '', + `package` INT(11) NOT NULL DEFAULT 0, + `release_date` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `reltype` VARCHAR(11) NOT NULL DEFAULT '', + `stability` INT(11) NOT NULL DEFAULT 0, + `toolbar` VARCHAR(10) NOT NULL DEFAULT '', + `version_number` VARCHAR(11) NOT NULL DEFAULT '', + `xmlpublished` TINYINT(1) NOT NULL DEFAULT 0, + `zip_content` VARCHAR(1024) NOT NULL DEFAULT '', + `params` text NULL, + `published` TINYINT(3) NOT NULL DEFAULT 1, + `created_by` INT(10) unsigned NOT NULL DEFAULT 0, + `modified_by` INT(10) unsigned NOT NULL DEFAULT 0, + `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `checked_out` int(11) unsigned NOT NULL DEFAULT 0, + `checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `version` INT(10) unsigned NOT NULL DEFAULT 1, + `hits` INT(10) unsigned NOT NULL DEFAULT 0, + `ordering` INT(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_extension` (`extension`), + KEY `idx_package` (`package`), + KEY `idx_version_number` (`version_number`), + KEY `idx_reltype` (`reltype`), + KEY `idx_xmlpublished` (`xmlpublished`), + KEY `idx_stability` (`stability`), + KEY `idx_checkout` (`checked_out`), + KEY `idx_createdby` (`created_by`), + KEY `idx_modifiedby` (`modified_by`), + KEY `idx_state` (`published`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +CREATE TABLE IF NOT EXISTS `#__extensiondistributor_subscription` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `asset_id` INT(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to the #__assets table.', + `active` TINYINT(1) NOT NULL DEFAULT 0, + `comment` TEXT NOT NULL, + `date_expire` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `date_start` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `package` VARCHAR(255) NOT NULL DEFAULT '', + `user` INT(11) NOT NULL DEFAULT 0, + `params` text NULL, + `published` TINYINT(3) NOT NULL DEFAULT 1, + `created_by` INT(10) unsigned NOT NULL DEFAULT 0, + `modified_by` INT(10) unsigned NOT NULL DEFAULT 0, + `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `checked_out` int(11) unsigned NOT NULL DEFAULT 0, + `checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `version` INT(10) unsigned NOT NULL DEFAULT 1, + `hits` INT(10) unsigned NOT NULL DEFAULT 0, + `ordering` INT(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_user` (`user`), + KEY `idx_date_start` (`date_start`), + KEY `idx_active` (`active`), + KEY `idx_checkout` (`checked_out`), + KEY `idx_createdby` (`created_by`), + KEY `idx_modifiedby` (`modified_by`), + KEY `idx_state` (`published`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +CREATE TABLE IF NOT EXISTS `#__extensiondistributor_key` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `asset_id` INT(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to the #__assets table.', + `key` VARCHAR(64) NOT NULL DEFAULT '', + `user` INT(11) NOT NULL DEFAULT 0, + `website` VARCHAR(255) NOT NULL DEFAULT '', + `params` text NULL, + `published` TINYINT(3) NOT NULL DEFAULT 1, + `created_by` INT(10) unsigned NOT NULL DEFAULT 0, + `modified_by` INT(10) unsigned NOT NULL DEFAULT 0, + `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `checked_out` int(11) unsigned NOT NULL DEFAULT 0, + `checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `version` INT(10) unsigned NOT NULL DEFAULT 1, + `hits` INT(10) unsigned NOT NULL DEFAULT 0, + `ordering` INT(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_key` (`key`), + KEY `idx_user` (`user`), + KEY `idx_checkout` (`checked_out`), + KEY `idx_createdby` (`created_by`), + KEY `idx_modifiedby` (`modified_by`), + KEY `idx_state` (`published`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + +CREATE TABLE IF NOT EXISTS `#__extensiondistributor_download` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `asset_id` INT(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to the #__assets table.', + `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `domain` VARCHAR(64) NOT NULL DEFAULT '', + `ip` VARCHAR(50) NOT NULL DEFAULT '', + `key` VARCHAR(64) NOT NULL DEFAULT '', + `package` INT(11) NOT NULL DEFAULT 0, + `published` TINYINT(1) NOT NULL DEFAULT 0, + `transaction` VARCHAR(50) NOT NULL DEFAULT '', + `user` INT(11) NOT NULL DEFAULT 0, + `params` text NULL, + `created_by` INT(10) unsigned NOT NULL DEFAULT 0, + `modified_by` INT(10) unsigned NOT NULL DEFAULT 0, + `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `checked_out` int(11) unsigned NOT NULL DEFAULT 0, + `checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `version` INT(10) unsigned NOT NULL DEFAULT 1, + `hits` INT(10) unsigned NOT NULL DEFAULT 0, + `ordering` INT(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_transaction` (`transaction`), + KEY `idx_package` (`package`), + KEY `idx_user` (`user`), + KEY `idx_published` (`published`), + KEY `idx_checkout` (`checked_out`), + KEY `idx_createdby` (`created_by`), + KEY `idx_modifiedby` (`modified_by`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; + + diff --git a/admin/sql/uninstall.mysql.utf8.sql b/admin/sql/uninstall.mysql.utf8.sql new file mode 100644 index 0000000..e76740b --- /dev/null +++ b/admin/sql/uninstall.mysql.utf8.sql @@ -0,0 +1,7 @@ +DROP TABLE IF EXISTS `#__extensiondistributor_package`; +DROP TABLE IF EXISTS `#__extensiondistributor_extension`; +DROP TABLE IF EXISTS `#__extensiondistributor_release`; +DROP TABLE IF EXISTS `#__extensiondistributor_subscription`; +DROP TABLE IF EXISTS `#__extensiondistributor_key`; +DROP TABLE IF EXISTS `#__extensiondistributor_download`; + diff --git a/admin/sql/updates/index.html b/admin/sql/updates/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/sql/updates/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/sql/updates/mysql/1.0.0.sql b/admin/sql/updates/mysql/1.0.0.sql new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/admin/sql/updates/mysql/1.0.0.sql @@ -0,0 +1 @@ + diff --git a/admin/sql/updates/mysql/index.html b/admin/sql/updates/mysql/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/sql/updates/mysql/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/tables/download.php b/admin/tables/download.php new file mode 100644 index 0000000..e636923 --- /dev/null +++ b/admin/tables/download.php @@ -0,0 +1,330 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Table\Table; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Downloads Table class + */ +class ExtensiondistributorTableDownload extends Table +{ + /** + * Ensure the params and metadata in json encoded in the bind method + * + * @var array + * @since 3.3 + */ + protected $_jsonEncode = array('params', 'metadata'); + + /** + * Constructor + * + * @param object Database connector object + */ + function __construct(&$db) + { + parent::__construct('#__extensiondistributor_download', 'id', $db); + } + + public function bind($array, $ignore = '') + { + + if (isset($array['params']) && is_array($array['params'])) + { + $registry = new JRegistry; + $registry->loadArray($array['params']); + $array['params'] = (string) $registry; + } + + if (isset($array['metadata']) && is_array($array['metadata'])) + { + $registry = new JRegistry; + $registry->loadArray($array['metadata']); + $array['metadata'] = (string) $registry; + } + + // Bind the rules. + if (isset($array['rules']) && is_array($array['rules'])) + { + $rules = new JAccessRules($array['rules']); + $this->setRules($rules); + } + return parent::bind($array, $ignore); + } + + /** + * Overload the store method for the Download table. + * + * @param boolean Toggle whether null values should be updated. + * @return boolean True on success, false on failure. + * @since 1.6 + */ + public function store($updateNulls = false) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if ($this->id) + { + // Existing item + $this->modified = $date->toSql(); + $this->modified_by = $user->get('id'); + } + else + { + // New download. A download created and created_by field can be set by the user, + // so we don't touch either of these if they are set. + if (!(int) $this->created) + { + $this->created = $date->toSql(); + } + if (empty($this->created_by)) + { + $this->created_by = $user->get('id'); + } + } + + if (isset($this->alias)) + { + // Verify that the alias is unique + $table = JTable::getInstance('download', 'ExtensiondistributorTable'); + + if ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->setError(JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_ERROR_UNIQUE_ALIAS')); + return false; + } + } + + if (isset($this->url)) + { + // Convert IDN urls to punycode + $this->url = JStringPunycode::urlToPunycode($this->url); + } + if (isset($this->website)) + { + // Convert IDN urls to punycode + $this->website = JStringPunycode::urlToPunycode($this->website); + } + + return parent::store($updateNulls); + } + + /** + * Overloaded check method to ensure data integrity. + * + * @return boolean True on success. + */ + public function check() + { + if (isset($this->alias)) + { + // Generate a valid alias + $this->generateAlias(); + + $table = JTable::getInstance('download', 'extensiondistributorTable'); + + while ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->alias = StringHelper::increment($this->alias, 'dash'); + } + } + + /* + * Clean up keywords -- eliminate extra spaces between phrases + * and cr (\r) and lf (\n) characters from string. + * Only process if not empty. + */ + if (!empty($this->metakey)) + { + // Array of characters to remove. + $bad_characters = array("\n", "\r", "\"", "<", ">"); + + // Remove bad characters. + $after_clean = StringHelper::str_ireplace($bad_characters, "", $this->metakey); + + // Create array using commas as delimiter. + $keys = explode(',', $after_clean); + $clean_keys = array(); + + foreach ($keys as $key) + { + // Ignore blank keywords. + if (trim($key)) + { + $clean_keys[] = trim($key); + } + } + + // Put array back together delimited by ", " + $this->metakey = implode(", ", $clean_keys); + } + + // Clean up description -- eliminate quotes and <> brackets + if (!empty($this->metadesc)) + { + // Only process if not empty + $bad_characters = array("\"", "<", ">"); + $this->metadesc = StringHelper::str_ireplace($bad_characters, "", $this->metadesc); + } + + // If we don't have any access rules set at this point just use an empty JAccessRules class + if (!$this->getRules()) + { + $rules = $this->getDefaultAssetValues('com_extensiondistributor.download.'.$this->id); + $this->setRules($rules); + } + + // Set ordering + if ($this->published < 0) + { + // Set ordering to 0 if state is archived or trashed + $this->ordering = 0; + } + + return true; + } + + /** + * Gets the default asset values for a component. + * + * @param $string $component The component asset name to search for + * + * @return JAccessRules The JAccessRules object for the asset + */ + protected function getDefaultAssetValues($component, $try = true) + { + // Need to find the asset id by the name of the component. + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote($component)); + $db->setQuery($query); + $db->execute(); + if ($db->loadRowList()) + { + // asset already set so use saved rules + $assetId = (int) $db->loadResult(); + return JAccess::getAssetRules($assetId); // (TODO) instead of keeping inherited Allowed it becomes Allowed. + } + // try again + elseif ($try) + { + $try = explode('.',$component); + $result = $this->getDefaultAssetValues($try[0], false); + if ($result instanceof JAccessRules) + { + if (isset($try[1])) + { + $_result = (string) $result; + $_result = json_decode($_result); + foreach ($_result as $name => &$rule) + { + $v = explode('.', $name); + if ($try[1] !== $v[0]) + { + // remove since it is not part of this view + unset($_result->$name); + } + else + { + // clear the value since we inherit + $rule = array(); + } + } + // check if there are any view values remaining + if (count( (array) $_result)) + { + $_result = json_encode($_result); + $_result = array($_result); + // Instantiate and return the JAccessRules object for the asset rules. + $rules = new JAccessRules; + $rules->mergeCollection($_result); + + return $rules; + } + } + return $result; + } + } + return JAccess::getAssetRules(0); + } + + /** + * Method to compute the default name of the asset. + * The default name is in the form 'table_name.id' + * where id is the value of the primary key of the table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetName() + { + $k = $this->_tbl_key; + return 'com_extensiondistributor.download.'.(int) $this->$k; + } + + /** + * Method to return the title to use for the asset table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetTitle() + { + if (isset($this->title)) + { + return $this->title; + } + return ''; + } + + /** + * Get the parent asset id for the record + * + * @return int + * @since 2.5 + */ + protected function _getAssetParentId(JTable $table = NULL, $id = NULL) + { + $asset = JTable::getInstance('Asset'); + $asset->loadByName('com_extensiondistributor'); + + return $asset->id; + } + + /** + * This view does not actually have an alias + * + * @return bool + */ + public function generateAlias() + { + return false; + } + +} diff --git a/admin/tables/extension.php b/admin/tables/extension.php new file mode 100644 index 0000000..70bc20b --- /dev/null +++ b/admin/tables/extension.php @@ -0,0 +1,349 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Table\Table; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensions Table class + */ +class ExtensiondistributorTableExtension extends Table +{ + /** + * Ensure the params and metadata in json encoded in the bind method + * + * @var array + * @since 3.3 + */ + protected $_jsonEncode = array('params', 'metadata'); + + /** + * Constructor + * + * @param object Database connector object + */ + function __construct(&$db) + { + parent::__construct('#__extensiondistributor_extension', 'id', $db); + + // Adding Tag Options + JTableObserverTags::createObserver($this, array('typeAlias' => 'com_extensiondistributor.extension')); + + // Adding History Options + JTableObserverContenthistory::createObserver($this, array('typeAlias' => 'com_extensiondistributor.extension')); + } + + public function bind($array, $ignore = '') + { + + if (isset($array['params']) && is_array($array['params'])) + { + $registry = new JRegistry; + $registry->loadArray($array['params']); + $array['params'] = (string) $registry; + } + + if (isset($array['metadata']) && is_array($array['metadata'])) + { + $registry = new JRegistry; + $registry->loadArray($array['metadata']); + $array['metadata'] = (string) $registry; + } + + // Bind the rules. + if (isset($array['rules']) && is_array($array['rules'])) + { + $rules = new JAccessRules($array['rules']); + $this->setRules($rules); + } + return parent::bind($array, $ignore); + } + + /** + * Overload the store method for the Extension table. + * + * @param boolean Toggle whether null values should be updated. + * @return boolean True on success, false on failure. + * @since 1.6 + */ + public function store($updateNulls = false) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if ($this->id) + { + // Existing item + $this->modified = $date->toSql(); + $this->modified_by = $user->get('id'); + } + else + { + // New extension. A extension created and created_by field can be set by the user, + // so we don't touch either of these if they are set. + if (!(int) $this->created) + { + $this->created = $date->toSql(); + } + if (empty($this->created_by)) + { + $this->created_by = $user->get('id'); + } + } + + if (isset($this->alias)) + { + // Verify that the alias is unique + $table = JTable::getInstance('extension', 'ExtensiondistributorTable'); + + if ($table->load(array('alias' => $this->alias, 'catid' => $this->catid)) && ($table->id != $this->id || $this->id == 0)) + { + $this->setError(JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_ERROR_UNIQUE_ALIAS')); + return false; + } + } + + if (isset($this->url)) + { + // Convert IDN urls to punycode + $this->url = JStringPunycode::urlToPunycode($this->url); + } + if (isset($this->website)) + { + // Convert IDN urls to punycode + $this->website = JStringPunycode::urlToPunycode($this->website); + } + + return parent::store($updateNulls); + } + + /** + * Overloaded check method to ensure data integrity. + * + * @return boolean True on success. + */ + public function check() + { + if (isset($this->alias)) + { + // Generate a valid alias + $this->generateAlias(); + + $table = JTable::getInstance('extension', 'extensiondistributorTable'); + + while ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->alias = StringHelper::increment($this->alias, 'dash'); + } + } + + /* + * Clean up keywords -- eliminate extra spaces between phrases + * and cr (\r) and lf (\n) characters from string. + * Only process if not empty. + */ + if (!empty($this->metakey)) + { + // Array of characters to remove. + $bad_characters = array("\n", "\r", "\"", "<", ">"); + + // Remove bad characters. + $after_clean = StringHelper::str_ireplace($bad_characters, "", $this->metakey); + + // Create array using commas as delimiter. + $keys = explode(',', $after_clean); + $clean_keys = array(); + + foreach ($keys as $key) + { + // Ignore blank keywords. + if (trim($key)) + { + $clean_keys[] = trim($key); + } + } + + // Put array back together delimited by ", " + $this->metakey = implode(", ", $clean_keys); + } + + // Clean up description -- eliminate quotes and <> brackets + if (!empty($this->metadesc)) + { + // Only process if not empty + $bad_characters = array("\"", "<", ">"); + $this->metadesc = StringHelper::str_ireplace($bad_characters, "", $this->metadesc); + } + + // If we don't have any access rules set at this point just use an empty JAccessRules class + if (!$this->getRules()) + { + $rules = $this->getDefaultAssetValues('com_extensiondistributor.extension.'.$this->id); + $this->setRules($rules); + } + + // Set ordering + if ($this->published < 0) + { + // Set ordering to 0 if state is archived or trashed + $this->ordering = 0; + } + + return true; + } + + /** + * Gets the default asset values for a component. + * + * @param $string $component The component asset name to search for + * + * @return JAccessRules The JAccessRules object for the asset + */ + protected function getDefaultAssetValues($component, $try = true) + { + // Need to find the asset id by the name of the component. + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote($component)); + $db->setQuery($query); + $db->execute(); + if ($db->loadRowList()) + { + // asset already set so use saved rules + $assetId = (int) $db->loadResult(); + return JAccess::getAssetRules($assetId); // (TODO) instead of keeping inherited Allowed it becomes Allowed. + } + // try again + elseif ($try) + { + $try = explode('.',$component); + $result = $this->getDefaultAssetValues($try[0], false); + if ($result instanceof JAccessRules) + { + if (isset($try[1])) + { + $_result = (string) $result; + $_result = json_decode($_result); + foreach ($_result as $name => &$rule) + { + $v = explode('.', $name); + if ($try[1] !== $v[0]) + { + // remove since it is not part of this view + unset($_result->$name); + } + else + { + // clear the value since we inherit + $rule = array(); + } + } + // check if there are any view values remaining + if (count( (array) $_result)) + { + $_result = json_encode($_result); + $_result = array($_result); + // Instantiate and return the JAccessRules object for the asset rules. + $rules = new JAccessRules; + $rules->mergeCollection($_result); + + return $rules; + } + } + return $result; + } + } + return JAccess::getAssetRules(0); + } + + /** + * Method to compute the default name of the asset. + * The default name is in the form 'table_name.id' + * where id is the value of the primary key of the table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetName() + { + $k = $this->_tbl_key; + return 'com_extensiondistributor.extension.'.(int) $this->$k; + } + + /** + * Method to return the title to use for the asset table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetTitle() + { + if (isset($this->title)) + { + return $this->title; + } + return ''; + } + + /** + * Get the parent asset id for the record + * + * @return int + * @since 2.5 + */ + protected function _getAssetParentId(JTable $table = NULL, $id = NULL) + { + $asset = JTable::getInstance('Asset'); + $asset->loadByName('com_extensiondistributor'); + + return $asset->id; + } + + /** + * Generate a valid alias from title / date. + * Remains public to be able to check for duplicated alias before saving + * + * @return string + */ + public function generateAlias() + { + if (empty($this->alias)) + { + $this->alias = $this->name; + } + + $this->alias = JApplicationHelper::stringURLSafe($this->alias); + + if (trim(str_replace('-', '', $this->alias)) == '') + { + $this->alias = JFactory::getDate()->format('Y-m-d-H-i-s'); + } + + return $this->alias; + } + +} diff --git a/admin/tables/index.html b/admin/tables/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/tables/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/tables/key.php b/admin/tables/key.php new file mode 100644 index 0000000..51bbae7 --- /dev/null +++ b/admin/tables/key.php @@ -0,0 +1,333 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Table\Table; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Keys Table class + */ +class ExtensiondistributorTableKey extends Table +{ + /** + * Ensure the params and metadata in json encoded in the bind method + * + * @var array + * @since 3.3 + */ + protected $_jsonEncode = array('params', 'metadata'); + + /** + * Constructor + * + * @param object Database connector object + */ + function __construct(&$db) + { + parent::__construct('#__extensiondistributor_key', 'id', $db); + + // Adding History Options + JTableObserverContenthistory::createObserver($this, array('typeAlias' => 'com_extensiondistributor.key')); + } + + public function bind($array, $ignore = '') + { + + if (isset($array['params']) && is_array($array['params'])) + { + $registry = new JRegistry; + $registry->loadArray($array['params']); + $array['params'] = (string) $registry; + } + + if (isset($array['metadata']) && is_array($array['metadata'])) + { + $registry = new JRegistry; + $registry->loadArray($array['metadata']); + $array['metadata'] = (string) $registry; + } + + // Bind the rules. + if (isset($array['rules']) && is_array($array['rules'])) + { + $rules = new JAccessRules($array['rules']); + $this->setRules($rules); + } + return parent::bind($array, $ignore); + } + + /** + * Overload the store method for the Key table. + * + * @param boolean Toggle whether null values should be updated. + * @return boolean True on success, false on failure. + * @since 1.6 + */ + public function store($updateNulls = false) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if ($this->id) + { + // Existing item + $this->modified = $date->toSql(); + $this->modified_by = $user->get('id'); + } + else + { + // New key. A key created and created_by field can be set by the user, + // so we don't touch either of these if they are set. + if (!(int) $this->created) + { + $this->created = $date->toSql(); + } + if (empty($this->created_by)) + { + $this->created_by = $user->get('id'); + } + } + + if (isset($this->alias)) + { + // Verify that the alias is unique + $table = JTable::getInstance('key', 'ExtensiondistributorTable'); + + if ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->setError(JText::_('COM_EXTENSIONDISTRIBUTOR_KEY_ERROR_UNIQUE_ALIAS')); + return false; + } + } + + if (isset($this->url)) + { + // Convert IDN urls to punycode + $this->url = JStringPunycode::urlToPunycode($this->url); + } + if (isset($this->website)) + { + // Convert IDN urls to punycode + $this->website = JStringPunycode::urlToPunycode($this->website); + } + + return parent::store($updateNulls); + } + + /** + * Overloaded check method to ensure data integrity. + * + * @return boolean True on success. + */ + public function check() + { + if (isset($this->alias)) + { + // Generate a valid alias + $this->generateAlias(); + + $table = JTable::getInstance('key', 'extensiondistributorTable'); + + while ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->alias = StringHelper::increment($this->alias, 'dash'); + } + } + + /* + * Clean up keywords -- eliminate extra spaces between phrases + * and cr (\r) and lf (\n) characters from string. + * Only process if not empty. + */ + if (!empty($this->metakey)) + { + // Array of characters to remove. + $bad_characters = array("\n", "\r", "\"", "<", ">"); + + // Remove bad characters. + $after_clean = StringHelper::str_ireplace($bad_characters, "", $this->metakey); + + // Create array using commas as delimiter. + $keys = explode(',', $after_clean); + $clean_keys = array(); + + foreach ($keys as $key) + { + // Ignore blank keywords. + if (trim($key)) + { + $clean_keys[] = trim($key); + } + } + + // Put array back together delimited by ", " + $this->metakey = implode(", ", $clean_keys); + } + + // Clean up description -- eliminate quotes and <> brackets + if (!empty($this->metadesc)) + { + // Only process if not empty + $bad_characters = array("\"", "<", ">"); + $this->metadesc = StringHelper::str_ireplace($bad_characters, "", $this->metadesc); + } + + // If we don't have any access rules set at this point just use an empty JAccessRules class + if (!$this->getRules()) + { + $rules = $this->getDefaultAssetValues('com_extensiondistributor.key.'.$this->id); + $this->setRules($rules); + } + + // Set ordering + if ($this->published < 0) + { + // Set ordering to 0 if state is archived or trashed + $this->ordering = 0; + } + + return true; + } + + /** + * Gets the default asset values for a component. + * + * @param $string $component The component asset name to search for + * + * @return JAccessRules The JAccessRules object for the asset + */ + protected function getDefaultAssetValues($component, $try = true) + { + // Need to find the asset id by the name of the component. + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote($component)); + $db->setQuery($query); + $db->execute(); + if ($db->loadRowList()) + { + // asset already set so use saved rules + $assetId = (int) $db->loadResult(); + return JAccess::getAssetRules($assetId); // (TODO) instead of keeping inherited Allowed it becomes Allowed. + } + // try again + elseif ($try) + { + $try = explode('.',$component); + $result = $this->getDefaultAssetValues($try[0], false); + if ($result instanceof JAccessRules) + { + if (isset($try[1])) + { + $_result = (string) $result; + $_result = json_decode($_result); + foreach ($_result as $name => &$rule) + { + $v = explode('.', $name); + if ($try[1] !== $v[0]) + { + // remove since it is not part of this view + unset($_result->$name); + } + else + { + // clear the value since we inherit + $rule = array(); + } + } + // check if there are any view values remaining + if (count( (array) $_result)) + { + $_result = json_encode($_result); + $_result = array($_result); + // Instantiate and return the JAccessRules object for the asset rules. + $rules = new JAccessRules; + $rules->mergeCollection($_result); + + return $rules; + } + } + return $result; + } + } + return JAccess::getAssetRules(0); + } + + /** + * Method to compute the default name of the asset. + * The default name is in the form 'table_name.id' + * where id is the value of the primary key of the table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetName() + { + $k = $this->_tbl_key; + return 'com_extensiondistributor.key.'.(int) $this->$k; + } + + /** + * Method to return the title to use for the asset table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetTitle() + { + if (isset($this->title)) + { + return $this->title; + } + return ''; + } + + /** + * Get the parent asset id for the record + * + * @return int + * @since 2.5 + */ + protected function _getAssetParentId(JTable $table = NULL, $id = NULL) + { + $asset = JTable::getInstance('Asset'); + $asset->loadByName('com_extensiondistributor'); + + return $asset->id; + } + + /** + * This view does not actually have an alias + * + * @return bool + */ + public function generateAlias() + { + return false; + } + +} diff --git a/admin/tables/package.php b/admin/tables/package.php new file mode 100644 index 0000000..c6ad61e --- /dev/null +++ b/admin/tables/package.php @@ -0,0 +1,346 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Table\Table; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Packages Table class + */ +class ExtensiondistributorTablePackage extends Table +{ + /** + * Ensure the params and metadata in json encoded in the bind method + * + * @var array + * @since 3.3 + */ + protected $_jsonEncode = array('params', 'metadata'); + + /** + * Constructor + * + * @param object Database connector object + */ + function __construct(&$db) + { + parent::__construct('#__extensiondistributor_package', 'id', $db); + + // Adding History Options + JTableObserverContenthistory::createObserver($this, array('typeAlias' => 'com_extensiondistributor.package')); + } + + public function bind($array, $ignore = '') + { + + if (isset($array['params']) && is_array($array['params'])) + { + $registry = new JRegistry; + $registry->loadArray($array['params']); + $array['params'] = (string) $registry; + } + + if (isset($array['metadata']) && is_array($array['metadata'])) + { + $registry = new JRegistry; + $registry->loadArray($array['metadata']); + $array['metadata'] = (string) $registry; + } + + // Bind the rules. + if (isset($array['rules']) && is_array($array['rules'])) + { + $rules = new JAccessRules($array['rules']); + $this->setRules($rules); + } + return parent::bind($array, $ignore); + } + + /** + * Overload the store method for the Package table. + * + * @param boolean Toggle whether null values should be updated. + * @return boolean True on success, false on failure. + * @since 1.6 + */ + public function store($updateNulls = false) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if ($this->id) + { + // Existing item + $this->modified = $date->toSql(); + $this->modified_by = $user->get('id'); + } + else + { + // New package. A package created and created_by field can be set by the user, + // so we don't touch either of these if they are set. + if (!(int) $this->created) + { + $this->created = $date->toSql(); + } + if (empty($this->created_by)) + { + $this->created_by = $user->get('id'); + } + } + + if (isset($this->alias)) + { + // Verify that the alias is unique + $table = JTable::getInstance('package', 'ExtensiondistributorTable'); + + if ($table->load(array('alias' => $this->alias, 'catid' => $this->catid)) && ($table->id != $this->id || $this->id == 0)) + { + $this->setError(JText::_('COM_EXTENSIONDISTRIBUTOR_PACKAGE_ERROR_UNIQUE_ALIAS')); + return false; + } + } + + if (isset($this->url)) + { + // Convert IDN urls to punycode + $this->url = JStringPunycode::urlToPunycode($this->url); + } + if (isset($this->website)) + { + // Convert IDN urls to punycode + $this->website = JStringPunycode::urlToPunycode($this->website); + } + + return parent::store($updateNulls); + } + + /** + * Overloaded check method to ensure data integrity. + * + * @return boolean True on success. + */ + public function check() + { + if (isset($this->alias)) + { + // Generate a valid alias + $this->generateAlias(); + + $table = JTable::getInstance('package', 'extensiondistributorTable'); + + while ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->alias = StringHelper::increment($this->alias, 'dash'); + } + } + + /* + * Clean up keywords -- eliminate extra spaces between phrases + * and cr (\r) and lf (\n) characters from string. + * Only process if not empty. + */ + if (!empty($this->metakey)) + { + // Array of characters to remove. + $bad_characters = array("\n", "\r", "\"", "<", ">"); + + // Remove bad characters. + $after_clean = StringHelper::str_ireplace($bad_characters, "", $this->metakey); + + // Create array using commas as delimiter. + $keys = explode(',', $after_clean); + $clean_keys = array(); + + foreach ($keys as $key) + { + // Ignore blank keywords. + if (trim($key)) + { + $clean_keys[] = trim($key); + } + } + + // Put array back together delimited by ", " + $this->metakey = implode(", ", $clean_keys); + } + + // Clean up description -- eliminate quotes and <> brackets + if (!empty($this->metadesc)) + { + // Only process if not empty + $bad_characters = array("\"", "<", ">"); + $this->metadesc = StringHelper::str_ireplace($bad_characters, "", $this->metadesc); + } + + // If we don't have any access rules set at this point just use an empty JAccessRules class + if (!$this->getRules()) + { + $rules = $this->getDefaultAssetValues('com_extensiondistributor.package.'.$this->id); + $this->setRules($rules); + } + + // Set ordering + if ($this->published < 0) + { + // Set ordering to 0 if state is archived or trashed + $this->ordering = 0; + } + + return true; + } + + /** + * Gets the default asset values for a component. + * + * @param $string $component The component asset name to search for + * + * @return JAccessRules The JAccessRules object for the asset + */ + protected function getDefaultAssetValues($component, $try = true) + { + // Need to find the asset id by the name of the component. + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote($component)); + $db->setQuery($query); + $db->execute(); + if ($db->loadRowList()) + { + // asset already set so use saved rules + $assetId = (int) $db->loadResult(); + return JAccess::getAssetRules($assetId); // (TODO) instead of keeping inherited Allowed it becomes Allowed. + } + // try again + elseif ($try) + { + $try = explode('.',$component); + $result = $this->getDefaultAssetValues($try[0], false); + if ($result instanceof JAccessRules) + { + if (isset($try[1])) + { + $_result = (string) $result; + $_result = json_decode($_result); + foreach ($_result as $name => &$rule) + { + $v = explode('.', $name); + if ($try[1] !== $v[0]) + { + // remove since it is not part of this view + unset($_result->$name); + } + else + { + // clear the value since we inherit + $rule = array(); + } + } + // check if there are any view values remaining + if (count( (array) $_result)) + { + $_result = json_encode($_result); + $_result = array($_result); + // Instantiate and return the JAccessRules object for the asset rules. + $rules = new JAccessRules; + $rules->mergeCollection($_result); + + return $rules; + } + } + return $result; + } + } + return JAccess::getAssetRules(0); + } + + /** + * Method to compute the default name of the asset. + * The default name is in the form 'table_name.id' + * where id is the value of the primary key of the table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetName() + { + $k = $this->_tbl_key; + return 'com_extensiondistributor.package.'.(int) $this->$k; + } + + /** + * Method to return the title to use for the asset table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetTitle() + { + if (isset($this->title)) + { + return $this->title; + } + return ''; + } + + /** + * Get the parent asset id for the record + * + * @return int + * @since 2.5 + */ + protected function _getAssetParentId(JTable $table = NULL, $id = NULL) + { + $asset = JTable::getInstance('Asset'); + $asset->loadByName('com_extensiondistributor'); + + return $asset->id; + } + + /** + * Generate a valid alias from title / date. + * Remains public to be able to check for duplicated alias before saving + * + * @return string + */ + public function generateAlias() + { + if (empty($this->alias)) + { + $this->alias = $this->name; + } + + $this->alias = JApplicationHelper::stringURLSafe($this->alias); + + if (trim(str_replace('-', '', $this->alias)) == '') + { + $this->alias = JFactory::getDate()->format('Y-m-d-H-i-s'); + } + + return $this->alias; + } + +} diff --git a/admin/tables/release.php b/admin/tables/release.php new file mode 100644 index 0000000..1639ac2 --- /dev/null +++ b/admin/tables/release.php @@ -0,0 +1,333 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Table\Table; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Releases Table class + */ +class ExtensiondistributorTableRelease extends Table +{ + /** + * Ensure the params and metadata in json encoded in the bind method + * + * @var array + * @since 3.3 + */ + protected $_jsonEncode = array('params', 'metadata'); + + /** + * Constructor + * + * @param object Database connector object + */ + function __construct(&$db) + { + parent::__construct('#__extensiondistributor_release', 'id', $db); + + // Adding History Options + JTableObserverContenthistory::createObserver($this, array('typeAlias' => 'com_extensiondistributor.release')); + } + + public function bind($array, $ignore = '') + { + + if (isset($array['params']) && is_array($array['params'])) + { + $registry = new JRegistry; + $registry->loadArray($array['params']); + $array['params'] = (string) $registry; + } + + if (isset($array['metadata']) && is_array($array['metadata'])) + { + $registry = new JRegistry; + $registry->loadArray($array['metadata']); + $array['metadata'] = (string) $registry; + } + + // Bind the rules. + if (isset($array['rules']) && is_array($array['rules'])) + { + $rules = new JAccessRules($array['rules']); + $this->setRules($rules); + } + return parent::bind($array, $ignore); + } + + /** + * Overload the store method for the Release table. + * + * @param boolean Toggle whether null values should be updated. + * @return boolean True on success, false on failure. + * @since 1.6 + */ + public function store($updateNulls = false) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if ($this->id) + { + // Existing item + $this->modified = $date->toSql(); + $this->modified_by = $user->get('id'); + } + else + { + // New release. A release created and created_by field can be set by the user, + // so we don't touch either of these if they are set. + if (!(int) $this->created) + { + $this->created = $date->toSql(); + } + if (empty($this->created_by)) + { + $this->created_by = $user->get('id'); + } + } + + if (isset($this->alias)) + { + // Verify that the alias is unique + $table = JTable::getInstance('release', 'ExtensiondistributorTable'); + + if ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->setError(JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_ERROR_UNIQUE_ALIAS')); + return false; + } + } + + if (isset($this->url)) + { + // Convert IDN urls to punycode + $this->url = JStringPunycode::urlToPunycode($this->url); + } + if (isset($this->website)) + { + // Convert IDN urls to punycode + $this->website = JStringPunycode::urlToPunycode($this->website); + } + + return parent::store($updateNulls); + } + + /** + * Overloaded check method to ensure data integrity. + * + * @return boolean True on success. + */ + public function check() + { + if (isset($this->alias)) + { + // Generate a valid alias + $this->generateAlias(); + + $table = JTable::getInstance('release', 'extensiondistributorTable'); + + while ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->alias = StringHelper::increment($this->alias, 'dash'); + } + } + + /* + * Clean up keywords -- eliminate extra spaces between phrases + * and cr (\r) and lf (\n) characters from string. + * Only process if not empty. + */ + if (!empty($this->metakey)) + { + // Array of characters to remove. + $bad_characters = array("\n", "\r", "\"", "<", ">"); + + // Remove bad characters. + $after_clean = StringHelper::str_ireplace($bad_characters, "", $this->metakey); + + // Create array using commas as delimiter. + $keys = explode(',', $after_clean); + $clean_keys = array(); + + foreach ($keys as $key) + { + // Ignore blank keywords. + if (trim($key)) + { + $clean_keys[] = trim($key); + } + } + + // Put array back together delimited by ", " + $this->metakey = implode(", ", $clean_keys); + } + + // Clean up description -- eliminate quotes and <> brackets + if (!empty($this->metadesc)) + { + // Only process if not empty + $bad_characters = array("\"", "<", ">"); + $this->metadesc = StringHelper::str_ireplace($bad_characters, "", $this->metadesc); + } + + // If we don't have any access rules set at this point just use an empty JAccessRules class + if (!$this->getRules()) + { + $rules = $this->getDefaultAssetValues('com_extensiondistributor.release.'.$this->id); + $this->setRules($rules); + } + + // Set ordering + if ($this->published < 0) + { + // Set ordering to 0 if state is archived or trashed + $this->ordering = 0; + } + + return true; + } + + /** + * Gets the default asset values for a component. + * + * @param $string $component The component asset name to search for + * + * @return JAccessRules The JAccessRules object for the asset + */ + protected function getDefaultAssetValues($component, $try = true) + { + // Need to find the asset id by the name of the component. + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote($component)); + $db->setQuery($query); + $db->execute(); + if ($db->loadRowList()) + { + // asset already set so use saved rules + $assetId = (int) $db->loadResult(); + return JAccess::getAssetRules($assetId); // (TODO) instead of keeping inherited Allowed it becomes Allowed. + } + // try again + elseif ($try) + { + $try = explode('.',$component); + $result = $this->getDefaultAssetValues($try[0], false); + if ($result instanceof JAccessRules) + { + if (isset($try[1])) + { + $_result = (string) $result; + $_result = json_decode($_result); + foreach ($_result as $name => &$rule) + { + $v = explode('.', $name); + if ($try[1] !== $v[0]) + { + // remove since it is not part of this view + unset($_result->$name); + } + else + { + // clear the value since we inherit + $rule = array(); + } + } + // check if there are any view values remaining + if (count( (array) $_result)) + { + $_result = json_encode($_result); + $_result = array($_result); + // Instantiate and return the JAccessRules object for the asset rules. + $rules = new JAccessRules; + $rules->mergeCollection($_result); + + return $rules; + } + } + return $result; + } + } + return JAccess::getAssetRules(0); + } + + /** + * Method to compute the default name of the asset. + * The default name is in the form 'table_name.id' + * where id is the value of the primary key of the table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetName() + { + $k = $this->_tbl_key; + return 'com_extensiondistributor.release.'.(int) $this->$k; + } + + /** + * Method to return the title to use for the asset table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetTitle() + { + if (isset($this->title)) + { + return $this->title; + } + return ''; + } + + /** + * Get the parent asset id for the record + * + * @return int + * @since 2.5 + */ + protected function _getAssetParentId(JTable $table = NULL, $id = NULL) + { + $asset = JTable::getInstance('Asset'); + $asset->loadByName('com_extensiondistributor'); + + return $asset->id; + } + + /** + * This view does not actually have an alias + * + * @return bool + */ + public function generateAlias() + { + return false; + } + +} diff --git a/admin/tables/subscription.php b/admin/tables/subscription.php new file mode 100644 index 0000000..ae87dad --- /dev/null +++ b/admin/tables/subscription.php @@ -0,0 +1,330 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Table\Table; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Subscriptions Table class + */ +class ExtensiondistributorTableSubscription extends Table +{ + /** + * Ensure the params and metadata in json encoded in the bind method + * + * @var array + * @since 3.3 + */ + protected $_jsonEncode = array('params', 'metadata'); + + /** + * Constructor + * + * @param object Database connector object + */ + function __construct(&$db) + { + parent::__construct('#__extensiondistributor_subscription', 'id', $db); + } + + public function bind($array, $ignore = '') + { + + if (isset($array['params']) && is_array($array['params'])) + { + $registry = new JRegistry; + $registry->loadArray($array['params']); + $array['params'] = (string) $registry; + } + + if (isset($array['metadata']) && is_array($array['metadata'])) + { + $registry = new JRegistry; + $registry->loadArray($array['metadata']); + $array['metadata'] = (string) $registry; + } + + // Bind the rules. + if (isset($array['rules']) && is_array($array['rules'])) + { + $rules = new JAccessRules($array['rules']); + $this->setRules($rules); + } + return parent::bind($array, $ignore); + } + + /** + * Overload the store method for the Subscription table. + * + * @param boolean Toggle whether null values should be updated. + * @return boolean True on success, false on failure. + * @since 1.6 + */ + public function store($updateNulls = false) + { + $date = JFactory::getDate(); + $user = JFactory::getUser(); + + if ($this->id) + { + // Existing item + $this->modified = $date->toSql(); + $this->modified_by = $user->get('id'); + } + else + { + // New subscription. A subscription created and created_by field can be set by the user, + // so we don't touch either of these if they are set. + if (!(int) $this->created) + { + $this->created = $date->toSql(); + } + if (empty($this->created_by)) + { + $this->created_by = $user->get('id'); + } + } + + if (isset($this->alias)) + { + // Verify that the alias is unique + $table = JTable::getInstance('subscription', 'ExtensiondistributorTable'); + + if ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->setError(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_ERROR_UNIQUE_ALIAS')); + return false; + } + } + + if (isset($this->url)) + { + // Convert IDN urls to punycode + $this->url = JStringPunycode::urlToPunycode($this->url); + } + if (isset($this->website)) + { + // Convert IDN urls to punycode + $this->website = JStringPunycode::urlToPunycode($this->website); + } + + return parent::store($updateNulls); + } + + /** + * Overloaded check method to ensure data integrity. + * + * @return boolean True on success. + */ + public function check() + { + if (isset($this->alias)) + { + // Generate a valid alias + $this->generateAlias(); + + $table = JTable::getInstance('subscription', 'extensiondistributorTable'); + + while ($table->load(array('alias' => $this->alias)) && ($table->id != $this->id || $this->id == 0)) + { + $this->alias = StringHelper::increment($this->alias, 'dash'); + } + } + + /* + * Clean up keywords -- eliminate extra spaces between phrases + * and cr (\r) and lf (\n) characters from string. + * Only process if not empty. + */ + if (!empty($this->metakey)) + { + // Array of characters to remove. + $bad_characters = array("\n", "\r", "\"", "<", ">"); + + // Remove bad characters. + $after_clean = StringHelper::str_ireplace($bad_characters, "", $this->metakey); + + // Create array using commas as delimiter. + $keys = explode(',', $after_clean); + $clean_keys = array(); + + foreach ($keys as $key) + { + // Ignore blank keywords. + if (trim($key)) + { + $clean_keys[] = trim($key); + } + } + + // Put array back together delimited by ", " + $this->metakey = implode(", ", $clean_keys); + } + + // Clean up description -- eliminate quotes and <> brackets + if (!empty($this->metadesc)) + { + // Only process if not empty + $bad_characters = array("\"", "<", ">"); + $this->metadesc = StringHelper::str_ireplace($bad_characters, "", $this->metadesc); + } + + // If we don't have any access rules set at this point just use an empty JAccessRules class + if (!$this->getRules()) + { + $rules = $this->getDefaultAssetValues('com_extensiondistributor.subscription.'.$this->id); + $this->setRules($rules); + } + + // Set ordering + if ($this->published < 0) + { + // Set ordering to 0 if state is archived or trashed + $this->ordering = 0; + } + + return true; + } + + /** + * Gets the default asset values for a component. + * + * @param $string $component The component asset name to search for + * + * @return JAccessRules The JAccessRules object for the asset + */ + protected function getDefaultAssetValues($component, $try = true) + { + // Need to find the asset id by the name of the component. + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote($component)); + $db->setQuery($query); + $db->execute(); + if ($db->loadRowList()) + { + // asset already set so use saved rules + $assetId = (int) $db->loadResult(); + return JAccess::getAssetRules($assetId); // (TODO) instead of keeping inherited Allowed it becomes Allowed. + } + // try again + elseif ($try) + { + $try = explode('.',$component); + $result = $this->getDefaultAssetValues($try[0], false); + if ($result instanceof JAccessRules) + { + if (isset($try[1])) + { + $_result = (string) $result; + $_result = json_decode($_result); + foreach ($_result as $name => &$rule) + { + $v = explode('.', $name); + if ($try[1] !== $v[0]) + { + // remove since it is not part of this view + unset($_result->$name); + } + else + { + // clear the value since we inherit + $rule = array(); + } + } + // check if there are any view values remaining + if (count( (array) $_result)) + { + $_result = json_encode($_result); + $_result = array($_result); + // Instantiate and return the JAccessRules object for the asset rules. + $rules = new JAccessRules; + $rules->mergeCollection($_result); + + return $rules; + } + } + return $result; + } + } + return JAccess::getAssetRules(0); + } + + /** + * Method to compute the default name of the asset. + * The default name is in the form 'table_name.id' + * where id is the value of the primary key of the table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetName() + { + $k = $this->_tbl_key; + return 'com_extensiondistributor.subscription.'.(int) $this->$k; + } + + /** + * Method to return the title to use for the asset table. + * + * @return string + * @since 2.5 + */ + protected function _getAssetTitle() + { + if (isset($this->title)) + { + return $this->title; + } + return ''; + } + + /** + * Get the parent asset id for the record + * + * @return int + * @since 2.5 + */ + protected function _getAssetParentId(JTable $table = NULL, $id = NULL) + { + $asset = JTable::getInstance('Asset'); + $asset->loadByName('com_extensiondistributor'); + + return $asset->id; + } + + /** + * This view does not actually have an alias + * + * @return bool + */ + public function generateAlias() + { + return false; + } + +} diff --git a/admin/views/download/submitbutton.js b/admin/views/download/submitbutton.js new file mode 100644 index 0000000..19bc59f --- /dev/null +++ b/admin/views/download/submitbutton.js @@ -0,0 +1,34 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage submitbutton.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +Joomla.submitbutton = function(task) +{ + if (task == ''){ + return false; + } else { + var action = task.split('.'); + if (action[1] == 'cancel' || action[1] == 'close' || document.formvalidator.isValid(document.getElementById("adminForm"))){ + Joomla.submitform(task, document.getElementById("adminForm")); + return true; + } else { + alert(Joomla.JText._('download, some values are not acceptable.','Some values are unacceptable')); + return false; + } + } +} \ No newline at end of file diff --git a/admin/views/download/tmpl/edit.php b/admin/views/download/tmpl/edit.php new file mode 100644 index 0000000..a9342a7 --- /dev/null +++ b/admin/views/download/tmpl/edit.php @@ -0,0 +1,95 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html'); +JHtml::_('behavior.formvalidator'); +JHtml::_('formbehavior.chosen', 'select'); +JHtml::_('behavior.keepalive'); +$componentParams = $this->params; // will be removed just use $this->params instead +?> + + diff --git a/admin/views/download/tmpl/index.html b/admin/views/download/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/download/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/download/view.html.php b/admin/views/download/view.html.php new file mode 100644 index 0000000..e5afc40 --- /dev/null +++ b/admin/views/download/view.html.php @@ -0,0 +1,201 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Download Html View class + */ +class ExtensiondistributorViewDownload extends HtmlView +{ + /** + * display method of View + * @return void + */ + public function display($tpl = null) + { + // set params + $this->params = JComponentHelper::getParams('com_extensiondistributor'); + // Assign the variables + $this->form = $this->get('Form'); + $this->item = $this->get('Item'); + $this->script = $this->get('Script'); + $this->state = $this->get('State'); + // get action permissions + $this->canDo = ExtensiondistributorHelper::getActions('download', $this->item); + // get input + $jinput = JFactory::getApplication()->input; + $this->ref = $jinput->get('ref', 0, 'word'); + $this->refid = $jinput->get('refid', 0, 'int'); + $return = $jinput->get('return', null, 'base64'); + // set the referral string + $this->referral = ''; + if ($this->refid && $this->ref) + { + // return to the item that referred to this item + $this->referral = '&ref=' . (string)$this->ref . '&refid=' . (int)$this->refid; + } + elseif($this->ref) + { + // return to the list view that referred to this item + $this->referral = '&ref=' . (string)$this->ref; + } + // check return value + if (!is_null($return)) + { + // add the return value + $this->referral .= '&return=' . (string)$return; + } + + // Set the toolbar + $this->addToolBar(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JFactory::getApplication()->input->set('hidemainmenu', true); + $user = JFactory::getUser(); + $userId = $user->id; + $isNew = $this->item->id == 0; + + JToolbarHelper::title( JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_NEW' : 'COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_EDIT'), 'pencil-2 article-add'); + // Built the actions for new and existing records. + if (ExtensiondistributorHelper::checkString($this->referral)) + { + if ($this->canDo->get('core.create') && $isNew) + { + // We can create the record. + JToolBarHelper::save('download.save', 'JTOOLBAR_SAVE'); + } + elseif ($this->canDo->get('core.edit')) + { + // We can save the record. + JToolBarHelper::save('download.save', 'JTOOLBAR_SAVE'); + } + if ($isNew) + { + // Do not creat but cancel. + JToolBarHelper::cancel('download.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + // We can close it. + JToolBarHelper::cancel('download.cancel', 'JTOOLBAR_CLOSE'); + } + } + else + { + if ($isNew) + { + // For new records, check the create permission. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::apply('download.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('download.save', 'JTOOLBAR_SAVE'); + JToolBarHelper::custom('download.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + }; + JToolBarHelper::cancel('download.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + if ($this->canDo->get('core.edit')) + { + // We can save the new record + JToolBarHelper::apply('download.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('download.save', 'JTOOLBAR_SAVE'); + // We can save this record, but check the create permission to see + // if we can return to make a new one. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('download.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + } + } + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('download.save2copy', 'save-copy.png', 'save-copy_f2.png', 'JTOOLBAR_SAVE_AS_COPY', false); + } + JToolBarHelper::cancel('download.cancel', 'JTOOLBAR_CLOSE'); + } + } + JToolbarHelper::divider(); + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('download'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 30) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true, 30); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + $isNew = ($this->item->id < 1); + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_NEW' : 'COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_EDIT')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/download.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + $this->document->addScript(JURI::root() . $this->script, (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + $this->document->addScript(JURI::root() . "administrator/components/com_extensiondistributor/views/download/submitbutton.js", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + JText::script('view not acceptable. Error'); + } +} diff --git a/admin/views/downloads/index.html b/admin/views/downloads/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/downloads/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/downloads/tmpl/default.php b/admin/views/downloads/tmpl/default.php new file mode 100644 index 0000000..ab5d637 --- /dev/null +++ b/admin/views/downloads/tmpl/default.php @@ -0,0 +1,98 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::_('behavior.multiselect'); +JHtml::_('dropdown.init'); +JHtml::_('formbehavior.chosen', '.multipleDownloadsfiltertransaction', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_TRANSACTION_TYPE') . ' -')); +JHtml::_('formbehavior.chosen', '.multiplePackagelog', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_PACKAGE') . ' -')); +JHtml::_('formbehavior.chosen', '.multipleDownloadsfilteruser', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_JOOMLA_USER') . ' -')); +JHtml::_('formbehavior.chosen', 'select'); +if ($this->saveOrder) +{ + $saveOrderingUrl = 'index.php?option=com_extensiondistributor&task=downloads.saveOrderAjax&tmpl=component'; + JHtml::_('sortablelist.sortable', 'downloadList', 'adminForm', strtolower($this->listDirn), $saveOrderingUrl); +} +?> +
    +sidebar)): ?> +
    + sidebar; ?> +
    +
    + +
    + + $this)); +?> +items)): ?> +
    + +
    + + + loadTemplate('head');?> + loadTemplate('foot');?> + loadTemplate('body');?> +
    + + canCreate && $this->canEdit) : ?> + JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOADS_BATCH_OPTIONS'), + 'footer' => $this->loadTemplate('batch_footer') + ), + $this->loadTemplate('batch_body') + ); ?> + + +
    + + + + + diff --git a/admin/views/downloads/tmpl/default_batch_body.php b/admin/views/downloads/tmpl/default_batch_body.php new file mode 100644 index 0000000..5a3d2d3 --- /dev/null +++ b/admin/views/downloads/tmpl/default_batch_body.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + +

    +batchDisplay; ?> \ No newline at end of file diff --git a/admin/views/downloads/tmpl/default_batch_footer.php b/admin/views/downloads/tmpl/default_batch_footer.php new file mode 100644 index 0000000..f37a908 --- /dev/null +++ b/admin/views/downloads/tmpl/default_batch_footer.php @@ -0,0 +1,32 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + + + \ No newline at end of file diff --git a/admin/views/downloads/tmpl/default_body.php b/admin/views/downloads/tmpl/default_body.php new file mode 100644 index 0000000..2eadd04 --- /dev/null +++ b/admin/views/downloads/tmpl/default_body.php @@ -0,0 +1,92 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +$edit = "index.php?option=com_extensiondistributor&view=downloads&task=download.edit"; + +?> +items as $i => $item): ?> + user->authorise('core.manage', 'com_checkin') || $item->checked_out == $this->user->id || $item->checked_out == 0; + $userChkOut = JFactory::getUser($item->checked_out); + $canDo = ExtensiondistributorHelper::getActions('download',$item,'downloads'); + ?> + + + get('core.edit.state')): ?> + saveOrder) + { + $iconClass = ' inactive tip-top" hasTooltip" title="' . JHtml::tooltipText('JORDERINGDISABLED'); + } + ?> + + + + saveOrder) : ?> + + + + ⋮ + + + + get('core.edit')): ?> + checked_out) : ?> + + id); ?> + + □ + + + id); ?> + + + □ + + + + transaction); ?> + + + escape($item->package_name); ?> + + + user)->name; ?> + + + escape($item->ip); ?> + + + escape($item->domain); ?> + + +
    created, 'DATE_FORMAT_LC6') ?> +
    + + + id; ?> + + + \ No newline at end of file diff --git a/admin/views/downloads/tmpl/default_foot.php b/admin/views/downloads/tmpl/default_foot.php new file mode 100644 index 0000000..9dcb0f8 --- /dev/null +++ b/admin/views/downloads/tmpl/default_foot.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + pagination->getListFooter(); ?> + \ No newline at end of file diff --git a/admin/views/downloads/tmpl/default_head.php b/admin/views/downloads/tmpl/default_head.php new file mode 100644 index 0000000..39df92e --- /dev/null +++ b/admin/views/downloads/tmpl/default_head.php @@ -0,0 +1,62 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + canEdit&& $this->canState): ?> + + listDirn, $this->listOrder, null, 'asc', 'JGRID_HEADING_ORDERING', 'icon-menu-2'); ?> + + + + + + + ▾ + + + ■ + + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + \ No newline at end of file diff --git a/admin/views/downloads/tmpl/default_toolbar.php b/admin/views/downloads/tmpl/default_toolbar.php new file mode 100644 index 0000000..85975f3 --- /dev/null +++ b/admin/views/downloads/tmpl/default_toolbar.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> +
    + +
    + + +
    +
    + + pagination->getLimitBox(); ?> +
    +
    + + +
    +
    + + +
    +
    +
    \ No newline at end of file diff --git a/admin/views/downloads/tmpl/index.html b/admin/views/downloads/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/downloads/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/downloads/view.html.php b/admin/views/downloads/view.html.php new file mode 100644 index 0000000..03a49f0 --- /dev/null +++ b/admin/views/downloads/view.html.php @@ -0,0 +1,290 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Downloads + */ +class ExtensiondistributorViewDownloads extends HtmlView +{ + /** + * Downloads view display method + * @return void + */ + function display($tpl = null) + { + if ($this->getLayout() !== 'modal') + { + // Include helper submenu + ExtensiondistributorHelper::addSubmenu('downloads'); + } + + // Assign data to the view + $this->items = $this->get('Items'); + $this->pagination = $this->get('Pagination'); + $this->state = $this->get('State'); + $this->user = JFactory::getUser(); + // Load the filter form from xml. + $this->filterForm = $this->get('FilterForm'); + // Load the active filters. + $this->activeFilters = $this->get('ActiveFilters'); + // Add the list ordering clause. + $this->listOrder = $this->escape($this->state->get('list.ordering', 'a.id')); + $this->listDirn = $this->escape($this->state->get('list.direction', 'DESC')); + $this->saveOrder = $this->listOrder == 'a.ordering'; + // set the return here value + $this->return_here = urlencode(base64_encode((string) JUri::getInstance())); + // get global action permissions + $this->canDo = ExtensiondistributorHelper::getActions('download'); + $this->canEdit = $this->canDo->get('core.edit'); + $this->canState = $this->canDo->get('core.edit.state'); + $this->canCreate = $this->canDo->get('core.create'); + $this->canDelete = $this->canDo->get('core.delete'); + $this->canBatch = $this->canDo->get('core.batch'); + + // We don't need toolbar in the modal window. + if ($this->getLayout() !== 'modal') + { + $this->addToolbar(); + $this->sidebar = JHtmlSidebar::render(); + // load the batch html + if ($this->canCreate && $this->canEdit && $this->canState) + { + $this->batchDisplay = JHtmlBatch_::render(); + } + } + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JToolBarHelper::title(JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOADS'), 'download'); + JHtmlSidebar::setAction('index.php?option=com_extensiondistributor&view=downloads'); + JFormHelper::addFieldPath(JPATH_COMPONENT . '/models/fields'); + + if ($this->canCreate) + { + JToolBarHelper::addNew('download.add'); + } + + // Only load if there are items + if (ExtensiondistributorHelper::checkArray($this->items)) + { + if ($this->canEdit) + { + JToolBarHelper::editList('download.edit'); + } + + if ($this->canState) + { + JToolBarHelper::publishList('downloads.publish'); + JToolBarHelper::unpublishList('downloads.unpublish'); + JToolBarHelper::archiveList('downloads.archive'); + + if ($this->canDo->get('core.admin')) + { + JToolBarHelper::checkin('downloads.checkin'); + } + } + + // Add a batch button + if ($this->canBatch && $this->canCreate && $this->canEdit && $this->canState) + { + // Get the toolbar object instance + $bar = JToolBar::getInstance('toolbar'); + // set the batch button name + $title = JText::_('JTOOLBAR_BATCH'); + // Instantiate a new JLayoutFile instance and render the batch button + $layout = new JLayoutFile('joomla.toolbar.batch'); + // add the button to the page + $dhtml = $layout->render(array('title' => $title)); + $bar->appendButton('Custom', $dhtml, 'batch'); + } + if ($this->user->authorise('download.delete', 'com_extensiondistributor')) + { + // add Delete button. + JToolBarHelper::custom('downloads.deleteLogs', 'delete custom-button-deletelogs', '', 'COM_EXTENSIONDISTRIBUTOR_DELETE', 'true'); + } + + if ($this->state->get('filter.published') == -2 && ($this->canState && $this->canDelete)) + { + JToolbarHelper::deleteList('', 'downloads.delete', 'JTOOLBAR_EMPTY_TRASH'); + } + elseif ($this->canState && $this->canDelete) + { + JToolbarHelper::trash('downloads.trash'); + } + } + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('downloads'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + // add the options comp button + if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + + // Only load published batch if state and batch is allowed + if ($this->canState && $this->canBatch) + { + JHtmlBatch_::addListSelection( + JText::_('COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_STATE'), + 'batch[published]', + JHtml::_('select.options', JHtml::_('jgrid.publishedOptions', array('all' => false)), 'value', 'text', '', true) + ); + } + + // Only load Transaction batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Transaction Selection + $this->transactionOptions = JFormHelper::loadFieldType('downloadsfiltertransaction')->options; + // We do some sanitation for Transaction filter + if (ExtensiondistributorHelper::checkArray($this->transactionOptions) && + isset($this->transactionOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->transactionOptions[0]->value)) + { + unset($this->transactionOptions[0]); + } + // Transaction Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_TRANSACTION_LABEL').' -', + 'batch[transaction]', + JHtml::_('select.options', $this->transactionOptions, 'value', 'text') + ); + } + + // Only load Package Name batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Package Name Selection + $this->packageNameOptions = JFormHelper::loadFieldType('Packagelog')->options; + // We do some sanitation for Package Name filter + if (ExtensiondistributorHelper::checkArray($this->packageNameOptions) && + isset($this->packageNameOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->packageNameOptions[0]->value)) + { + unset($this->packageNameOptions[0]); + } + // Package Name Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_PACKAGE_LABEL').' -', + 'batch[package]', + JHtml::_('select.options', $this->packageNameOptions, 'value', 'text') + ); + } + + // Only load User batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set User Selection + $this->userOptions = JFormHelper::loadFieldType('downloadsfilteruser')->options; + // We do some sanitation for User filter + if (ExtensiondistributorHelper::checkArray($this->userOptions) && + isset($this->userOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->userOptions[0]->value)) + { + unset($this->userOptions[0]); + } + // User Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_USER_LABEL').' -', + 'batch[user]', + JHtml::_('select.options', $this->userOptions, 'value', 'text') + ); + } + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOADS')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/downloads.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 50) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Returns an array of fields the table can be sorted by + * + * @return array Array containing the field name to sort by as the key and display text as value + */ + protected function getSortFields() + { + return array( + 'a.ordering' => JText::_('JGRID_HEADING_ORDERING'), + 'a.published' => JText::_('JSTATUS'), + 'a.transaction' => JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_TRANSACTION_LABEL'), + 'g.name' => JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_PACKAGE_LABEL'), + 'a.user' => JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_USER_LABEL'), + 'a.ip' => JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_IP_LABEL'), + 'a.domain' => JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_DOMAIN_LABEL'), + 'a.created' => JText::_('COM_EXTENSIONDISTRIBUTOR_DOWNLOAD_CREATED_LABEL'), + 'a.id' => JText::_('JGRID_HEADING_ID') + ); + } +} diff --git a/admin/views/extension/submitbutton.js b/admin/views/extension/submitbutton.js new file mode 100644 index 0000000..aea46fe --- /dev/null +++ b/admin/views/extension/submitbutton.js @@ -0,0 +1,34 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage submitbutton.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +Joomla.submitbutton = function(task) +{ + if (task == ''){ + return false; + } else { + var action = task.split('.'); + if (action[1] == 'cancel' || action[1] == 'close' || document.formvalidator.isValid(document.getElementById("adminForm"))){ + Joomla.submitform(task, document.getElementById("adminForm")); + return true; + } else { + alert(Joomla.JText._('extension, some values are not acceptable.','Some values are unacceptable')); + return false; + } + } +} \ No newline at end of file diff --git a/admin/views/extension/tmpl/edit.php b/admin/views/extension/tmpl/edit.php new file mode 100644 index 0000000..03b637f --- /dev/null +++ b/admin/views/extension/tmpl/edit.php @@ -0,0 +1,159 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html'); +JHtml::_('behavior.formvalidator'); +JHtml::_('formbehavior.chosen', 'select'); +JHtml::_('behavior.keepalive'); +$componentParams = $this->params; // will be removed just use $this->params instead +?> + + + + diff --git a/admin/views/extension/tmpl/index.html b/admin/views/extension/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/extension/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/extension/view.html.php b/admin/views/extension/view.html.php new file mode 100644 index 0000000..b7083df --- /dev/null +++ b/admin/views/extension/view.html.php @@ -0,0 +1,224 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extension Html View class + */ +class ExtensiondistributorViewExtension extends HtmlView +{ + /** + * display method of View + * @return void + */ + public function display($tpl = null) + { + // set params + $this->params = JComponentHelper::getParams('com_extensiondistributor'); + // Assign the variables + $this->form = $this->get('Form'); + $this->item = $this->get('Item'); + $this->script = $this->get('Script'); + $this->state = $this->get('State'); + // get action permissions + $this->canDo = ExtensiondistributorHelper::getActions('extension', $this->item); + // get input + $jinput = JFactory::getApplication()->input; + $this->ref = $jinput->get('ref', 0, 'word'); + $this->refid = $jinput->get('refid', 0, 'int'); + $return = $jinput->get('return', null, 'base64'); + // set the referral string + $this->referral = ''; + if ($this->refid && $this->ref) + { + // return to the item that referred to this item + $this->referral = '&ref=' . (string)$this->ref . '&refid=' . (int)$this->refid; + } + elseif($this->ref) + { + // return to the list view that referred to this item + $this->referral = '&ref=' . (string)$this->ref; + } + // check return value + if (!is_null($return)) + { + // add the return value + $this->referral .= '&return=' . (string)$return; + } + + // Get Linked view data + $this->vvwreleases = $this->get('Vvwreleases'); + + // Set the toolbar + $this->addToolBar(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JFactory::getApplication()->input->set('hidemainmenu', true); + $user = JFactory::getUser(); + $userId = $user->id; + $isNew = $this->item->id == 0; + + JToolbarHelper::title( JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_NEW' : 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_EDIT'), 'pencil-2 article-add'); + // Built the actions for new and existing records. + if (ExtensiondistributorHelper::checkString($this->referral)) + { + if ($this->canDo->get('core.create') && $isNew) + { + // We can create the record. + JToolBarHelper::save('extension.save', 'JTOOLBAR_SAVE'); + } + elseif ($this->canDo->get('core.edit')) + { + // We can save the record. + JToolBarHelper::save('extension.save', 'JTOOLBAR_SAVE'); + } + if ($isNew) + { + // Do not creat but cancel. + JToolBarHelper::cancel('extension.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + // We can close it. + JToolBarHelper::cancel('extension.cancel', 'JTOOLBAR_CLOSE'); + } + } + else + { + if ($isNew) + { + // For new records, check the create permission. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::apply('extension.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('extension.save', 'JTOOLBAR_SAVE'); + JToolBarHelper::custom('extension.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + }; + JToolBarHelper::cancel('extension.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + if ($this->canDo->get('core.edit')) + { + // We can save the new record + JToolBarHelper::apply('extension.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('extension.save', 'JTOOLBAR_SAVE'); + // We can save this record, but check the create permission to see + // if we can return to make a new one. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('extension.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + } + } + $canVersion = ($this->canDo->get('core.version') && $this->canDo->get('extension.version')); + if ($this->state->params->get('save_history', 1) && $this->canDo->get('core.edit') && $canVersion) + { + JToolbarHelper::versions('com_extensiondistributor.extension', $this->item->id); + } + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('extension.save2copy', 'save-copy.png', 'save-copy_f2.png', 'JTOOLBAR_SAVE_AS_COPY', false); + } + JToolBarHelper::cancel('extension.cancel', 'JTOOLBAR_CLOSE'); + } + } + JToolbarHelper::divider(); + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('extension'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 30) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true, 30); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + $isNew = ($this->item->id < 1); + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_NEW' : 'COM_EXTENSIONDISTRIBUTOR_EXTENSION_EDIT')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/extension.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + + // Add the CSS for Footable + $this->document->addStyleSheet('https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'); + JHtml::_('stylesheet', 'media/com_extensiondistributor/footable-v3/css/footable.standalone.min.css', ['version' => 'auto']); + // Add the JavaScript for Footable (adding all functions) + JHtml::_('script', 'media/com_extensiondistributor/footable-v3/js/footable.min.js', ['version' => 'auto']); + + $footable = "jQuery(document).ready(function() { jQuery(function () { jQuery('.footable').footable();});});"; + $this->document->addScriptDeclaration($footable); + + $this->document->addScript(JURI::root() . $this->script, (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + $this->document->addScript(JURI::root() . "administrator/components/com_extensiondistributor/views/extension/submitbutton.js", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + + $this->document->addStyleSheet("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/css/uikit.min.css"); + $this->document->addScript("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit.min.js"); + $this->document->addScript("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit-icons.min.js"); + + JText::script('view not acceptable. Error'); + } +} diff --git a/admin/views/extensiondistributor/index.html b/admin/views/extensiondistributor/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/extensiondistributor/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/extensiondistributor/tmpl/default.php b/admin/views/extensiondistributor/tmpl/default.php new file mode 100644 index 0000000..cbed6b0 --- /dev/null +++ b/admin/views/extensiondistributor/tmpl/default.php @@ -0,0 +1,42 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + + + +?> +
    +
    + 'main')); ?> + + loadTemplate('main');?> + + +
    +
    + 'vdm')); ?> + + loadTemplate('vdm');?> + + +
    +
    \ No newline at end of file diff --git a/admin/views/extensiondistributor/tmpl/default_main.php b/admin/views/extensiondistributor/tmpl/default_main.php new file mode 100644 index 0000000..7576a76 --- /dev/null +++ b/admin/views/extensiondistributor/tmpl/default_main.php @@ -0,0 +1,39 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> +icons['main']) && is_array($this->icons['main'])) :?> + icons['main'] as $icon): ?> + + +
    + +

    + \ No newline at end of file diff --git a/admin/views/extensiondistributor/tmpl/default_vdm.php b/admin/views/extensiondistributor/tmpl/default_vdm.php new file mode 100644 index 0000000..8027055 --- /dev/null +++ b/admin/views/extensiondistributor/tmpl/default_vdm.php @@ -0,0 +1,47 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> +<?php echo JText::_('COM_EXTENSIONDISTRIBUTOR'); ?> + +
    +contributors)): ?> + contributors) > 1): ?> +

    + +

    + +
      + contributors as $contributor): ?> +
    • :
    • + +
    +
    + \ No newline at end of file diff --git a/admin/views/extensiondistributor/tmpl/index.html b/admin/views/extensiondistributor/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/extensiondistributor/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/extensiondistributor/view.html.php b/admin/views/extensiondistributor/view.html.php new file mode 100644 index 0000000..59a0662 --- /dev/null +++ b/admin/views/extensiondistributor/view.html.php @@ -0,0 +1,99 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + + + +/** + * Extensiondistributor View class + */ +class ExtensiondistributorViewExtensiondistributor extends JViewLegacy +{ + /** + * View display method + * @return void + */ + function display($tpl = null) + { + // Assign data to the view + $this->icons = $this->get('Icons'); + $this->contributors = ExtensiondistributorHelper::getContributors(); + + // get the manifest details of the component + $this->manifest = ExtensiondistributorHelper::manifest(); + + // Set the toolbar + $this->addToolBar(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + $canDo = ExtensiondistributorHelper::getActions('extensiondistributor'); + JToolBarHelper::title(JText::_('COM_EXTENSIONDISTRIBUTOR_DASHBOARD'), 'grid-2'); + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('extensiondistributor'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + if ($canDo->get('core.admin') || $canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + $document = JFactory::getDocument(); + + // add dashboard style sheets + $document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/dashboard.css"); + + // set page title + $document->setTitle(JText::_('COM_EXTENSIONDISTRIBUTOR_DASHBOARD')); + + // add manifest to page JavaScript + $document->addScriptDeclaration("var manifest = jQuery.parseJSON('" . json_encode($this->manifest) . "');", "text/javascript"); + } +} diff --git a/admin/views/extensions/index.html b/admin/views/extensions/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/extensions/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/extensions/tmpl/default.php b/admin/views/extensions/tmpl/default.php new file mode 100644 index 0000000..5b0dfcb --- /dev/null +++ b/admin/views/extensions/tmpl/default.php @@ -0,0 +1,75 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::_('behavior.multiselect'); +JHtml::_('dropdown.init'); +JHtml::_('formbehavior.chosen', '.multipleExtensionsfiltertype', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_EXTENSION_TYPE') . ' -')); +JHtml::_('formbehavior.chosen', '.multipleCategories', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_CATEGORIES') . ' -')); +JHtml::_('formbehavior.chosen', 'select'); +if ($this->saveOrder) +{ + $saveOrderingUrl = 'index.php?option=com_extensiondistributor&task=extensions.saveOrderAjax&tmpl=component'; + JHtml::_('sortablelist.sortable', 'extensionList', 'adminForm', strtolower($this->listDirn), $saveOrderingUrl); +} +?> +
    +sidebar)): ?> +
    + sidebar; ?> +
    +
    + +
    + + $this)); +?> +items)): ?> +
    + +
    + + + loadTemplate('head');?> + loadTemplate('foot');?> + loadTemplate('body');?> +
    + + canCreate && $this->canEdit) : ?> + JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSIONS_BATCH_OPTIONS'), + 'footer' => $this->loadTemplate('batch_footer') + ), + $this->loadTemplate('batch_body') + ); ?> + + +
    + + + + diff --git a/admin/views/extensions/tmpl/default_batch_body.php b/admin/views/extensions/tmpl/default_batch_body.php new file mode 100644 index 0000000..e51c3a3 --- /dev/null +++ b/admin/views/extensions/tmpl/default_batch_body.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + +

    +batchDisplay; ?> \ No newline at end of file diff --git a/admin/views/extensions/tmpl/default_batch_footer.php b/admin/views/extensions/tmpl/default_batch_footer.php new file mode 100644 index 0000000..18ab75b --- /dev/null +++ b/admin/views/extensions/tmpl/default_batch_footer.php @@ -0,0 +1,32 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + + + \ No newline at end of file diff --git a/admin/views/extensions/tmpl/default_body.php b/admin/views/extensions/tmpl/default_body.php new file mode 100644 index 0000000..3bed67c --- /dev/null +++ b/admin/views/extensions/tmpl/default_body.php @@ -0,0 +1,173 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +$edit = "index.php?option=com_extensiondistributor&view=extensions&task=extension.edit"; + +?> +items as $i => $item): ?> + user->authorise('core.manage', 'com_checkin') || $item->checked_out == $this->user->id || $item->checked_out == 0; + $userChkOut = JFactory::getUser($item->checked_out); + $canDo = ExtensiondistributorHelper::getActions('extension',$item,'extensions'); + ?> + + + get('core.edit.state')): ?> + saveOrder) + { + $iconClass = ' inactive tip-top" hasTooltip" title="' . JHtml::tooltipText('JORDERINGDISABLED'); + } + ?> + + + + saveOrder) : ?> + + + + ⋮ + + + + get('core.edit')): ?> + checked_out) : ?> + + id); ?> + + □ + + + id); ?> + + + □ + + + +
    + get('core.edit')): ?> + escape($item->name); ?> + checked_out): ?> + name, $item->checked_out_time, 'extensions.', $canCheckin); ?> + + + escape($item->name); ?> + +
    + + +
    + + type); ?> +group) : ?>  [ + escape($item->group); ?> ] +client) : ?>  [ + client); ?> ] +
    + + +
    +id); +$latest = null; +if (ExtensiondistributorHelper::checkArray($releases)) { + $latest = $releases[0]; + unset($releases[0]); +} +$lists = ExtensiondistributorHelper::getApplicationLists(); +$stability = $lists['stability']; +?> + +escape($item->version_number); ?> +   +stability]['label'] ?> + +N/A + +
    + + +
    +release_date !== '0000-00-00 00:00:00') : ?> +release_date,'DATE_FORMAT_LC3'); ?> + +N/A + +
    + + + + + + get('core.edit.state')) : ?> + checked_out) : ?> + + published, $i, 'extensions.', true, 'cb'); ?> + + published, $i, 'extensions.', false, 'cb'); ?> + + + published, $i, 'extensions.', true, 'cb'); ?> + + + published, $i, 'extensions.', false, 'cb'); ?> + + + + id; ?> + + + \ No newline at end of file diff --git a/admin/views/extensions/tmpl/default_foot.php b/admin/views/extensions/tmpl/default_foot.php new file mode 100644 index 0000000..76ef22f --- /dev/null +++ b/admin/views/extensions/tmpl/default_foot.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + pagination->getListFooter(); ?> + \ No newline at end of file diff --git a/admin/views/extensions/tmpl/default_head.php b/admin/views/extensions/tmpl/default_head.php new file mode 100644 index 0000000..73f5e64 --- /dev/null +++ b/admin/views/extensions/tmpl/default_head.php @@ -0,0 +1,68 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + canEdit&& $this->canState): ?> + + listDirn, $this->listOrder, null, 'asc', 'JGRID_HEADING_ORDERING', 'icon-menu-2'); ?> + + + + + + + ▾ + + + ■ + + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + + + canState): ?> + + listDirn, $this->listOrder); ?> + + + + + + + + listDirn, $this->listOrder); ?> + + \ No newline at end of file diff --git a/admin/views/extensions/tmpl/default_toolbar.php b/admin/views/extensions/tmpl/default_toolbar.php new file mode 100644 index 0000000..43fc4af --- /dev/null +++ b/admin/views/extensions/tmpl/default_toolbar.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> +
    + +
    + + +
    +
    + + pagination->getLimitBox(); ?> +
    +
    + + +
    +
    + + +
    +
    +
    \ No newline at end of file diff --git a/admin/views/extensions/tmpl/index.html b/admin/views/extensions/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/extensions/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/extensions/view.html.php b/admin/views/extensions/view.html.php new file mode 100644 index 0000000..5361374 --- /dev/null +++ b/admin/views/extensions/view.html.php @@ -0,0 +1,293 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Extensions + */ +class ExtensiondistributorViewExtensions extends HtmlView +{ + /** + * Extensions view display method + * @return void + */ + function display($tpl = null) + { + if ($this->getLayout() !== 'modal') + { + // Include helper submenu + ExtensiondistributorHelper::addSubmenu('extensions'); + } + + // Assign data to the view + $this->items = $this->get('Items'); + $this->pagination = $this->get('Pagination'); + $this->state = $this->get('State'); + $this->user = JFactory::getUser(); + // Load the filter form from xml. + $this->filterForm = $this->get('FilterForm'); + // Load the active filters. + $this->activeFilters = $this->get('ActiveFilters'); + // Add the list ordering clause. + $this->listOrder = $this->escape($this->state->get('list.ordering', 'a.id')); + $this->listDirn = $this->escape($this->state->get('list.direction', 'DESC')); + $this->saveOrder = $this->listOrder == 'a.ordering'; + // set the return here value + $this->return_here = urlencode(base64_encode((string) JUri::getInstance())); + // get global action permissions + $this->canDo = ExtensiondistributorHelper::getActions('extension'); + $this->canEdit = $this->canDo->get('core.edit'); + $this->canState = $this->canDo->get('core.edit.state'); + $this->canCreate = $this->canDo->get('core.create'); + $this->canDelete = $this->canDo->get('core.delete'); + $this->canBatch = $this->canDo->get('core.batch'); + + // We don't need toolbar in the modal window. + if ($this->getLayout() !== 'modal') + { + $this->addToolbar(); + $this->sidebar = JHtmlSidebar::render(); + // load the batch html + if ($this->canCreate && $this->canEdit && $this->canState) + { + $this->batchDisplay = JHtmlBatch_::render(); + } + } + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JToolBarHelper::title(JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSIONS'), 'cube'); + JHtmlSidebar::setAction('index.php?option=com_extensiondistributor&view=extensions'); + JFormHelper::addFieldPath(JPATH_COMPONENT . '/models/fields'); + + if ($this->canCreate) + { + JToolBarHelper::addNew('extension.add'); + } + + // Only load if there are items + if (ExtensiondistributorHelper::checkArray($this->items)) + { + if ($this->canEdit) + { + JToolBarHelper::editList('extension.edit'); + } + + if ($this->canState) + { + JToolBarHelper::publishList('extensions.publish'); + JToolBarHelper::unpublishList('extensions.unpublish'); + JToolBarHelper::archiveList('extensions.archive'); + + if ($this->canDo->get('core.admin')) + { + JToolBarHelper::checkin('extensions.checkin'); + } + } + + // Add a batch button + if ($this->canBatch && $this->canCreate && $this->canEdit && $this->canState) + { + // Get the toolbar object instance + $bar = JToolBar::getInstance('toolbar'); + // set the batch button name + $title = JText::_('JTOOLBAR_BATCH'); + // Instantiate a new JLayoutFile instance and render the batch button + $layout = new JLayoutFile('joomla.toolbar.batch'); + // add the button to the page + $dhtml = $layout->render(array('title' => $title)); + $bar->appendButton('Custom', $dhtml, 'batch'); + } + + if ($this->state->get('filter.published') == -2 && ($this->canState && $this->canDelete)) + { + JToolbarHelper::deleteList('', 'extensions.delete', 'JTOOLBAR_EMPTY_TRASH'); + } + elseif ($this->canState && $this->canDelete) + { + JToolbarHelper::trash('extensions.trash'); + } + } + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('extensions'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + // add the options comp button + if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + + // Only load published batch if state and batch is allowed + if ($this->canState && $this->canBatch) + { + JHtmlBatch_::addListSelection( + JText::_('COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_STATE'), + 'batch[published]', + JHtml::_('select.options', JHtml::_('jgrid.publishedOptions', array('all' => false)), 'value', 'text', '', true) + ); + } + + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Category Batch selection. + JHtmlBatch_::addListSelection( + JText::_('COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_CATEGORY'), + 'batch[category]', + JHtml::_('select.options', JHtml::_('category.options', 'com_extensiondistributor'), 'value', 'text') + ); + } + + // Only load Type batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Type Selection + $this->typeOptions = JFormHelper::loadFieldType('extensionsfiltertype')->options; + // We do some sanitation for Type filter + if (ExtensiondistributorHelper::checkArray($this->typeOptions) && + isset($this->typeOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->typeOptions[0]->value)) + { + unset($this->typeOptions[0]); + } + // Type Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_TYPE_LABEL').' -', + 'batch[type]', + JHtml::_('select.options', $this->typeOptions, 'value', 'text') + ); + } + + // Only load Group batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Group Selection + $this->groupOptions = JFormHelper::loadFieldType('extensionsfiltergroup')->options; + // We do some sanitation for Group filter + if (ExtensiondistributorHelper::checkArray($this->groupOptions) && + isset($this->groupOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->groupOptions[0]->value)) + { + unset($this->groupOptions[0]); + } + // Group Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_GROUP_LABEL').' -', + 'batch[group]', + JHtml::_('select.options', $this->groupOptions, 'value', 'text') + ); + } + + // Only load Client batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Client Selection + $this->clientOptions = JFormHelper::loadFieldType('extensionsfilterclient')->options; + // We do some sanitation for Client filter + if (ExtensiondistributorHelper::checkArray($this->clientOptions) && + isset($this->clientOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->clientOptions[0]->value)) + { + unset($this->clientOptions[0]); + } + // Client Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_CLIENT_LABEL').' -', + 'batch[client]', + JHtml::_('select.options', $this->clientOptions, 'value', 'text') + ); + } + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSIONS')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/extensions.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 50) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Returns an array of fields the table can be sorted by + * + * @return array Array containing the field name to sort by as the key and display text as value + */ + protected function getSortFields() + { + return array( + 'a.ordering' => JText::_('JGRID_HEADING_ORDERING'), + 'a.published' => JText::_('JSTATUS'), + 'a.name' => JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_NAME_LABEL'), + 'a.type' => JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_TYPE_LABEL'), + 'a.version_number' => JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_VERSION_NUMBER'), + 'a.release_date' => JText::_('COM_EXTENSIONDISTRIBUTOR_EXTENSION_RELEASE_DATE'), + 'a.id' => JText::_('JGRID_HEADING_ID') + ); + } +} diff --git a/admin/views/import/index.html b/admin/views/import/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/import/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/import/tmpl/default.php b/admin/views/import/tmpl/default.php new file mode 100644 index 0000000..8f1b55e --- /dev/null +++ b/admin/views/import/tmpl/default.php @@ -0,0 +1,174 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html'); +JHtml::_('behavior.formvalidator'); +JHtml::_('formbehavior.chosen', 'select'); +JHtml::_('behavior.keepalive'); + +$this->app->input->set('hidemainmenu', false); + +?> +canDo->get('import.access')): ?> + +item->id)) ? '&id='. (int) $this->item->id : ''; ?> +
    + +sidebar)): ?> +
    + sidebar; ?> +
    +
    + +
    + +
    + + Drop your files on this area +
    + + ... or click here +
    +
    + +
    +
    + + + + + + + + +

    + + diff --git a/admin/views/import/tmpl/index.html b/admin/views/import/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/import/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/import/view.html.php b/admin/views/import/view.html.php new file mode 100644 index 0000000..d05493c --- /dev/null +++ b/admin/views/import/view.html.php @@ -0,0 +1,143 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Import + */ +class ExtensiondistributorViewImport extends HtmlView +{ + // Overwriting JView display method + function display($tpl = null) + { + // get component params + $this->params = JComponentHelper::getParams('com_extensiondistributor'); + // get the application + $this->app = JFactory::getApplication(); + // get the user object + $this->user = JFactory::getUser(); + // get global action permissions + $this->canDo = ExtensiondistributorHelper::getActions('import'); + // Initialise variables. + $this->item = $this->get('Item'); + // We don't need sidebar in the modal window. + if ($this->getLayout() !== 'modal') + { + //$this->app->input->set('hidemainmenu', false); + ExtensiondistributorHelper::addSubmenu('import'); + $this->sidebar = JHtmlSidebar::render(); + } + + + // We don't need toolbar in the modal window. + if ($this->getLayout() !== 'modal') + { + // add the tool bar + $this->addToolBar(); + } + + // set the document + $this->setDocument(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode(PHP_EOL, $errors), 500); + } + + parent::display($tpl); + } + + /** + * Prepares the document + */ + protected function setDocument() + { + + // always make sure jquery is loaded. + JHtml::_('jquery.framework'); + // Load the header checker class. + require_once( JPATH_COMPONENT_ADMINISTRATOR.'/helpers/headercheck.php' ); + // Initialize the header checker. + $HeaderCheck = new extensiondistributorHeaderCheck; + + $this->document->addStyleSheet("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/css/uikit.min.css"); + $this->document->addScript("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit.min.js"); + $this->document->addScript("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit-icons.min.js"); + + + // add the document default css file + $this->document->addStyleSheet(JURI::root(true) .'/administrator/components/com_extensiondistributor/assets/css/import.css', (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + // hide the main menu + $this->app->input->set('hidemainmenu', true); + // set the title + if (isset($this->item->name) && $this->item->name) + { + $title = $this->item->name; + } + // Check for empty title and add view name if param is set + if (empty($title)) + { + $title = JText::_('COM_EXTENSIONDISTRIBUTOR_IMPORT'); + } + // add title to the page + JToolbarHelper::title($title,'download'); + // add cpanel button + JToolBarHelper::custom('import.dashboard', 'grid-2', '', 'COM_EXTENSIONDISTRIBUTOR_DASH', false); + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('import'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + // add the options comp button + if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } +} +?> diff --git a/admin/views/index.html b/admin/views/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/key/submitbutton.js b/admin/views/key/submitbutton.js new file mode 100644 index 0000000..df7a0a1 --- /dev/null +++ b/admin/views/key/submitbutton.js @@ -0,0 +1,34 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage submitbutton.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +Joomla.submitbutton = function(task) +{ + if (task == ''){ + return false; + } else { + var action = task.split('.'); + if (action[1] == 'cancel' || action[1] == 'close' || document.formvalidator.isValid(document.getElementById("adminForm"))){ + Joomla.submitform(task, document.getElementById("adminForm")); + return true; + } else { + alert(Joomla.JText._('key, some values are not acceptable.','Some values are unacceptable')); + return false; + } + } +} \ No newline at end of file diff --git a/admin/views/key/tmpl/edit.php b/admin/views/key/tmpl/edit.php new file mode 100644 index 0000000..0ee2e2a --- /dev/null +++ b/admin/views/key/tmpl/edit.php @@ -0,0 +1,138 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html'); +JHtml::_('behavior.formvalidator'); +JHtml::_('formbehavior.chosen', 'select'); +JHtml::_('behavior.keepalive'); +$componentParams = $this->params; // will be removed just use $this->params instead +?> + + + + diff --git a/admin/views/key/tmpl/index.html b/admin/views/key/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/key/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/key/view.html.php b/admin/views/key/view.html.php new file mode 100644 index 0000000..36affe8 --- /dev/null +++ b/admin/views/key/view.html.php @@ -0,0 +1,206 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Key Html View class + */ +class ExtensiondistributorViewKey extends HtmlView +{ + /** + * display method of View + * @return void + */ + public function display($tpl = null) + { + // set params + $this->params = JComponentHelper::getParams('com_extensiondistributor'); + // Assign the variables + $this->form = $this->get('Form'); + $this->item = $this->get('Item'); + $this->script = $this->get('Script'); + $this->state = $this->get('State'); + // get action permissions + $this->canDo = ExtensiondistributorHelper::getActions('key', $this->item); + // get input + $jinput = JFactory::getApplication()->input; + $this->ref = $jinput->get('ref', 0, 'word'); + $this->refid = $jinput->get('refid', 0, 'int'); + $return = $jinput->get('return', null, 'base64'); + // set the referral string + $this->referral = ''; + if ($this->refid && $this->ref) + { + // return to the item that referred to this item + $this->referral = '&ref=' . (string)$this->ref . '&refid=' . (int)$this->refid; + } + elseif($this->ref) + { + // return to the list view that referred to this item + $this->referral = '&ref=' . (string)$this->ref; + } + // check return value + if (!is_null($return)) + { + // add the return value + $this->referral .= '&return=' . (string)$return; + } + + // Set the toolbar + $this->addToolBar(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JFactory::getApplication()->input->set('hidemainmenu', true); + $user = JFactory::getUser(); + $userId = $user->id; + $isNew = $this->item->id == 0; + + JToolbarHelper::title( JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_KEY_NEW' : 'COM_EXTENSIONDISTRIBUTOR_KEY_EDIT'), 'pencil-2 article-add'); + // Built the actions for new and existing records. + if (ExtensiondistributorHelper::checkString($this->referral)) + { + if ($this->canDo->get('core.create') && $isNew) + { + // We can create the record. + JToolBarHelper::save('key.save', 'JTOOLBAR_SAVE'); + } + elseif ($this->canDo->get('core.edit')) + { + // We can save the record. + JToolBarHelper::save('key.save', 'JTOOLBAR_SAVE'); + } + if ($isNew) + { + // Do not creat but cancel. + JToolBarHelper::cancel('key.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + // We can close it. + JToolBarHelper::cancel('key.cancel', 'JTOOLBAR_CLOSE'); + } + } + else + { + if ($isNew) + { + // For new records, check the create permission. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::apply('key.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('key.save', 'JTOOLBAR_SAVE'); + JToolBarHelper::custom('key.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + }; + JToolBarHelper::cancel('key.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + if ($this->canDo->get('core.edit')) + { + // We can save the new record + JToolBarHelper::apply('key.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('key.save', 'JTOOLBAR_SAVE'); + // We can save this record, but check the create permission to see + // if we can return to make a new one. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('key.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + } + } + $canVersion = ($this->canDo->get('core.version') && $this->canDo->get('key.version')); + if ($this->state->params->get('save_history', 1) && $this->canDo->get('core.edit') && $canVersion) + { + JToolbarHelper::versions('com_extensiondistributor.key', $this->item->id); + } + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('key.save2copy', 'save-copy.png', 'save-copy_f2.png', 'JTOOLBAR_SAVE_AS_COPY', false); + } + JToolBarHelper::cancel('key.cancel', 'JTOOLBAR_CLOSE'); + } + } + JToolbarHelper::divider(); + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('key'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 30) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true, 30); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + $isNew = ($this->item->id < 1); + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_KEY_NEW' : 'COM_EXTENSIONDISTRIBUTOR_KEY_EDIT')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/key.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + $this->document->addScript(JURI::root() . $this->script, (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + $this->document->addScript(JURI::root() . "administrator/components/com_extensiondistributor/views/key/submitbutton.js", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + JText::script('view not acceptable. Error'); + } +} diff --git a/admin/views/keys/index.html b/admin/views/keys/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/keys/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/keys/tmpl/default.php b/admin/views/keys/tmpl/default.php new file mode 100644 index 0000000..197b130 --- /dev/null +++ b/admin/views/keys/tmpl/default.php @@ -0,0 +1,92 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::_('behavior.multiselect'); +JHtml::_('dropdown.init'); +JHtml::_('formbehavior.chosen', '.multipleKeysfilteruser', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_JOOMLA_USER') . ' -')); +JHtml::_('formbehavior.chosen', 'select'); +if ($this->saveOrder) +{ + $saveOrderingUrl = 'index.php?option=com_extensiondistributor&task=keys.saveOrderAjax&tmpl=component'; + JHtml::_('sortablelist.sortable', 'keyList', 'adminForm', strtolower($this->listDirn), $saveOrderingUrl); +} +?> +
    +sidebar)): ?> +
    + sidebar; ?> +
    +
    + +
    + + $this)); +?> +items)): ?> +
    + +
    + + + loadTemplate('head');?> + loadTemplate('foot');?> + loadTemplate('body');?> +
    + + canCreate && $this->canEdit) : ?> + JText::_('COM_EXTENSIONDISTRIBUTOR_KEYS_BATCH_OPTIONS'), + 'footer' => $this->loadTemplate('batch_footer') + ), + $this->loadTemplate('batch_body') + ); ?> + + +
    + + + + + diff --git a/admin/views/keys/tmpl/default_batch_body.php b/admin/views/keys/tmpl/default_batch_body.php new file mode 100644 index 0000000..a833017 --- /dev/null +++ b/admin/views/keys/tmpl/default_batch_body.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + +

    +batchDisplay; ?> \ No newline at end of file diff --git a/admin/views/keys/tmpl/default_batch_footer.php b/admin/views/keys/tmpl/default_batch_footer.php new file mode 100644 index 0000000..09193f3 --- /dev/null +++ b/admin/views/keys/tmpl/default_batch_footer.php @@ -0,0 +1,32 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + + + \ No newline at end of file diff --git a/admin/views/keys/tmpl/default_body.php b/admin/views/keys/tmpl/default_body.php new file mode 100644 index 0000000..6126e41 --- /dev/null +++ b/admin/views/keys/tmpl/default_body.php @@ -0,0 +1,106 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +$edit = "index.php?option=com_extensiondistributor&view=keys&task=key.edit"; + +?> +items as $i => $item): ?> + user->authorise('core.manage', 'com_checkin') || $item->checked_out == $this->user->id || $item->checked_out == 0; + $userChkOut = JFactory::getUser($item->checked_out); + $canDo = ExtensiondistributorHelper::getActions('key',$item,'keys'); + ?> + + + get('core.edit.state')): ?> + saveOrder) + { + $iconClass = ' inactive tip-top" hasTooltip" title="' . JHtml::tooltipText('JORDERINGDISABLED'); + } + ?> + + + + saveOrder) : ?> + + + + ⋮ + + + + get('core.edit')): ?> + checked_out) : ?> + + id); ?> + + □ + + + id); ?> + + + □ + + + +
    + get('core.edit')): ?> + escape($item->key); ?> + checked_out): ?> + name, $item->checked_out_time, 'keys.', $canCheckin); ?> + + + escape($item->key); ?> + +
    + + + escape($item->website); ?> + + + user)->name; ?> + + + get('core.edit.state')) : ?> + checked_out) : ?> + + published, $i, 'keys.', true, 'cb'); ?> + + published, $i, 'keys.', false, 'cb'); ?> + + + published, $i, 'keys.', true, 'cb'); ?> + + + published, $i, 'keys.', false, 'cb'); ?> + + + + id; ?> + + + \ No newline at end of file diff --git a/admin/views/keys/tmpl/default_foot.php b/admin/views/keys/tmpl/default_foot.php new file mode 100644 index 0000000..ffdec49 --- /dev/null +++ b/admin/views/keys/tmpl/default_foot.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + pagination->getListFooter(); ?> + \ No newline at end of file diff --git a/admin/views/keys/tmpl/default_head.php b/admin/views/keys/tmpl/default_head.php new file mode 100644 index 0000000..eb08edf --- /dev/null +++ b/admin/views/keys/tmpl/default_head.php @@ -0,0 +1,62 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + canEdit&& $this->canState): ?> + + listDirn, $this->listOrder, null, 'asc', 'JGRID_HEADING_ORDERING', 'icon-menu-2'); ?> + + + + + + + ▾ + + + ■ + + + + + + + + + + + + canState): ?> + + listDirn, $this->listOrder); ?> + + + + + + + + listDirn, $this->listOrder); ?> + + \ No newline at end of file diff --git a/admin/views/keys/tmpl/default_toolbar.php b/admin/views/keys/tmpl/default_toolbar.php new file mode 100644 index 0000000..f6d18a5 --- /dev/null +++ b/admin/views/keys/tmpl/default_toolbar.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> +
    + +
    + + +
    +
    + + pagination->getLimitBox(); ?> +
    +
    + + +
    +
    + + +
    +
    +
    \ No newline at end of file diff --git a/admin/views/keys/tmpl/index.html b/admin/views/keys/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/keys/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/keys/view.html.php b/admin/views/keys/view.html.php new file mode 100644 index 0000000..320b180 --- /dev/null +++ b/admin/views/keys/view.html.php @@ -0,0 +1,239 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Keys + */ +class ExtensiondistributorViewKeys extends HtmlView +{ + /** + * Keys view display method + * @return void + */ + function display($tpl = null) + { + if ($this->getLayout() !== 'modal') + { + // Include helper submenu + ExtensiondistributorHelper::addSubmenu('keys'); + } + + // Assign data to the view + $this->items = $this->get('Items'); + $this->pagination = $this->get('Pagination'); + $this->state = $this->get('State'); + $this->user = JFactory::getUser(); + // Load the filter form from xml. + $this->filterForm = $this->get('FilterForm'); + // Load the active filters. + $this->activeFilters = $this->get('ActiveFilters'); + // Add the list ordering clause. + $this->listOrder = $this->escape($this->state->get('list.ordering', 'a.id')); + $this->listDirn = $this->escape($this->state->get('list.direction', 'DESC')); + $this->saveOrder = $this->listOrder == 'a.ordering'; + // set the return here value + $this->return_here = urlencode(base64_encode((string) JUri::getInstance())); + // get global action permissions + $this->canDo = ExtensiondistributorHelper::getActions('key'); + $this->canEdit = $this->canDo->get('core.edit'); + $this->canState = $this->canDo->get('core.edit.state'); + $this->canCreate = $this->canDo->get('core.create'); + $this->canDelete = $this->canDo->get('core.delete'); + $this->canBatch = $this->canDo->get('core.batch'); + + // We don't need toolbar in the modal window. + if ($this->getLayout() !== 'modal') + { + $this->addToolbar(); + $this->sidebar = JHtmlSidebar::render(); + // load the batch html + if ($this->canCreate && $this->canEdit && $this->canState) + { + $this->batchDisplay = JHtmlBatch_::render(); + } + } + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JToolBarHelper::title(JText::_('COM_EXTENSIONDISTRIBUTOR_KEYS'), 'key'); + JHtmlSidebar::setAction('index.php?option=com_extensiondistributor&view=keys'); + JFormHelper::addFieldPath(JPATH_COMPONENT . '/models/fields'); + + if ($this->canCreate) + { + JToolBarHelper::addNew('key.add'); + } + + // Only load if there are items + if (ExtensiondistributorHelper::checkArray($this->items)) + { + if ($this->canEdit) + { + JToolBarHelper::editList('key.edit'); + } + + if ($this->canState) + { + JToolBarHelper::publishList('keys.publish'); + JToolBarHelper::unpublishList('keys.unpublish'); + JToolBarHelper::archiveList('keys.archive'); + + if ($this->canDo->get('core.admin')) + { + JToolBarHelper::checkin('keys.checkin'); + } + } + + // Add a batch button + if ($this->canBatch && $this->canCreate && $this->canEdit && $this->canState) + { + // Get the toolbar object instance + $bar = JToolBar::getInstance('toolbar'); + // set the batch button name + $title = JText::_('JTOOLBAR_BATCH'); + // Instantiate a new JLayoutFile instance and render the batch button + $layout = new JLayoutFile('joomla.toolbar.batch'); + // add the button to the page + $dhtml = $layout->render(array('title' => $title)); + $bar->appendButton('Custom', $dhtml, 'batch'); + } + + if ($this->state->get('filter.published') == -2 && ($this->canState && $this->canDelete)) + { + JToolbarHelper::deleteList('', 'keys.delete', 'JTOOLBAR_EMPTY_TRASH'); + } + elseif ($this->canState && $this->canDelete) + { + JToolbarHelper::trash('keys.trash'); + } + } + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('keys'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + // add the options comp button + if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + + // Only load published batch if state and batch is allowed + if ($this->canState && $this->canBatch) + { + JHtmlBatch_::addListSelection( + JText::_('COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_STATE'), + 'batch[published]', + JHtml::_('select.options', JHtml::_('jgrid.publishedOptions', array('all' => false)), 'value', 'text', '', true) + ); + } + + // Only load User batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set User Selection + $this->userOptions = JFormHelper::loadFieldType('keysfilteruser')->options; + // We do some sanitation for User filter + if (ExtensiondistributorHelper::checkArray($this->userOptions) && + isset($this->userOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->userOptions[0]->value)) + { + unset($this->userOptions[0]); + } + // User Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_KEY_USER_LABEL').' -', + 'batch[user]', + JHtml::_('select.options', $this->userOptions, 'value', 'text') + ); + } + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_('COM_EXTENSIONDISTRIBUTOR_KEYS')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/keys.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 50) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Returns an array of fields the table can be sorted by + * + * @return array Array containing the field name to sort by as the key and display text as value + */ + protected function getSortFields() + { + return array( + 'a.ordering' => JText::_('JGRID_HEADING_ORDERING'), + 'a.published' => JText::_('JSTATUS'), + 'a.id' => JText::_('JGRID_HEADING_ID') + ); + } +} diff --git a/admin/views/maintenance/index.html b/admin/views/maintenance/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/maintenance/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/maintenance/tmpl/default.php b/admin/views/maintenance/tmpl/default.php new file mode 100644 index 0000000..52f32b2 --- /dev/null +++ b/admin/views/maintenance/tmpl/default.php @@ -0,0 +1,229 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html'); +JHtml::_('behavior.formvalidator'); +JHtml::_('formbehavior.chosen', 'select'); +JHtml::_('behavior.keepalive'); + +$this->app->input->set('hidemainmenu', false); + +?> +canDo->get('maintenance.access')): ?> + +item->id)) ? '&id='. (int) $this->item->id : ''; ?> + +sidebar)): ?> +
    + sidebar; ?> +
    +
    + +
    + +
    +
    +
    +

    Directories configuration

    +
    This the place to configure and secure your files directories
    +
    +
    + directoryform) : ?> + directoryform as $field) : ?> +
    +
    label;?>
    +
    input;?>
    +
    + +
    + +
    + + + +
    +
    +

    Rename all release files

    +
    Execute this task to rename all release files according to the file pattern. This is useful when you change it and want to apply changes to already created files
    +
    +
    +
    + +
    + + +
    +
    +
    +
    +

    Duplicates

    +
    A file is not supposed to be used twice. Here is the list of the duplicated files. Please make sure everything is correct.
    +
    + duplicates) : ?> +
      + duplicates as $duplicate) : ?> + +
    • +
      File :
      +
      Usage :
      + + +
      + [e_type ? $release->e_type : 'package' ?>] + e_name ? $release->e_name : $release->p_name ?> + | vversion_number ?> + | rid ?> + + + Edit release + + extension) : ?> + + Edit extension + + + + Edit package + + + +
      + + +
    • + +
    + +
    No duplicates found
    + +
    +

    Orphans

    +
    Look for unused files and delete them to save some disk space
    +
    + orphans) : ?> +
    +
      + orphans as $orphan) : ?> +
    • + + + +   + +
    • + +
    +
    + +
    + + +
    + +
    No orphans found
    + +
    +

    Missing

    +
    Look for missing release files
    +
    + missing) : ?> +
    + + + +
    + +
    No missing files found
    + +
    +
    + + + + +

    + + diff --git a/admin/views/maintenance/tmpl/index.html b/admin/views/maintenance/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/maintenance/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/maintenance/view.html.php b/admin/views/maintenance/view.html.php new file mode 100644 index 0000000..1649bb4 --- /dev/null +++ b/admin/views/maintenance/view.html.php @@ -0,0 +1,141 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Maintenance + */ +class ExtensiondistributorViewMaintenance extends HtmlView +{ + // Overwriting JView display method + function display($tpl = null) + { + // get component params + $this->params = JComponentHelper::getParams('com_extensiondistributor'); + // get the application + $this->app = JFactory::getApplication(); + // get the user object + $this->user = JFactory::getUser(); + // get global action permissions + $this->canDo = ExtensiondistributorHelper::getActions('maintenance'); + // Initialise variables. + $this->item = $this->get('Item'); + $this->duplicates = $this->get('Duplicates'); + $this->orphans = $this->get('Orphans'); + $this->missing = $this->get('Missing'); + $this->directoryform = $this->get('DirectoryForm'); + // We don't need sidebar in the modal window. + if ($this->getLayout() !== 'modal') + { + //$this->app->input->set('hidemainmenu', false); + ExtensiondistributorHelper::addSubmenu('maintenance'); + $this->sidebar = JHtmlSidebar::render(); + } + + + // We don't need toolbar in the modal window. + if ($this->getLayout() !== 'modal') + { + // add the tool bar + $this->addToolBar(); + } + + // set the document + $this->setDocument(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode(PHP_EOL, $errors), 500); + } + + parent::display($tpl); + } + + /** + * Prepares the document + */ + protected function setDocument() + { + + // always make sure jquery is loaded. + JHtml::_('jquery.framework'); + // Load the header checker class. + require_once( JPATH_COMPONENT_ADMINISTRATOR.'/helpers/headercheck.php' ); + // Initialize the header checker. + $HeaderCheck = new extensiondistributorHeaderCheck; + // add the document default css file + $this->document->addStyleSheet(JURI::root(true) .'/administrator/components/com_extensiondistributor/assets/css/maintenance.css', (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + // hide the main menu + $this->app->input->set('hidemainmenu', true); + // set the title + if (isset($this->item->name) && $this->item->name) + { + $title = $this->item->name; + } + // Check for empty title and add view name if param is set + if (empty($title)) + { + $title = JText::_('COM_EXTENSIONDISTRIBUTOR_MAINTENANCE'); + } + // add title to the page + JToolbarHelper::title($title,'cogs'); + // add cpanel button + JToolBarHelper::custom('maintenance.dashboard', 'grid-2', '', 'COM_EXTENSIONDISTRIBUTOR_DASH', false); + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('maintenance'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + // add the options comp button + if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } +} +?> diff --git a/admin/views/package/submitbutton.js b/admin/views/package/submitbutton.js new file mode 100644 index 0000000..20cf18f --- /dev/null +++ b/admin/views/package/submitbutton.js @@ -0,0 +1,34 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage submitbutton.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +Joomla.submitbutton = function(task) +{ + if (task == ''){ + return false; + } else { + var action = task.split('.'); + if (action[1] == 'cancel' || action[1] == 'close' || document.formvalidator.isValid(document.getElementById("adminForm"))){ + Joomla.submitform(task, document.getElementById("adminForm")); + return true; + } else { + alert(Joomla.JText._('package, some values are not acceptable.','Some values are unacceptable')); + return false; + } + } +} \ No newline at end of file diff --git a/admin/views/package/tmpl/edit.php b/admin/views/package/tmpl/edit.php new file mode 100644 index 0000000..4fd7e72 --- /dev/null +++ b/admin/views/package/tmpl/edit.php @@ -0,0 +1,327 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html'); +JHtml::_('behavior.formvalidator'); +JHtml::_('formbehavior.chosen', 'select'); +JHtml::_('behavior.keepalive'); +$componentParams = $this->params; // will be removed just use $this->params instead +?> + + + + diff --git a/admin/views/package/tmpl/index.html b/admin/views/package/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/package/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/package/view.html.php b/admin/views/package/view.html.php new file mode 100644 index 0000000..59f3a20 --- /dev/null +++ b/admin/views/package/view.html.php @@ -0,0 +1,224 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Package Html View class + */ +class ExtensiondistributorViewPackage extends HtmlView +{ + /** + * display method of View + * @return void + */ + public function display($tpl = null) + { + // set params + $this->params = JComponentHelper::getParams('com_extensiondistributor'); + // Assign the variables + $this->form = $this->get('Form'); + $this->item = $this->get('Item'); + $this->script = $this->get('Script'); + $this->state = $this->get('State'); + // get action permissions + $this->canDo = ExtensiondistributorHelper::getActions('package', $this->item); + // get input + $jinput = JFactory::getApplication()->input; + $this->ref = $jinput->get('ref', 0, 'word'); + $this->refid = $jinput->get('refid', 0, 'int'); + $return = $jinput->get('return', null, 'base64'); + // set the referral string + $this->referral = ''; + if ($this->refid && $this->ref) + { + // return to the item that referred to this item + $this->referral = '&ref=' . (string)$this->ref . '&refid=' . (int)$this->refid; + } + elseif($this->ref) + { + // return to the list view that referred to this item + $this->referral = '&ref=' . (string)$this->ref; + } + // check return value + if (!is_null($return)) + { + // add the return value + $this->referral .= '&return=' . (string)$return; + } + + // Get Linked view data + $this->vvvreleases = $this->get('Vvvreleases'); + + // Set the toolbar + $this->addToolBar(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JFactory::getApplication()->input->set('hidemainmenu', true); + $user = JFactory::getUser(); + $userId = $user->id; + $isNew = $this->item->id == 0; + + JToolbarHelper::title( JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_PACKAGE_NEW' : 'COM_EXTENSIONDISTRIBUTOR_PACKAGE_EDIT'), 'pencil-2 article-add'); + // Built the actions for new and existing records. + if (ExtensiondistributorHelper::checkString($this->referral)) + { + if ($this->canDo->get('core.create') && $isNew) + { + // We can create the record. + JToolBarHelper::save('package.save', 'JTOOLBAR_SAVE'); + } + elseif ($this->canDo->get('core.edit')) + { + // We can save the record. + JToolBarHelper::save('package.save', 'JTOOLBAR_SAVE'); + } + if ($isNew) + { + // Do not creat but cancel. + JToolBarHelper::cancel('package.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + // We can close it. + JToolBarHelper::cancel('package.cancel', 'JTOOLBAR_CLOSE'); + } + } + else + { + if ($isNew) + { + // For new records, check the create permission. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::apply('package.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('package.save', 'JTOOLBAR_SAVE'); + JToolBarHelper::custom('package.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + }; + JToolBarHelper::cancel('package.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + if ($this->canDo->get('core.edit')) + { + // We can save the new record + JToolBarHelper::apply('package.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('package.save', 'JTOOLBAR_SAVE'); + // We can save this record, but check the create permission to see + // if we can return to make a new one. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('package.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + } + } + $canVersion = ($this->canDo->get('core.version') && $this->canDo->get('package.version')); + if ($this->state->params->get('save_history', 1) && $this->canDo->get('core.edit') && $canVersion) + { + JToolbarHelper::versions('com_extensiondistributor.package', $this->item->id); + } + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('package.save2copy', 'save-copy.png', 'save-copy_f2.png', 'JTOOLBAR_SAVE_AS_COPY', false); + } + JToolBarHelper::cancel('package.cancel', 'JTOOLBAR_CLOSE'); + } + } + JToolbarHelper::divider(); + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('package'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 30) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true, 30); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + $isNew = ($this->item->id < 1); + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_PACKAGE_NEW' : 'COM_EXTENSIONDISTRIBUTOR_PACKAGE_EDIT')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/package.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + + // Add the CSS for Footable + $this->document->addStyleSheet('https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'); + JHtml::_('stylesheet', 'media/com_extensiondistributor/footable-v3/css/footable.standalone.min.css', ['version' => 'auto']); + // Add the JavaScript for Footable (adding all functions) + JHtml::_('script', 'media/com_extensiondistributor/footable-v3/js/footable.min.js', ['version' => 'auto']); + + $footable = "jQuery(document).ready(function() { jQuery(function () { jQuery('.footable').footable();});});"; + $this->document->addScriptDeclaration($footable); + + $this->document->addScript(JURI::root() . $this->script, (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + $this->document->addScript(JURI::root() . "administrator/components/com_extensiondistributor/views/package/submitbutton.js", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + + $this->document->addStyleSheet("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/css/uikit.min.css"); + $this->document->addScript("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit.min.js"); + $this->document->addScript("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit-icons.min.js"); + + JText::script('view not acceptable. Error'); + } +} diff --git a/admin/views/packages/index.html b/admin/views/packages/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/packages/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/packages/tmpl/default.php b/admin/views/packages/tmpl/default.php new file mode 100644 index 0000000..843761d --- /dev/null +++ b/admin/views/packages/tmpl/default.php @@ -0,0 +1,99 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::_('behavior.multiselect'); +JHtml::_('dropdown.init'); +JHtml::_('formbehavior.chosen', '.multiple', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_CATEGORIES') . ' -')); +JHtml::_('formbehavior.chosen', '.multipleAccessLevels', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_ACCESS') . ' -')); +JHtml::_('formbehavior.chosen', 'select'); +if ($this->saveOrder) +{ + $saveOrderingUrl = 'index.php?option=com_extensiondistributor&task=packages.saveOrderAjax&tmpl=component'; + JHtml::_('sortablelist.sortable', 'packageList', 'adminForm', strtolower($this->listDirn), $saveOrderingUrl); +} +?> +
    +sidebar)): ?> +
    + sidebar; ?> +
    +
    + +
    + + $this)); +?> +items)): ?> +
    + +
    + + + loadTemplate('head');?> + loadTemplate('foot');?> + loadTemplate('body');?> +
    + + canCreate && $this->canEdit) : ?> + JText::_('COM_EXTENSIONDISTRIBUTOR_PACKAGES_BATCH_OPTIONS'), + 'footer' => $this->loadTemplate('batch_footer') + ), + $this->loadTemplate('batch_body') + ); ?> + + +
    + + + + + diff --git a/admin/views/packages/tmpl/default_batch_body.php b/admin/views/packages/tmpl/default_batch_body.php new file mode 100644 index 0000000..58cf3b2 --- /dev/null +++ b/admin/views/packages/tmpl/default_batch_body.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + +

    +batchDisplay; ?> \ No newline at end of file diff --git a/admin/views/packages/tmpl/default_batch_footer.php b/admin/views/packages/tmpl/default_batch_footer.php new file mode 100644 index 0000000..7aa6e33 --- /dev/null +++ b/admin/views/packages/tmpl/default_batch_footer.php @@ -0,0 +1,32 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + + + \ No newline at end of file diff --git a/admin/views/packages/tmpl/default_body.php b/admin/views/packages/tmpl/default_body.php new file mode 100644 index 0000000..38cbed7 --- /dev/null +++ b/admin/views/packages/tmpl/default_body.php @@ -0,0 +1,254 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +$edit = "index.php?option=com_extensiondistributor&view=packages&task=package.edit"; + +?> +items as $i => $item): ?> + user->authorise('core.manage', 'com_checkin') || $item->checked_out == $this->user->id || $item->checked_out == 0; + $userChkOut = JFactory::getUser($item->checked_out); + $canDo = ExtensiondistributorHelper::getActions('package',$item,'packages'); + ?> + + + get('core.edit.state')): ?> + saveOrder) + { + $iconClass = ' inactive tip-top" hasTooltip" title="' . JHtml::tooltipText('JORDERINGDISABLED'); + } + ?> + + + + saveOrder) : ?> + + + + ⋮ + + + + get('core.edit')): ?> + checked_out) : ?> + + id); ?> + + □ + + + id); ?> + + + □ + + + +
    + get('core.edit')): ?> + escape($item->name); ?> + checked_out): ?> + name, $item->checked_out_time, 'packages.', $canCheckin); ?> + + + escape($item->name); ?> + catid) : ?>
    Category: + user->authorise('core.edit', 'com_extensiondistributor.extension.category.' . (int)$item->catid)): ?> + escape($item->category_title); ?> + + escape($item->category_title); ?> + +
    + + + escape($item->description); ?> + + +
    id); +$releases = ExtensiondistributorHelper::getReleases($item->id,'package'); +$latest = null; +if (ExtensiondistributorHelper::checkArray($releases)) { + $latest = $releases[0]; + unset($releases[0]); +} +$lists = ExtensiondistributorHelper::getApplicationLists(); +$html = array(); +foreach ($content['extensions'] as $extension) { + $html[] = ExtensiondistributorHelper::createBadge( + $extension['details']->id, + $extension['details']->name, + $extension['details']->type, + $extension['details']->group, + $extension['details']->client + ); +} +echo implode(' ',$html); +?> + +
    + + +
    +file != '-1') : ?> +type === 'component') { + $componentVersion = $element['release']->version_number; + $componentDate = $element['release']->release_date; + } +} +if ($componentVersion > $item->version_number) { + $notice = '  '; +} elseif ($componentVersion < $item->version_number) { + $notice = '  '; +} +?> + + escape($item->version_number); ?>
    + release_date, 'DATE_FORMAT_FILTER_DATE'); ?> + +N/A + +
    + +
    + + +
    + +file != '-1') : ?> + + + Download + + + + + + +update) : ?> +get('updates_directory','updates').'/'); +$update_file = $update_path . $item->update; +$update_link = JUri::root().$cparams->get('updates_directory','updates').'/'.$item->update; +$existsXml = JFile::exists($update_file); +$downloadXml = '#'; +$color = 'danger'; +$tooltipCopy = 'No update file available'; +$tooltipDownload = 'No update file available'; +$tooltipUpload = 'No update file available
    Click to create a new one'; +$iconUpload = 'upload'; +if ($existsXml) { + $tooltipCopy = 'Copy the update URL'; + $tooltipDownload = 'Download the XML update file'; + $tooltipUpload = 'XML file is not up to date
    Click to update it'; + $downloadXml = 'index.php?option=com_extensiondistributor&task=extension.download&package='.$item->id.'&'.JSession::getFormToken().'=1'; + $versions = ExtensiondistributorHelper::getXmlVersions($update_file); + $color = 'warning'; + if (in_array($item->version_number, $versions)) { + $tooltipUpload = 'XML file is up to date
    Click to force refresh'; + $color = 'success'; + $iconUpload = 'loop'; + } +} +?> + + + + + + + + + + + + + +N/A + +
    + +
    + + + free); ?> + + + get('core.edit.state')) : ?> + checked_out) : ?> + + published, $i, 'packages.', true, 'cb'); ?> + + published, $i, 'packages.', false, 'cb'); ?> + + + published, $i, 'packages.', true, 'cb'); ?> + + + published, $i, 'packages.', false, 'cb'); ?> + + + + id; ?> + + + \ No newline at end of file diff --git a/admin/views/packages/tmpl/default_foot.php b/admin/views/packages/tmpl/default_foot.php new file mode 100644 index 0000000..9dcb0f8 --- /dev/null +++ b/admin/views/packages/tmpl/default_foot.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + pagination->getListFooter(); ?> + \ No newline at end of file diff --git a/admin/views/packages/tmpl/default_head.php b/admin/views/packages/tmpl/default_head.php new file mode 100644 index 0000000..6fc45cd --- /dev/null +++ b/admin/views/packages/tmpl/default_head.php @@ -0,0 +1,71 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + canEdit&& $this->canState): ?> + + listDirn, $this->listOrder, null, 'asc', 'JGRID_HEADING_ORDERING', 'icon-menu-2'); ?> + + + + + + + ▾ + + + ■ + + + + listDirn, $this->listOrder); ?> + + + + + + + + + + + + + + + + + canState): ?> + + listDirn, $this->listOrder); ?> + + + + + + + + listDirn, $this->listOrder); ?> + + \ No newline at end of file diff --git a/admin/views/packages/tmpl/default_toolbar.php b/admin/views/packages/tmpl/default_toolbar.php new file mode 100644 index 0000000..932c09d --- /dev/null +++ b/admin/views/packages/tmpl/default_toolbar.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> +
    + +
    + + +
    +
    + + pagination->getLimitBox(); ?> +
    +
    + + +
    +
    + + +
    +
    +
    \ No newline at end of file diff --git a/admin/views/packages/tmpl/index.html b/admin/views/packages/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/packages/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/packages/view.html.php b/admin/views/packages/view.html.php new file mode 100644 index 0000000..bab3e68 --- /dev/null +++ b/admin/views/packages/view.html.php @@ -0,0 +1,260 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Packages + */ +class ExtensiondistributorViewPackages extends HtmlView +{ + /** + * Packages view display method + * @return void + */ + function display($tpl = null) + { + if ($this->getLayout() !== 'modal') + { + // Include helper submenu + ExtensiondistributorHelper::addSubmenu('packages'); + } + + // Assign data to the view + $this->items = $this->get('Items'); + $this->pagination = $this->get('Pagination'); + $this->state = $this->get('State'); + $this->user = JFactory::getUser(); + // Load the filter form from xml. + $this->filterForm = $this->get('FilterForm'); + // Load the active filters. + $this->activeFilters = $this->get('ActiveFilters'); + // Add the list ordering clause. + $this->listOrder = $this->escape($this->state->get('list.ordering', 'a.id')); + $this->listDirn = $this->escape($this->state->get('list.direction', 'DESC')); + $this->saveOrder = $this->listOrder == 'a.ordering'; + // set the return here value + $this->return_here = urlencode(base64_encode((string) JUri::getInstance())); + // get global action permissions + $this->canDo = ExtensiondistributorHelper::getActions('package'); + $this->canEdit = $this->canDo->get('core.edit'); + $this->canState = $this->canDo->get('core.edit.state'); + $this->canCreate = $this->canDo->get('core.create'); + $this->canDelete = $this->canDo->get('core.delete'); + $this->canBatch = $this->canDo->get('core.batch'); + + // We don't need toolbar in the modal window. + if ($this->getLayout() !== 'modal') + { + $this->addToolbar(); + $this->sidebar = JHtmlSidebar::render(); + // load the batch html + if ($this->canCreate && $this->canEdit && $this->canState) + { + $this->batchDisplay = JHtmlBatch_::render(); + } + } + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JToolBarHelper::title(JText::_('COM_EXTENSIONDISTRIBUTOR_PACKAGES'), 'joomla'); + JHtmlSidebar::setAction('index.php?option=com_extensiondistributor&view=packages'); + JFormHelper::addFieldPath(JPATH_COMPONENT . '/models/fields'); + + if ($this->canCreate) + { + JToolBarHelper::addNew('package.add'); + } + + // Only load if there are items + if (ExtensiondistributorHelper::checkArray($this->items)) + { + if ($this->canEdit) + { + JToolBarHelper::editList('package.edit'); + } + + if ($this->canState) + { + JToolBarHelper::publishList('packages.publish'); + JToolBarHelper::unpublishList('packages.unpublish'); + JToolBarHelper::archiveList('packages.archive'); + + if ($this->canDo->get('core.admin')) + { + JToolBarHelper::checkin('packages.checkin'); + } + } + + // Add a batch button + if ($this->canBatch && $this->canCreate && $this->canEdit && $this->canState) + { + // Get the toolbar object instance + $bar = JToolBar::getInstance('toolbar'); + // set the batch button name + $title = JText::_('JTOOLBAR_BATCH'); + // Instantiate a new JLayoutFile instance and render the batch button + $layout = new JLayoutFile('joomla.toolbar.batch'); + // add the button to the page + $dhtml = $layout->render(array('title' => $title)); + $bar->appendButton('Custom', $dhtml, 'batch'); + } + + if ($this->state->get('filter.published') == -2 && ($this->canState && $this->canDelete)) + { + JToolbarHelper::deleteList('', 'packages.delete', 'JTOOLBAR_EMPTY_TRASH'); + } + elseif ($this->canState && $this->canDelete) + { + JToolbarHelper::trash('packages.trash'); + } + } + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('packages'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + // add the options comp button + if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + + // Only load published batch if state and batch is allowed + if ($this->canState && $this->canBatch) + { + JHtmlBatch_::addListSelection( + JText::_('COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_STATE'), + 'batch[published]', + JHtml::_('select.options', JHtml::_('jgrid.publishedOptions', array('all' => false)), 'value', 'text', '', true) + ); + } + + // Only load access batch if create, edit and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + JHtmlBatch_::addListSelection( + JText::_('COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_ACCESS'), + 'batch[access]', + JHtml::_('select.options', JHtml::_('access.assetgroups'), 'value', 'text') + ); + } + + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Category Batch selection. + JHtmlBatch_::addListSelection( + JText::_('COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_CATEGORY'), + 'batch[category]', + JHtml::_('select.options', JHtml::_('category.options', 'com_extensiondistributor'), 'value', 'text') + ); + } + + // Only load Free batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Free Selection + $this->freeOptions = JFormHelper::loadFieldType('packagesfilterfree')->options; + // We do some sanitation for Free filter + if (ExtensiondistributorHelper::checkArray($this->freeOptions) && + isset($this->freeOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->freeOptions[0]->value)) + { + unset($this->freeOptions[0]); + } + // Free Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_PACKAGE_FREE_LABEL').' -', + 'batch[free]', + JHtml::_('select.options', $this->freeOptions, 'value', 'text') + ); + } + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_('COM_EXTENSIONDISTRIBUTOR_PACKAGES')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/packages.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 50) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Returns an array of fields the table can be sorted by + * + * @return array Array containing the field name to sort by as the key and display text as value + */ + protected function getSortFields() + { + return array( + 'a.ordering' => JText::_('JGRID_HEADING_ORDERING'), + 'a.published' => JText::_('JSTATUS'), + 'a.name' => JText::_('COM_EXTENSIONDISTRIBUTOR_PACKAGE_NAME_LABEL'), + 'a.id' => JText::_('JGRID_HEADING_ID') + ); + } +} diff --git a/admin/views/release/submitbutton.js b/admin/views/release/submitbutton.js new file mode 100644 index 0000000..7e202ac --- /dev/null +++ b/admin/views/release/submitbutton.js @@ -0,0 +1,34 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage submitbutton.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +Joomla.submitbutton = function(task) +{ + if (task == ''){ + return false; + } else { + var action = task.split('.'); + if (action[1] == 'cancel' || action[1] == 'close' || document.formvalidator.isValid(document.getElementById("adminForm"))){ + Joomla.submitform(task, document.getElementById("adminForm")); + return true; + } else { + alert(Joomla.JText._('release, some values are not acceptable.','Some values are unacceptable')); + return false; + } + } +} \ No newline at end of file diff --git a/admin/views/release/tmpl/edit.php b/admin/views/release/tmpl/edit.php new file mode 100644 index 0000000..724d0f7 --- /dev/null +++ b/admin/views/release/tmpl/edit.php @@ -0,0 +1,211 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html'); +JHtml::_('behavior.formvalidator'); +JHtml::_('formbehavior.chosen', 'select'); +JHtml::_('behavior.keepalive'); +$componentParams = $this->params; // will be removed just use $this->params instead +?> + + + + diff --git a/admin/views/release/tmpl/index.html b/admin/views/release/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/release/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/release/view.html.php b/admin/views/release/view.html.php new file mode 100644 index 0000000..96918af --- /dev/null +++ b/admin/views/release/view.html.php @@ -0,0 +1,214 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Release Html View class + */ +class ExtensiondistributorViewRelease extends HtmlView +{ + /** + * display method of View + * @return void + */ + public function display($tpl = null) + { + // set params + $this->params = JComponentHelper::getParams('com_extensiondistributor'); + // Assign the variables + $this->form = $this->get('Form'); + $this->item = $this->get('Item'); + $this->script = $this->get('Script'); + $this->state = $this->get('State'); + // get action permissions + $this->canDo = ExtensiondistributorHelper::getActions('release', $this->item); + // get input + $jinput = JFactory::getApplication()->input; + $this->ref = $jinput->get('ref', 0, 'word'); + $this->refid = $jinput->get('refid', 0, 'int'); + $return = $jinput->get('return', null, 'base64'); + // set the referral string + $this->referral = ''; + if ($this->refid && $this->ref) + { + // return to the item that referred to this item + $this->referral = '&ref=' . (string)$this->ref . '&refid=' . (int)$this->refid; + } + elseif($this->ref) + { + // return to the list view that referred to this item + $this->referral = '&ref=' . (string)$this->ref; + } + // check return value + if (!is_null($return)) + { + // add the return value + $this->referral .= '&return=' . (string)$return; + } + + // Set the toolbar + $this->addToolBar(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JFactory::getApplication()->input->set('hidemainmenu', true); + $user = JFactory::getUser(); + $userId = $user->id; + $isNew = $this->item->id == 0; + + JToolbarHelper::title( JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_RELEASE_NEW' : 'COM_EXTENSIONDISTRIBUTOR_RELEASE_EDIT'), 'pencil-2 article-add'); + // Built the actions for new and existing records. + if (ExtensiondistributorHelper::checkString($this->referral)) + { + if ($this->canDo->get('core.create') && $isNew) + { + // We can create the record. + JToolBarHelper::save('release.save', 'JTOOLBAR_SAVE'); + } + elseif ($this->canDo->get('core.edit')) + { + // We can save the record. + JToolBarHelper::save('release.save', 'JTOOLBAR_SAVE'); + } + if ($isNew) + { + // Do not creat but cancel. + JToolBarHelper::cancel('release.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + // We can close it. + JToolBarHelper::cancel('release.cancel', 'JTOOLBAR_CLOSE'); + } + } + else + { + if ($isNew) + { + // For new records, check the create permission. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::apply('release.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('release.save', 'JTOOLBAR_SAVE'); + JToolBarHelper::custom('release.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + }; + JToolBarHelper::cancel('release.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + if ($this->canDo->get('core.edit')) + { + // We can save the new record + JToolBarHelper::apply('release.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('release.save', 'JTOOLBAR_SAVE'); + // We can save this record, but check the create permission to see + // if we can return to make a new one. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('release.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + } + } + $canVersion = ($this->canDo->get('core.version') && $this->canDo->get('release.version')); + if ($this->state->params->get('save_history', 1) && $this->canDo->get('core.edit') && $canVersion) + { + JToolbarHelper::versions('com_extensiondistributor.release', $this->item->id); + } + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('release.save2copy', 'save-copy.png', 'save-copy_f2.png', 'JTOOLBAR_SAVE_AS_COPY', false); + } + JToolBarHelper::cancel('release.cancel', 'JTOOLBAR_CLOSE'); + } + } + JToolbarHelper::divider(); + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('release'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 30) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true, 30); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + $isNew = ($this->item->id < 1); + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_RELEASE_NEW' : 'COM_EXTENSIONDISTRIBUTOR_RELEASE_EDIT')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/release.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + $this->document->addScript(JURI::root() . $this->script, (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + $this->document->addScript(JURI::root() . "administrator/components/com_extensiondistributor/views/release/submitbutton.js", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + + $this->document->addStyleSheet("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/css/uikit.min.css"); + $this->document->addScript("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit.min.js"); + $this->document->addScript("https://cdn.jsdelivr.net/npm/uikit@3.2.3/dist/js/uikit-icons.min.js"); + + +// Do not delete this comment. It allows JCB compiler to create the layout +// echo JLayoutHelper::render('statewidget', array($item->published,null,$return)); + JText::script('view not acceptable. Error'); + } +} diff --git a/admin/views/releases/index.html b/admin/views/releases/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/releases/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/releases/tmpl/default.php b/admin/views/releases/tmpl/default.php new file mode 100644 index 0000000..ca2ced1 --- /dev/null +++ b/admin/views/releases/tmpl/default.php @@ -0,0 +1,77 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::_('behavior.multiselect'); +JHtml::_('dropdown.init'); +JHtml::_('formbehavior.chosen', '.multipleExtension', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_EXTENSION') . ' -')); +JHtml::_('formbehavior.chosen', '.multiplePackagekey', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_PACKAGE') . ' -')); +JHtml::_('formbehavior.chosen', '.multipleReleasesfilterreltype', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_RELEASE_TYPE') . ' -')); +JHtml::_('formbehavior.chosen', '.multipleReleasesfilterstability', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_STABILITY_LEVEL') . ' -')); +JHtml::_('formbehavior.chosen', 'select'); +if ($this->saveOrder) +{ + $saveOrderingUrl = 'index.php?option=com_extensiondistributor&task=releases.saveOrderAjax&tmpl=component'; + JHtml::_('sortablelist.sortable', 'releaseList', 'adminForm', strtolower($this->listDirn), $saveOrderingUrl); +} +?> +
    +sidebar)): ?> +
    + sidebar; ?> +
    +
    + +
    + + $this)); +?> +items)): ?> +
    + +
    + + + loadTemplate('head');?> + loadTemplate('foot');?> + loadTemplate('body');?> +
    + + canCreate && $this->canEdit) : ?> + JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASES_BATCH_OPTIONS'), + 'footer' => $this->loadTemplate('batch_footer') + ), + $this->loadTemplate('batch_body') + ); ?> + + +
    + + + + diff --git a/admin/views/releases/tmpl/default_batch_body.php b/admin/views/releases/tmpl/default_batch_body.php new file mode 100644 index 0000000..44f070a --- /dev/null +++ b/admin/views/releases/tmpl/default_batch_body.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + +

    +batchDisplay; ?> \ No newline at end of file diff --git a/admin/views/releases/tmpl/default_batch_footer.php b/admin/views/releases/tmpl/default_batch_footer.php new file mode 100644 index 0000000..f824345 --- /dev/null +++ b/admin/views/releases/tmpl/default_batch_footer.php @@ -0,0 +1,32 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + + + \ No newline at end of file diff --git a/admin/views/releases/tmpl/default_body.php b/admin/views/releases/tmpl/default_body.php new file mode 100644 index 0000000..45a56fe --- /dev/null +++ b/admin/views/releases/tmpl/default_body.php @@ -0,0 +1,146 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +$edit = "index.php?option=com_extensiondistributor&view=releases&task=release.edit"; + +?> +items as $i => $item): ?> + user->authorise('core.manage', 'com_checkin') || $item->checked_out == $this->user->id || $item->checked_out == 0; + $userChkOut = JFactory::getUser($item->checked_out); + $canDo = ExtensiondistributorHelper::getActions('release',$item,'releases'); + ?> + + + get('core.edit.state')): ?> + saveOrder) + { + $iconClass = ' inactive tip-top" hasTooltip" title="' . JHtml::tooltipText('JORDERINGDISABLED'); + } + ?> + + + + saveOrder) : ?> + + + + ⋮ + + + + get('core.edit')): ?> + checked_out) : ?> + + id); ?> + + □ + + + id); ?> + + + □ + + + +
    + +
    + + + escape($item->extension_name); ?> + + + escape($item->package_name); ?> + + +
    + get('core.edit')): ?> + escape($item->version_number); ?> + checked_out): ?> + name, $item->checked_out_time, 'releases.', $canCheckin); ?> + + + escape($item->version_number); ?> + +id,'id','stability'); +$stability = $lists['stability']; +?> + + + +
    + + +
    file == -1) : ?> +
    N/A
    + +input->get('view')); ?> + + Download + + + +
    + + +
    release_date,'DATE_FORMAT_LC3'); ?> + +
    + + + reltype); ?> + + +
    description; ?> +
    + + + get('core.edit.state')) : ?> + checked_out) : ?> + + published, $i, 'releases.', true, 'cb'); ?> + + published, $i, 'releases.', false, 'cb'); ?> + + + published, $i, 'releases.', true, 'cb'); ?> + + + published, $i, 'releases.', false, 'cb'); ?> + + + + id; ?> + + + \ No newline at end of file diff --git a/admin/views/releases/tmpl/default_foot.php b/admin/views/releases/tmpl/default_foot.php new file mode 100644 index 0000000..4079f11 --- /dev/null +++ b/admin/views/releases/tmpl/default_foot.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + pagination->getListFooter(); ?> + \ No newline at end of file diff --git a/admin/views/releases/tmpl/default_head.php b/admin/views/releases/tmpl/default_head.php new file mode 100644 index 0000000..6b82b1a --- /dev/null +++ b/admin/views/releases/tmpl/default_head.php @@ -0,0 +1,77 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + canEdit&& $this->canState): ?> + + listDirn, $this->listOrder, null, 'asc', 'JGRID_HEADING_ORDERING', 'icon-menu-2'); ?> + + + + + + + ▾ + + + ■ + + + + + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + + + canState): ?> + + listDirn, $this->listOrder); ?> + + + + + + + + listDirn, $this->listOrder); ?> + + \ No newline at end of file diff --git a/admin/views/releases/tmpl/default_toolbar.php b/admin/views/releases/tmpl/default_toolbar.php new file mode 100644 index 0000000..5419262 --- /dev/null +++ b/admin/views/releases/tmpl/default_toolbar.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> +
    + +
    + + +
    +
    + + pagination->getLimitBox(); ?> +
    +
    + + +
    +
    + + +
    +
    +
    \ No newline at end of file diff --git a/admin/views/releases/tmpl/index.html b/admin/views/releases/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/releases/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/releases/view.html.php b/admin/views/releases/view.html.php new file mode 100644 index 0000000..e20e66c --- /dev/null +++ b/admin/views/releases/view.html.php @@ -0,0 +1,304 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Releases + */ +class ExtensiondistributorViewReleases extends HtmlView +{ + /** + * Releases view display method + * @return void + */ + function display($tpl = null) + { + if ($this->getLayout() !== 'modal') + { + // Include helper submenu + ExtensiondistributorHelper::addSubmenu('releases'); + } + + // Assign data to the view + $this->items = $this->get('Items'); + $this->pagination = $this->get('Pagination'); + $this->state = $this->get('State'); + $this->user = JFactory::getUser(); + // Load the filter form from xml. + $this->filterForm = $this->get('FilterForm'); + // Load the active filters. + $this->activeFilters = $this->get('ActiveFilters'); + // Add the list ordering clause. + $this->listOrder = $this->escape($this->state->get('list.ordering', 'a.id')); + $this->listDirn = $this->escape($this->state->get('list.direction', 'DESC')); + $this->saveOrder = $this->listOrder == 'a.ordering'; + // set the return here value + $this->return_here = urlencode(base64_encode((string) JUri::getInstance())); + // get global action permissions + $this->canDo = ExtensiondistributorHelper::getActions('release'); + $this->canEdit = $this->canDo->get('core.edit'); + $this->canState = $this->canDo->get('core.edit.state'); + $this->canCreate = $this->canDo->get('core.create'); + $this->canDelete = $this->canDo->get('core.delete'); + $this->canBatch = $this->canDo->get('core.batch'); + + // We don't need toolbar in the modal window. + if ($this->getLayout() !== 'modal') + { + $this->addToolbar(); + $this->sidebar = JHtmlSidebar::render(); + // load the batch html + if ($this->canCreate && $this->canEdit && $this->canState) + { + $this->batchDisplay = JHtmlBatch_::render(); + } + } + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JToolBarHelper::title(JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASES'), 'list-2'); + JHtmlSidebar::setAction('index.php?option=com_extensiondistributor&view=releases'); + JFormHelper::addFieldPath(JPATH_COMPONENT . '/models/fields'); + + if ($this->canCreate) + { + JToolBarHelper::addNew('release.add'); + } + + // Only load if there are items + if (ExtensiondistributorHelper::checkArray($this->items)) + { + if ($this->canEdit) + { + JToolBarHelper::editList('release.edit'); + } + + if ($this->canState) + { + JToolBarHelper::publishList('releases.publish'); + JToolBarHelper::unpublishList('releases.unpublish'); + JToolBarHelper::archiveList('releases.archive'); + + if ($this->canDo->get('core.admin')) + { + JToolBarHelper::checkin('releases.checkin'); + } + } + + // Add a batch button + if ($this->canBatch && $this->canCreate && $this->canEdit && $this->canState) + { + // Get the toolbar object instance + $bar = JToolBar::getInstance('toolbar'); + // set the batch button name + $title = JText::_('JTOOLBAR_BATCH'); + // Instantiate a new JLayoutFile instance and render the batch button + $layout = new JLayoutFile('joomla.toolbar.batch'); + // add the button to the page + $dhtml = $layout->render(array('title' => $title)); + $bar->appendButton('Custom', $dhtml, 'batch'); + } + + if ($this->state->get('filter.published') == -2 && ($this->canState && $this->canDelete)) + { + JToolbarHelper::deleteList('', 'releases.delete', 'JTOOLBAR_EMPTY_TRASH'); + } + elseif ($this->canState && $this->canDelete) + { + JToolbarHelper::trash('releases.trash'); + } + } + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('releases'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + // add the options comp button + if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + + // Only load published batch if state and batch is allowed + if ($this->canState && $this->canBatch) + { + JHtmlBatch_::addListSelection( + JText::_('COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_STATE'), + 'batch[published]', + JHtml::_('select.options', JHtml::_('jgrid.publishedOptions', array('all' => false)), 'value', 'text', '', true) + ); + } + + // Only load Extension Name batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Extension Name Selection + $this->extensionNameOptions = JFormHelper::loadFieldType('Extension')->options; + // We do some sanitation for Extension Name filter + if (ExtensiondistributorHelper::checkArray($this->extensionNameOptions) && + isset($this->extensionNameOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->extensionNameOptions[0]->value)) + { + unset($this->extensionNameOptions[0]); + } + // Extension Name Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_EXTENSION_LABEL').' -', + 'batch[extension]', + JHtml::_('select.options', $this->extensionNameOptions, 'value', 'text') + ); + } + + // Only load Package Name batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Package Name Selection + $this->packageNameOptions = JFormHelper::loadFieldType('Packagekey')->options; + // We do some sanitation for Package Name filter + if (ExtensiondistributorHelper::checkArray($this->packageNameOptions) && + isset($this->packageNameOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->packageNameOptions[0]->value)) + { + unset($this->packageNameOptions[0]); + } + // Package Name Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_PACKAGE_LABEL').' -', + 'batch[package]', + JHtml::_('select.options', $this->packageNameOptions, 'value', 'text') + ); + } + + // Only load Reltype batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Reltype Selection + $this->reltypeOptions = JFormHelper::loadFieldType('releasesfilterreltype')->options; + // We do some sanitation for Reltype filter + if (ExtensiondistributorHelper::checkArray($this->reltypeOptions) && + isset($this->reltypeOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->reltypeOptions[0]->value)) + { + unset($this->reltypeOptions[0]); + } + // Reltype Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_RELTYPE_LABEL').' -', + 'batch[reltype]', + JHtml::_('select.options', $this->reltypeOptions, 'value', 'text') + ); + } + + // Only load Stability batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Stability Selection + $this->stabilityOptions = JFormHelper::loadFieldType('releasesfilterstability')->options; + // We do some sanitation for Stability filter + if (ExtensiondistributorHelper::checkArray($this->stabilityOptions) && + isset($this->stabilityOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->stabilityOptions[0]->value)) + { + unset($this->stabilityOptions[0]); + } + // Stability Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_STABILITY_LABEL').' -', + 'batch[stability]', + JHtml::_('select.options', $this->stabilityOptions, 'value', 'text') + ); + } + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASES')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/releases.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 50) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Returns an array of fields the table can be sorted by + * + * @return array Array containing the field name to sort by as the key and display text as value + */ + protected function getSortFields() + { + return array( + 'a.ordering' => JText::_('JGRID_HEADING_ORDERING'), + 'a.published' => JText::_('JSTATUS'), + 'g.name' => JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_EXTENSION_LABEL'), + 'h.name' => JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_PACKAGE_LABEL'), + 'a.version_number' => JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_VERSION_NUMBER_LABEL'), + 'a.release_date' => JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_RELEASE_DATE_LABEL'), + 'a.reltype' => JText::_('COM_EXTENSIONDISTRIBUTOR_RELEASE_RELTYPE_LABEL'), + 'a.id' => JText::_('JGRID_HEADING_ID') + ); + } +} diff --git a/admin/views/statistics/index.html b/admin/views/statistics/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/statistics/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/statistics/tmpl/default.php b/admin/views/statistics/tmpl/default.php new file mode 100644 index 0000000..ec93199 --- /dev/null +++ b/admin/views/statistics/tmpl/default.php @@ -0,0 +1,67 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html'); +JHtml::_('behavior.formvalidator'); +JHtml::_('formbehavior.chosen', 'select'); +JHtml::_('behavior.keepalive'); + +$this->app->input->set('hidemainmenu', false); + +?> +canDo->get('statistics.access')): ?> + +item->id)) ? '&id='. (int) $this->item->id : ''; ?> +
    + +sidebar)): ?> +
    + sidebar; ?> +
    +
    + +
    + +

    Coming soon...

    + + + + + + +

    + + diff --git a/admin/views/statistics/tmpl/index.html b/admin/views/statistics/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/statistics/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/statistics/view.html.php b/admin/views/statistics/view.html.php new file mode 100644 index 0000000..ae2fd7e --- /dev/null +++ b/admin/views/statistics/view.html.php @@ -0,0 +1,137 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Statistics + */ +class ExtensiondistributorViewStatistics extends HtmlView +{ + // Overwriting JView display method + function display($tpl = null) + { + // get component params + $this->params = JComponentHelper::getParams('com_extensiondistributor'); + // get the application + $this->app = JFactory::getApplication(); + // get the user object + $this->user = JFactory::getUser(); + // get global action permissions + $this->canDo = ExtensiondistributorHelper::getActions('statistics'); + // Initialise variables. + $this->item = $this->get('Item'); + // We don't need sidebar in the modal window. + if ($this->getLayout() !== 'modal') + { + //$this->app->input->set('hidemainmenu', false); + ExtensiondistributorHelper::addSubmenu('statistics'); + $this->sidebar = JHtmlSidebar::render(); + } + + + // We don't need toolbar in the modal window. + if ($this->getLayout() !== 'modal') + { + // add the tool bar + $this->addToolBar(); + } + + // set the document + $this->setDocument(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode(PHP_EOL, $errors), 500); + } + + parent::display($tpl); + } + + /** + * Prepares the document + */ + protected function setDocument() + { + + // always make sure jquery is loaded. + JHtml::_('jquery.framework'); + // Load the header checker class. + require_once( JPATH_COMPONENT_ADMINISTRATOR.'/helpers/headercheck.php' ); + // Initialize the header checker. + $HeaderCheck = new extensiondistributorHeaderCheck; + // add the document default css file + $this->document->addStyleSheet(JURI::root(true) .'/administrator/components/com_extensiondistributor/assets/css/statistics.css', (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + // hide the main menu + $this->app->input->set('hidemainmenu', true); + // set the title + if (isset($this->item->name) && $this->item->name) + { + $title = $this->item->name; + } + // Check for empty title and add view name if param is set + if (empty($title)) + { + $title = JText::_('COM_EXTENSIONDISTRIBUTOR_STATISTICS'); + } + // add title to the page + JToolbarHelper::title($title,'bars'); + // add cpanel button + JToolBarHelper::custom('statistics.dashboard', 'grid-2', '', 'COM_EXTENSIONDISTRIBUTOR_DASH', false); + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('statistics'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + // add the options comp button + if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } +} +?> diff --git a/admin/views/subscription/submitbutton.js b/admin/views/subscription/submitbutton.js new file mode 100644 index 0000000..59c4056 --- /dev/null +++ b/admin/views/subscription/submitbutton.js @@ -0,0 +1,34 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage submitbutton.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +Joomla.submitbutton = function(task) +{ + if (task == ''){ + return false; + } else { + var action = task.split('.'); + if (action[1] == 'cancel' || action[1] == 'close' || document.formvalidator.isValid(document.getElementById("adminForm"))){ + Joomla.submitform(task, document.getElementById("adminForm")); + return true; + } else { + alert(Joomla.JText._('subscription, some values are not acceptable.','Some values are unacceptable')); + return false; + } + } +} \ No newline at end of file diff --git a/admin/views/subscription/tmpl/edit.php b/admin/views/subscription/tmpl/edit.php new file mode 100644 index 0000000..8d5d927 --- /dev/null +++ b/admin/views/subscription/tmpl/edit.php @@ -0,0 +1,122 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html'); +JHtml::_('behavior.formvalidator'); +JHtml::_('formbehavior.chosen', 'select'); +JHtml::_('behavior.keepalive'); +$componentParams = $this->params; // will be removed just use $this->params instead +?> + + + + diff --git a/admin/views/subscription/tmpl/index.html b/admin/views/subscription/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/subscription/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/subscription/view.html.php b/admin/views/subscription/view.html.php new file mode 100644 index 0000000..ad8815d --- /dev/null +++ b/admin/views/subscription/view.html.php @@ -0,0 +1,201 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Subscription Html View class + */ +class ExtensiondistributorViewSubscription extends HtmlView +{ + /** + * display method of View + * @return void + */ + public function display($tpl = null) + { + // set params + $this->params = JComponentHelper::getParams('com_extensiondistributor'); + // Assign the variables + $this->form = $this->get('Form'); + $this->item = $this->get('Item'); + $this->script = $this->get('Script'); + $this->state = $this->get('State'); + // get action permissions + $this->canDo = ExtensiondistributorHelper::getActions('subscription', $this->item); + // get input + $jinput = JFactory::getApplication()->input; + $this->ref = $jinput->get('ref', 0, 'word'); + $this->refid = $jinput->get('refid', 0, 'int'); + $return = $jinput->get('return', null, 'base64'); + // set the referral string + $this->referral = ''; + if ($this->refid && $this->ref) + { + // return to the item that referred to this item + $this->referral = '&ref=' . (string)$this->ref . '&refid=' . (int)$this->refid; + } + elseif($this->ref) + { + // return to the list view that referred to this item + $this->referral = '&ref=' . (string)$this->ref; + } + // check return value + if (!is_null($return)) + { + // add the return value + $this->referral .= '&return=' . (string)$return; + } + + // Set the toolbar + $this->addToolBar(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JFactory::getApplication()->input->set('hidemainmenu', true); + $user = JFactory::getUser(); + $userId = $user->id; + $isNew = $this->item->id == 0; + + JToolbarHelper::title( JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_NEW' : 'COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_EDIT'), 'pencil-2 article-add'); + // Built the actions for new and existing records. + if (ExtensiondistributorHelper::checkString($this->referral)) + { + if ($this->canDo->get('core.create') && $isNew) + { + // We can create the record. + JToolBarHelper::save('subscription.save', 'JTOOLBAR_SAVE'); + } + elseif ($this->canDo->get('core.edit')) + { + // We can save the record. + JToolBarHelper::save('subscription.save', 'JTOOLBAR_SAVE'); + } + if ($isNew) + { + // Do not creat but cancel. + JToolBarHelper::cancel('subscription.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + // We can close it. + JToolBarHelper::cancel('subscription.cancel', 'JTOOLBAR_CLOSE'); + } + } + else + { + if ($isNew) + { + // For new records, check the create permission. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::apply('subscription.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('subscription.save', 'JTOOLBAR_SAVE'); + JToolBarHelper::custom('subscription.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + }; + JToolBarHelper::cancel('subscription.cancel', 'JTOOLBAR_CANCEL'); + } + else + { + if ($this->canDo->get('core.edit')) + { + // We can save the new record + JToolBarHelper::apply('subscription.apply', 'JTOOLBAR_APPLY'); + JToolBarHelper::save('subscription.save', 'JTOOLBAR_SAVE'); + // We can save this record, but check the create permission to see + // if we can return to make a new one. + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('subscription.save2new', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); + } + } + if ($this->canDo->get('core.create')) + { + JToolBarHelper::custom('subscription.save2copy', 'save-copy.png', 'save-copy_f2.png', 'JTOOLBAR_SAVE_AS_COPY', false); + } + JToolBarHelper::cancel('subscription.cancel', 'JTOOLBAR_CLOSE'); + } + } + JToolbarHelper::divider(); + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('subscription'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 30) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true, 30); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + $isNew = ($this->item->id < 1); + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_($isNew ? 'COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_NEW' : 'COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_EDIT')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/subscription.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + $this->document->addScript(JURI::root() . $this->script, (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + $this->document->addScript(JURI::root() . "administrator/components/com_extensiondistributor/views/subscription/submitbutton.js", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/javascript'); + JText::script('view not acceptable. Error'); + } +} diff --git a/admin/views/subscriptions/index.html b/admin/views/subscriptions/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/subscriptions/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/subscriptions/tmpl/default.php b/admin/views/subscriptions/tmpl/default.php new file mode 100644 index 0000000..b54a166 --- /dev/null +++ b/admin/views/subscriptions/tmpl/default.php @@ -0,0 +1,93 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +JHtml::_('behavior.multiselect'); +JHtml::_('dropdown.init'); +JHtml::_('formbehavior.chosen', '.multipleSubscriptionsfilteruser', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_JOOMLA_USER') . ' -')); +JHtml::_('formbehavior.chosen', '.multiplePackagekeys', null, array('placeholder_text_multiple' => '- ' . JText::_('COM_EXTENSIONDISTRIBUTOR_FILTER_SELECT_PACKAGE') . ' -')); +JHtml::_('formbehavior.chosen', 'select'); +if ($this->saveOrder) +{ + $saveOrderingUrl = 'index.php?option=com_extensiondistributor&task=subscriptions.saveOrderAjax&tmpl=component'; + JHtml::_('sortablelist.sortable', 'subscriptionList', 'adminForm', strtolower($this->listDirn), $saveOrderingUrl); +} +?> +
    +sidebar)): ?> +
    + sidebar; ?> +
    +
    + +
    + + $this)); +?> +items)): ?> +
    + +
    + + + loadTemplate('head');?> + loadTemplate('foot');?> + loadTemplate('body');?> +
    + + canCreate && $this->canEdit) : ?> + JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS_BATCH_OPTIONS'), + 'footer' => $this->loadTemplate('batch_footer') + ), + $this->loadTemplate('batch_body') + ); ?> + + +
    + + + + + diff --git a/admin/views/subscriptions/tmpl/default_batch_body.php b/admin/views/subscriptions/tmpl/default_batch_body.php new file mode 100644 index 0000000..0b5d0c7 --- /dev/null +++ b/admin/views/subscriptions/tmpl/default_batch_body.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + +

    +batchDisplay; ?> \ No newline at end of file diff --git a/admin/views/subscriptions/tmpl/default_batch_footer.php b/admin/views/subscriptions/tmpl/default_batch_footer.php new file mode 100644 index 0000000..8631ae3 --- /dev/null +++ b/admin/views/subscriptions/tmpl/default_batch_footer.php @@ -0,0 +1,32 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + + + \ No newline at end of file diff --git a/admin/views/subscriptions/tmpl/default_body.php b/admin/views/subscriptions/tmpl/default_body.php new file mode 100644 index 0000000..0ce26af --- /dev/null +++ b/admin/views/subscriptions/tmpl/default_body.php @@ -0,0 +1,117 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +$edit = "index.php?option=com_extensiondistributor&view=subscriptions&task=subscription.edit"; + +?> +items as $i => $item): ?> + user->authorise('core.manage', 'com_checkin') || $item->checked_out == $this->user->id || $item->checked_out == 0; + $userChkOut = JFactory::getUser($item->checked_out); + $canDo = ExtensiondistributorHelper::getActions('subscription',$item,'subscriptions'); + ?> + + + get('core.edit.state')): ?> + saveOrder) + { + $iconClass = ' inactive tip-top" hasTooltip" title="' . JHtml::tooltipText('JORDERINGDISABLED'); + } + ?> + + + + saveOrder) : ?> + + + + ⋮ + + + + get('core.edit')): ?> + checked_out) : ?> + + id); ?> + + □ + + + id); ?> + + + □ + + + +
    get('core.edit')): ?> + Edit subscription + checked_out): ?> + name, $item->checked_out_time, 'subscriptions.', $canCheckin); ?> + + + Edit subscription + + +
    + + +
    user)->name; ?> +
    + escape($item->package_name); ?> +
    + + +
    date_start, 'DATE_FORMAT_LC3') ?> +
    + + +
    date_expire, 'DATE_FORMAT_LC3') ?> +
    + + + active); ?> + + + get('core.edit.state')) : ?> + checked_out) : ?> + + published, $i, 'subscriptions.', true, 'cb'); ?> + + published, $i, 'subscriptions.', false, 'cb'); ?> + + + published, $i, 'subscriptions.', true, 'cb'); ?> + + + published, $i, 'subscriptions.', false, 'cb'); ?> + + + + id; ?> + + + \ No newline at end of file diff --git a/admin/views/subscriptions/tmpl/default_foot.php b/admin/views/subscriptions/tmpl/default_foot.php new file mode 100644 index 0000000..76ef22f --- /dev/null +++ b/admin/views/subscriptions/tmpl/default_foot.php @@ -0,0 +1,27 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + pagination->getListFooter(); ?> + \ No newline at end of file diff --git a/admin/views/subscriptions/tmpl/default_head.php b/admin/views/subscriptions/tmpl/default_head.php new file mode 100644 index 0000000..b4e61ea --- /dev/null +++ b/admin/views/subscriptions/tmpl/default_head.php @@ -0,0 +1,68 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> + + canEdit&& $this->canState): ?> + + listDirn, $this->listOrder, null, 'asc', 'JGRID_HEADING_ORDERING', 'icon-menu-2'); ?> + + + + + + + ▾ + + + ■ + + + + + + + + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + + listDirn, $this->listOrder); ?> + + canState): ?> + + listDirn, $this->listOrder); ?> + + + + + + + + listDirn, $this->listOrder); ?> + + \ No newline at end of file diff --git a/admin/views/subscriptions/tmpl/default_toolbar.php b/admin/views/subscriptions/tmpl/default_toolbar.php new file mode 100644 index 0000000..49ac40a --- /dev/null +++ b/admin/views/subscriptions/tmpl/default_toolbar.php @@ -0,0 +1,54 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +?> +
    + +
    + + +
    +
    + + pagination->getLimitBox(); ?> +
    +
    + + +
    +
    + + +
    +
    +
    \ No newline at end of file diff --git a/admin/views/subscriptions/tmpl/index.html b/admin/views/subscriptions/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/admin/views/subscriptions/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/admin/views/subscriptions/view.html.php b/admin/views/subscriptions/view.html.php new file mode 100644 index 0000000..bd019ed --- /dev/null +++ b/admin/views/subscriptions/view.html.php @@ -0,0 +1,282 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Subscriptions + */ +class ExtensiondistributorViewSubscriptions extends HtmlView +{ + /** + * Subscriptions view display method + * @return void + */ + function display($tpl = null) + { + if ($this->getLayout() !== 'modal') + { + // Include helper submenu + ExtensiondistributorHelper::addSubmenu('subscriptions'); + } + + // Assign data to the view + $this->items = $this->get('Items'); + $this->pagination = $this->get('Pagination'); + $this->state = $this->get('State'); + $this->user = JFactory::getUser(); + // Load the filter form from xml. + $this->filterForm = $this->get('FilterForm'); + // Load the active filters. + $this->activeFilters = $this->get('ActiveFilters'); + // Add the list ordering clause. + $this->listOrder = $this->escape($this->state->get('list.ordering', 'a.id')); + $this->listDirn = $this->escape($this->state->get('list.direction', 'DESC')); + $this->saveOrder = $this->listOrder == 'a.ordering'; + // set the return here value + $this->return_here = urlencode(base64_encode((string) JUri::getInstance())); + // get global action permissions + $this->canDo = ExtensiondistributorHelper::getActions('subscription'); + $this->canEdit = $this->canDo->get('core.edit'); + $this->canState = $this->canDo->get('core.edit.state'); + $this->canCreate = $this->canDo->get('core.create'); + $this->canDelete = $this->canDo->get('core.delete'); + $this->canBatch = $this->canDo->get('core.batch'); + + // We don't need toolbar in the modal window. + if ($this->getLayout() !== 'modal') + { + $this->addToolbar(); + $this->sidebar = JHtmlSidebar::render(); + // load the batch html + if ($this->canCreate && $this->canEdit && $this->canState) + { + $this->batchDisplay = JHtmlBatch_::render(); + } + } + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode("\n", $errors), 500); + } + + // Display the template + parent::display($tpl); + + // Set the document + $this->setDocument(); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + JToolBarHelper::title(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS'), 'credit'); + JHtmlSidebar::setAction('index.php?option=com_extensiondistributor&view=subscriptions'); + JFormHelper::addFieldPath(JPATH_COMPONENT . '/models/fields'); + + if ($this->canCreate) + { + JToolBarHelper::addNew('subscription.add'); + } + + // Only load if there are items + if (ExtensiondistributorHelper::checkArray($this->items)) + { + if ($this->canEdit) + { + JToolBarHelper::editList('subscription.edit'); + } + + if ($this->canState) + { + JToolBarHelper::publishList('subscriptions.publish'); + JToolBarHelper::unpublishList('subscriptions.unpublish'); + JToolBarHelper::archiveList('subscriptions.archive'); + + if ($this->canDo->get('core.admin')) + { + JToolBarHelper::checkin('subscriptions.checkin'); + } + } + + // Add a batch button + if ($this->canBatch && $this->canCreate && $this->canEdit && $this->canState) + { + // Get the toolbar object instance + $bar = JToolBar::getInstance('toolbar'); + // set the batch button name + $title = JText::_('JTOOLBAR_BATCH'); + // Instantiate a new JLayoutFile instance and render the batch button + $layout = new JLayoutFile('joomla.toolbar.batch'); + // add the button to the page + $dhtml = $layout->render(array('title' => $title)); + $bar->appendButton('Custom', $dhtml, 'batch'); + } + + if ($this->state->get('filter.published') == -2 && ($this->canState && $this->canDelete)) + { + JToolbarHelper::deleteList('', 'subscriptions.delete', 'JTOOLBAR_EMPTY_TRASH'); + } + elseif ($this->canState && $this->canDelete) + { + JToolbarHelper::trash('subscriptions.trash'); + } + } + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('subscriptions'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + + // add the options comp button + if ($this->canDo->get('core.admin') || $this->canDo->get('core.options')) + { + JToolBarHelper::preferences('com_extensiondistributor'); + } + + // Only load published batch if state and batch is allowed + if ($this->canState && $this->canBatch) + { + JHtmlBatch_::addListSelection( + JText::_('COM_EXTENSIONDISTRIBUTOR_KEEP_ORIGINAL_STATE'), + 'batch[published]', + JHtml::_('select.options', JHtml::_('jgrid.publishedOptions', array('all' => false)), 'value', 'text', '', true) + ); + } + + // Only load User batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set User Selection + $this->userOptions = JFormHelper::loadFieldType('subscriptionsfilteruser')->options; + // We do some sanitation for User filter + if (ExtensiondistributorHelper::checkArray($this->userOptions) && + isset($this->userOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->userOptions[0]->value)) + { + unset($this->userOptions[0]); + } + // User Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_USER_LABEL').' -', + 'batch[user]', + JHtml::_('select.options', $this->userOptions, 'value', 'text') + ); + } + + // Only load Package Name batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Package Name Selection + $this->packageNameOptions = JFormHelper::loadFieldType('Packagekeys')->options; + // We do some sanitation for Package Name filter + if (ExtensiondistributorHelper::checkArray($this->packageNameOptions) && + isset($this->packageNameOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->packageNameOptions[0]->value)) + { + unset($this->packageNameOptions[0]); + } + // Package Name Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_PACKAGE_LABEL').' -', + 'batch[package]', + JHtml::_('select.options', $this->packageNameOptions, 'value', 'text') + ); + } + + // Only load Active batch if create, edit, and batch is allowed + if ($this->canBatch && $this->canCreate && $this->canEdit) + { + // Set Active Selection + $this->activeOptions = JFormHelper::loadFieldType('subscriptionsfilteractive')->options; + // We do some sanitation for Active filter + if (ExtensiondistributorHelper::checkArray($this->activeOptions) && + isset($this->activeOptions[0]->value) && + !ExtensiondistributorHelper::checkString($this->activeOptions[0]->value)) + { + unset($this->activeOptions[0]); + } + // Active Batch Selection + JHtmlBatch_::addListSelection( + '- Keep Original '.JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_ACTIVE_LABEL').' -', + 'batch[active]', + JHtml::_('select.options', $this->activeOptions, 'value', 'text') + ); + } + } + + /** + * Method to set up the document properties + * + * @return void + */ + protected function setDocument() + { + if (!isset($this->document)) + { + $this->document = JFactory::getDocument(); + } + $this->document->setTitle(JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTIONS')); + $this->document->addStyleSheet(JURI::root() . "administrator/components/com_extensiondistributor/assets/css/subscriptions.css", (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var) + { + if(strlen($var) > 50) + { + // use the helper htmlEscape method instead and shorten the string + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, true); + } + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset); + } + + /** + * Returns an array of fields the table can be sorted by + * + * @return array Array containing the field name to sort by as the key and display text as value + */ + protected function getSortFields() + { + return array( + 'a.ordering' => JText::_('JGRID_HEADING_ORDERING'), + 'a.published' => JText::_('JSTATUS'), + 'a.date_start' => JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_DATE_START_LABEL'), + 'a.date_expire' => JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_DATE_EXPIRE_LABEL'), + 'a.active' => JText::_('COM_EXTENSIONDISTRIBUTOR_SUBSCRIPTION_ACTIVE_LABEL'), + 'a.id' => JText::_('JGRID_HEADING_ID') + ); + } +} diff --git a/extensiondistributor.xml b/extensiondistributor.xml new file mode 100644 index 0000000..10e9cfb --- /dev/null +++ b/extensiondistributor.xml @@ -0,0 +1,107 @@ + + + COM_EXTENSIONDISTRIBUTOR + 2nd June, 2022 + Emmanuel Danan + emmanuel.danan@agerix.fr + https://agerix.fr + Copyright (C) 2021. All Rights Reserved. + GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + 1.0.7 + Extension Distributor (v.1.0.7) +
    +

    +

    Created by Emmanuel Danan
    Development started 12th December, 2020

    + ]]>
    + + + + + sql/install.mysql.utf8.sql + + + + + + + sql/uninstall.mysql.utf8.sql + + + + + script.php + + + index.html + js + css + images + uikit-v3 + footable-v3 + + + + + + sql/updates/mysql/ + + + + + controller.php + index.html + router.php + extensiondistributor.php + router.php + assets + helpers + controllers + models + views + layouts + + + + language/en-GB/en-GB.com_extensiondistributor.ini + language/en-GB/en-GB.com_extensiondistributor.sys.ini + language/fr-FR/fr-FR.com_extensiondistributor.ini + language/fr-FR/fr-FR.com_extensiondistributor.sys.ini + + + + COM_EXTENSIONDISTRIBUTOR_MENU + + + COM_EXTENSIONDISTRIBUTOR_MENU_PACKAGES + COM_EXTENSIONDISTRIBUTOR_MENU_EXTENSIONS + COM_EXTENSIONDISTRIBUTOR_MENU_SUBSCRIPTIONS + COM_EXTENSIONDISTRIBUTOR_MENU_KEYS + COM_EXTENSIONDISTRIBUTOR_MENU_IMPORT + COM_EXTENSIONDISTRIBUTOR_MENU_MAINTENANCE + COM_EXTENSIONDISTRIBUTOR_MENU_DOWNLOADS + COM_EXTENSIONDISTRIBUTOR_MENU_STATISTICS + + + access.xml + config.xml + controller.php + index.html + extensiondistributor.php + assets + controllers + helpers + models + sql + tables + views + layouts + + + + language/en-GB/en-GB.com_extensiondistributor.ini + language/en-GB/en-GB.com_extensiondistributor.sys.ini + + + +
    \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/css/index.html b/media/css/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/css/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/footable-v3/LICENSE-GPLv3 b/media/footable-v3/LICENSE-GPLv3 new file mode 100644 index 0000000..0daf0bb --- /dev/null +++ b/media/footable-v3/LICENSE-GPLv3 @@ -0,0 +1,14 @@ +Copyright (c) 2015 Steven Usher & Brad Vincent + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . \ No newline at end of file diff --git a/media/footable-v3/css/footable.bootstrap.css b/media/footable-v3/css/footable.bootstrap.css new file mode 100644 index 0000000..56b4fe3 --- /dev/null +++ b/media/footable-v3/css/footable.bootstrap.css @@ -0,0 +1,266 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.0.6 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +table.footable, +table.footable-details { + position: relative; + width: 100%; + border-spacing: 0; + border-collapse: collapse; +} +table.footable-details { + margin-bottom: 0; +} +table > tbody > tr > td > span.footable-toggle { + margin-right: 8px; + opacity: 0.3; +} +table > tbody > tr > td > span.footable-toggle.last-column { + margin-left: 8px; + float: right; +} +table.table-condensed > tbody > tr > td > span.footable-toggle { + margin-right: 5px; +} +table.footable-details > tbody > tr > th:nth-child(1) { + min-width: 40px; + width: 120px; +} +table.footable-details > tbody > tr > td:nth-child(2) { + word-break: break-all; +} + +table.footable-details > thead > tr:first-child > th, +table.footable-details > thead > tr:first-child > td, +table.footable-details > tbody > tr:first-child > th, +table.footable-details > tbody > tr:first-child > td, +table.footable-details > tfoot > tr:first-child > th, +table.footable-details > tfoot > tr:first-child > td { + border-top-width: 0; +} +table.footable-details.table-bordered > thead > tr:first-child > th, +table.footable-details.table-bordered > thead > tr:first-child > td, +table.footable-details.table-bordered > tbody > tr:first-child > th, +table.footable-details.table-bordered > tbody > tr:first-child > td, +table.footable-details.table-bordered > tfoot > tr:first-child > th, +table.footable-details.table-bordered > tfoot > tr:first-child > td { + border-top-width: 1px; +} + +div.footable-loader { + vertical-align: middle; + text-align: center; + height: 300px; + position: relative; +} +div.footable-loader > span.fooicon { + display: inline-block; + opacity: 0.3; + font-size: 30px; + line-height: 32px; + width: 32px; + height: 32px; + margin-top: -16px; + margin-left: -16px; + position: absolute; + top: 50%; + left: 50%; + -webkit-animation: fooicon-spin-r 2s infinite linear; + animation: fooicon-spin-r 2s infinite linear; +} +table.footable > tbody > tr.footable-empty > td { + vertical-align: middle; + text-align: center; + font-size: 30px; +} +table.footable > tbody > tr > td, +table.footable > tbody > tr > th { + display: none; +} +table.footable > tbody > tr.footable-empty > td, +table.footable > tbody > tr.footable-empty > th, +table.footable > tbody > tr.footable-detail-row > td, +table.footable > tbody > tr.footable-detail-row > th { + display: table-cell; +} +@-webkit-keyframes fooicon-spin-r { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +@keyframes fooicon-spin-r { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +.fooicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings' !important; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.fooicon:before, +.fooicon:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.fooicon-loader:before { + content: "\e030"; +} +.fooicon-plus:before { + content: "\2b"; +} +.fooicon-minus:before { + content: "\2212"; +} +.fooicon-search:before { + content: "\e003"; +} +.fooicon-remove:before { + content: "\e014"; +} +.fooicon-sort:before { + content: "\e150"; +} +.fooicon-sort-asc:before { + content: "\e155"; +} +.fooicon-sort-desc:before { + content: "\e156"; +} +.fooicon-pencil:before { + content: "\270f"; +} +.fooicon-trash:before { + content: "\e020"; +} +.fooicon-eye-close:before { + content: "\e106"; +} +.fooicon-flash:before { + content: "\e162"; +} +.fooicon-cog:before { + content: "\e019"; +} +table.footable > thead > tr.footable-filtering > th { + border-bottom-width: 1px; + font-weight: normal; +} +table.footable > thead > tr.footable-filtering > th, +table.footable.footable-filtering-right > thead > tr.footable-filtering > th { + text-align: right; +} +table.footable.footable-filtering-left > thead > tr.footable-filtering > th { + text-align: left; +} +table.footable.footable-filtering-center > thead > tr.footable-filtering > th { + text-align: center; +} +table.footable > thead > tr.footable-filtering > th div.form-group { + margin-bottom: 0; +} +table.footable > thead > tr.footable-filtering > th div.form-group+div.form-group { + margin-top: 5px; +} +table.footable > thead > tr.footable-filtering > th div.input-group { + width: 100%; +} +table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox { + margin: 0; + display: block; + position: relative; +} +table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox > label { + display: block; + padding-left: 20px; +} +table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox input[type="checkbox"] { + position: absolute; + margin-left: -20px; +} +@media (min-width: 768px) { + table.footable > thead > tr.footable-filtering > th div.input-group { + width: auto; + } + table.footable > thead > tr.footable-filtering > th div.form-group { + margin-left: 2px; + margin-right: 2px; + } + table.footable > thead > tr.footable-filtering > th div.form-group+div.form-group { + margin-top: 0; + } +} +td.footable-sortable, +th.footable-sortable { + position: relative; + padding-right: 30px; + cursor: pointer; +} +td.footable-sortable > span.fooicon, +th.footable-sortable > span.fooicon { + position: absolute; + right: 6px; + top: 50%; + margin-top: -7px; + opacity: 0; + transition: opacity 0.3s ease-in; +} +td.footable-sortable:hover > span.fooicon, +th.footable-sortable:hover > span.fooicon { + opacity: 1; +} +td.footable-sortable.footable-asc > span.fooicon, +th.footable-sortable.footable-asc > span.fooicon, +td.footable-sortable.footable-desc > span.fooicon, +th.footable-sortable.footable-desc > span.fooicon { + opacity: 1; +} +table.footable > tfoot > tr.footable-paging > td > ul.pagination { + margin: 10px 0 0 0; +} +table.footable > tfoot > tr.footable-paging > td > span.label { + display: inline-block; + margin: 0 0 10px 0; + padding: 4px 10px; +} +table.footable > tfoot > tr.footable-paging > td, +table.footable-paging-center > tfoot > tr.footable-paging > td { + text-align: center; +} +table.footable-paging-left > tfoot > tr.footable-paging > td { + text-align: left; +} +table.footable-paging-right > tfoot > tr.footable-paging > td { + text-align: right; +} +ul.pagination > li.footable-page { + display: none; +} +ul.pagination > li.footable-page.visible { + display: inline; +} \ No newline at end of file diff --git a/media/footable-v3/css/footable.bootstrap.min.css b/media/footable-v3/css/footable.bootstrap.min.css new file mode 100644 index 0000000..c297e48 --- /dev/null +++ b/media/footable-v3/css/footable.bootstrap.min.css @@ -0,0 +1 @@ +table.footable-details,table.footable>thead>tr.footable-filtering>th div.form-group{margin-bottom:0}table.footable,table.footable-details{position:relative;width:100%;border-spacing:0;border-collapse:collapse}table>tbody>tr>td>span.footable-toggle{margin-right:8px;opacity:.3}table>tbody>tr>td>span.footable-toggle.last-column{margin-left:8px;float:right}table.table-condensed>tbody>tr>td>span.footable-toggle{margin-right:5px}table.footable-details>tbody>tr>th:nth-child(1){min-width:40px;width:120px}table.footable-details>tbody>tr>td:nth-child(2){word-break:break-all}table.footable-details>tbody>tr:first-child>td,table.footable-details>tbody>tr:first-child>th,table.footable-details>tfoot>tr:first-child>td,table.footable-details>tfoot>tr:first-child>th,table.footable-details>thead>tr:first-child>td,table.footable-details>thead>tr:first-child>th{border-top-width:0}table.footable-details.table-bordered>tbody>tr:first-child>td,table.footable-details.table-bordered>tbody>tr:first-child>th,table.footable-details.table-bordered>tfoot>tr:first-child>td,table.footable-details.table-bordered>tfoot>tr:first-child>th,table.footable-details.table-bordered>thead>tr:first-child>td,table.footable-details.table-bordered>thead>tr:first-child>th{border-top-width:1px}div.footable-loader{vertical-align:middle;text-align:center;height:300px;position:relative}div.footable-loader>span.fooicon{display:inline-block;opacity:.3;font-size:30px;line-height:32px;width:32px;height:32px;margin-top:-16px;margin-left:-16px;position:absolute;top:50%;left:50%;-webkit-animation:fooicon-spin-r 2s infinite linear;animation:fooicon-spin-r 2s infinite linear}table.footable>tbody>tr.footable-empty>td{vertical-align:middle;text-align:center;font-size:30px}table.footable>tbody>tr>td,table.footable>tbody>tr>th{display:none}table.footable>tbody>tr.footable-detail-row>td,table.footable>tbody>tr.footable-detail-row>th,table.footable>tbody>tr.footable-empty>td,table.footable>tbody>tr.footable-empty>th{display:table-cell}@-webkit-keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fooicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings'!important;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fooicon:after,.fooicon:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fooicon-loader:before{content:"\e030"}.fooicon-plus:before{content:"\2b"}.fooicon-minus:before{content:"\2212"}.fooicon-search:before{content:"\e003"}.fooicon-remove:before{content:"\e014"}.fooicon-sort:before{content:"\e150"}.fooicon-sort-asc:before{content:"\e155"}.fooicon-sort-desc:before{content:"\e156"}.fooicon-pencil:before{content:"\270f"}.fooicon-trash:before{content:"\e020"}.fooicon-eye-close:before{content:"\e106"}.fooicon-flash:before{content:"\e162"}.fooicon-cog:before{content:"\e019"}table.footable>thead>tr.footable-filtering>th{border-bottom-width:1px;font-weight:400}table.footable.footable-filtering-right>thead>tr.footable-filtering>th,table.footable>thead>tr.footable-filtering>th{text-align:right}table.footable.footable-filtering-left>thead>tr.footable-filtering>th{text-align:left}table.footable-paging-center>tfoot>tr.footable-paging>td,table.footable.footable-filtering-center>thead>tr.footable-filtering>th,table.footable>tfoot>tr.footable-paging>td{text-align:center}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:5px}table.footable>thead>tr.footable-filtering>th div.input-group{width:100%}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox{margin:0;display:block;position:relative}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox>label{display:block;padding-left:20px}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox input[type=checkbox]{position:absolute;margin-left:-20px}@media (min-width:768px){table.footable>thead>tr.footable-filtering>th div.input-group{width:auto}table.footable>thead>tr.footable-filtering>th div.form-group{margin-left:2px;margin-right:2px}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:0}}td.footable-sortable,th.footable-sortable{position:relative;padding-right:30px;cursor:pointer}td.footable-sortable>span.fooicon,th.footable-sortable>span.fooicon{position:absolute;right:6px;top:50%;margin-top:-7px;opacity:0;transition:opacity .3s ease-in}td.footable-sortable.footable-asc>span.fooicon,td.footable-sortable.footable-desc>span.fooicon,td.footable-sortable:hover>span.fooicon,th.footable-sortable.footable-asc>span.fooicon,th.footable-sortable.footable-desc>span.fooicon,th.footable-sortable:hover>span.fooicon{opacity:1}table.footable>tfoot>tr.footable-paging>td>ul.pagination{margin:10px 0 0}table.footable>tfoot>tr.footable-paging>td>span.label{display:inline-block;margin:0 0 10px;padding:4px 10px}table.footable-paging-left>tfoot>tr.footable-paging>td{text-align:left}table.footable-paging-right>tfoot>tr.footable-paging>td{text-align:right}ul.pagination>li.footable-page{display:none}ul.pagination>li.footable-page.visible{display:inline} \ No newline at end of file diff --git a/media/footable-v3/css/footable.core.bootstrap.css b/media/footable-v3/css/footable.core.bootstrap.css new file mode 100644 index 0000000..30be581 --- /dev/null +++ b/media/footable-v3/css/footable.core.bootstrap.css @@ -0,0 +1,175 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +table.footable, +table.footable-details { + position: relative; + width: 100%; + border-spacing: 0; + border-collapse: collapse; +} +table.footable-details { + margin-bottom: 0; +} +table.footable-hide-fouc { + display: none; +} +table > tbody > tr > td > span.footable-toggle { + margin-right: 8px; + opacity: 0.3; +} +table > tbody > tr > td > span.footable-toggle.last-column { + margin-left: 8px; + float: right; +} +table.table-condensed > tbody > tr > td > span.footable-toggle { + margin-right: 5px; +} +table.footable-details > tbody > tr > th:nth-child(1) { + min-width: 40px; + width: 120px; +} +table.footable-details > tbody > tr > td:nth-child(2) { + word-break: break-all; +} + +table.footable-details > thead > tr:first-child > th, +table.footable-details > thead > tr:first-child > td, +table.footable-details > tbody > tr:first-child > th, +table.footable-details > tbody > tr:first-child > td, +table.footable-details > tfoot > tr:first-child > th, +table.footable-details > tfoot > tr:first-child > td { + border-top-width: 0; +} +table.footable-details.table-bordered > thead > tr:first-child > th, +table.footable-details.table-bordered > thead > tr:first-child > td, +table.footable-details.table-bordered > tbody > tr:first-child > th, +table.footable-details.table-bordered > tbody > tr:first-child > td, +table.footable-details.table-bordered > tfoot > tr:first-child > th, +table.footable-details.table-bordered > tfoot > tr:first-child > td { + border-top-width: 1px; +} + +div.footable-loader { + vertical-align: middle; + text-align: center; + height: 300px; + position: relative; +} +div.footable-loader > span.fooicon { + display: inline-block; + opacity: 0.3; + font-size: 30px; + line-height: 32px; + width: 32px; + height: 32px; + margin-top: -16px; + margin-left: -16px; + position: absolute; + top: 50%; + left: 50%; + -webkit-animation: fooicon-spin-r 2s infinite linear; + animation: fooicon-spin-r 2s infinite linear; +} +table.footable > tbody > tr.footable-empty > td { + vertical-align: middle; + text-align: center; + font-size: 30px; +} +table.footable > tbody > tr > td, +table.footable > tbody > tr > th { + display: none; +} +table.footable > tbody > tr.footable-empty > td, +table.footable > tbody > tr.footable-empty > th, +table.footable > tbody > tr.footable-detail-row > td, +table.footable > tbody > tr.footable-detail-row > th { + display: table-cell; +} +@-webkit-keyframes fooicon-spin-r { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +@keyframes fooicon-spin-r { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +.fooicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings' !important; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.fooicon:before, +.fooicon:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.fooicon-loader:before { + content: "\e030"; +} +.fooicon-plus:before { + content: "\2b"; +} +.fooicon-minus:before { + content: "\2212"; +} +.fooicon-search:before { + content: "\e003"; +} +.fooicon-remove:before { + content: "\e014"; +} +.fooicon-sort:before { + content: "\e150"; +} +.fooicon-sort-asc:before { + content: "\e155"; +} +.fooicon-sort-desc:before { + content: "\e156"; +} +.fooicon-pencil:before { + content: "\270f"; +} +.fooicon-trash:before { + content: "\e020"; +} +.fooicon-eye-close:before { + content: "\e106"; +} +.fooicon-flash:before { + content: "\e162"; +} +.fooicon-cog:before { + content: "\e019"; +} +.fooicon-stats:before { + content: "\e185"; +} diff --git a/media/footable-v3/css/footable.core.bootstrap.min.css b/media/footable-v3/css/footable.core.bootstrap.min.css new file mode 100644 index 0000000..771e966 --- /dev/null +++ b/media/footable-v3/css/footable.core.bootstrap.min.css @@ -0,0 +1 @@ +table.footable,table.footable-details{position:relative;width:100%;border-spacing:0;border-collapse:collapse}table.footable-details{margin-bottom:0}table.footable-hide-fouc{display:none}table>tbody>tr>td>span.footable-toggle{margin-right:8px;opacity:.3}table>tbody>tr>td>span.footable-toggle.last-column{margin-left:8px;float:right}table.table-condensed>tbody>tr>td>span.footable-toggle{margin-right:5px}table.footable-details>tbody>tr>th:nth-child(1){min-width:40px;width:120px}table.footable-details>tbody>tr>td:nth-child(2){word-break:break-all}table.footable-details>tbody>tr:first-child>td,table.footable-details>tbody>tr:first-child>th,table.footable-details>tfoot>tr:first-child>td,table.footable-details>tfoot>tr:first-child>th,table.footable-details>thead>tr:first-child>td,table.footable-details>thead>tr:first-child>th{border-top-width:0}table.footable-details.table-bordered>tbody>tr:first-child>td,table.footable-details.table-bordered>tbody>tr:first-child>th,table.footable-details.table-bordered>tfoot>tr:first-child>td,table.footable-details.table-bordered>tfoot>tr:first-child>th,table.footable-details.table-bordered>thead>tr:first-child>td,table.footable-details.table-bordered>thead>tr:first-child>th{border-top-width:1px}div.footable-loader{vertical-align:middle;text-align:center;height:300px;position:relative}div.footable-loader>span.fooicon{display:inline-block;opacity:.3;font-size:30px;line-height:32px;width:32px;height:32px;margin-top:-16px;margin-left:-16px;position:absolute;top:50%;left:50%;-webkit-animation:fooicon-spin-r 2s infinite linear;animation:fooicon-spin-r 2s infinite linear}table.footable>tbody>tr.footable-empty>td{vertical-align:middle;text-align:center;font-size:30px}table.footable>tbody>tr>td,table.footable>tbody>tr>th{display:none}table.footable>tbody>tr.footable-detail-row>td,table.footable>tbody>tr.footable-detail-row>th,table.footable>tbody>tr.footable-empty>td,table.footable>tbody>tr.footable-empty>th{display:table-cell}@-webkit-keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fooicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings'!important;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fooicon:after,.fooicon:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fooicon-loader:before{content:"\e030"}.fooicon-plus:before{content:"\2b"}.fooicon-minus:before{content:"\2212"}.fooicon-search:before{content:"\e003"}.fooicon-remove:before{content:"\e014"}.fooicon-sort:before{content:"\e150"}.fooicon-sort-asc:before{content:"\e155"}.fooicon-sort-desc:before{content:"\e156"}.fooicon-pencil:before{content:"\270f"}.fooicon-trash:before{content:"\e020"}.fooicon-eye-close:before{content:"\e106"}.fooicon-flash:before{content:"\e162"}.fooicon-cog:before{content:"\e019"}.fooicon-stats:before{content:"\e185"} \ No newline at end of file diff --git a/media/footable-v3/css/footable.core.standalone.css b/media/footable-v3/css/footable.core.standalone.css new file mode 100644 index 0000000..679543a --- /dev/null +++ b/media/footable-v3/css/footable.core.standalone.css @@ -0,0 +1,675 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +/* globals - instead we house them under the .footable.table class as the root of FooTable is always the table itself. */ +.footable.table, .footable.table *, +.footable-details.table, .footable-details.table * { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.footable.table th, +.footable-details.table th { + text-align: left; +} +/* tables */ +.footable.table, +.footable-details.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.footable.table > caption + thead > tr:first-child > th, +.footable.table > colgroup + thead > tr:first-child > th, +.footable.table > thead:first-child > tr:first-child > th, +.footable.table > caption + thead > tr:first-child > td, +.footable.table > colgroup + thead > tr:first-child > td, +.footable.table > thead:first-child > tr:first-child > td, +.footable-details.table > caption + thead > tr:first-child > th, +.footable-details.table > colgroup + thead > tr:first-child > th, +.footable-details.table > thead:first-child > tr:first-child > th, +.footable-details.table > caption + thead > tr:first-child > td, +.footable-details.table > colgroup + thead > tr:first-child > td, +.footable-details.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.footable.table > thead > tr > th, +.footable.table > tbody > tr > th, +.footable.table > tfoot > tr > th, +.footable.table > thead > tr > td, +.footable.table > tbody > tr > td, +.footable.table > tfoot > tr > td, +.footable-details.table > thead > tr > th, +.footable-details.table > tbody > tr > th, +.footable-details.table > tfoot > tr > th, +.footable-details.table > thead > tr > td, +.footable-details.table > tbody > tr > td, +.footable-details.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.footable.table > thead > tr > th, +.footable.table > thead > tr > td, +.footable-details.table > thead > tr > th, +.footable-details.table > thead > tr > td { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.footable.table-condensed > thead > tr > th, +.footable.table-condensed > tbody > tr > th, +.footable.table-condensed > tfoot > tr > th, +.footable.table-condensed > thead > tr > td, +.footable.table-condensed > tbody > tr > td, +.footable.table-condensed > tfoot > tr > td, +.footable-details.table-condensed > thead > tr > th, +.footable-details.table-condensed > tbody > tr > th, +.footable-details.table-condensed > tfoot > tr > th, +.footable-details.table-condensed > thead > tr > td, +.footable-details.table-condensed > tbody > tr > td, +.footable-details.table-condensed > tfoot > tr > td { + padding: 5px; +} +.footable.table-bordered, +.footable-details.table-bordered { + border: 1px solid #ddd; +} +.footable.table-bordered > thead > tr > th, +.footable.table-bordered > tbody > tr > th, +.footable.table-bordered > tfoot > tr > th, +.footable.table-bordered > thead > tr > td, +.footable.table-bordered > tbody > tr > td, +.footable.table-bordered > tfoot > tr > td, +.footable-details.table-bordered > thead > tr > th, +.footable-details.table-bordered > tbody > tr > th, +.footable-details.table-bordered > tfoot > tr > th, +.footable-details.table-bordered > thead > tr > td, +.footable-details.table-bordered > tbody > tr > td, +.footable-details.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.footable.table-bordered > thead > tr > th, +.footable.table-bordered > thead > tr > td, +.footable-details.table-bordered > thead > tr > th, +.footable-details.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.footable.table-striped > tbody > tr:nth-child(odd), +.footable-details.table-striped > tbody > tr:nth-child(odd) { + background-color: #f9f9f9; +} +.footable.table-hover > tbody > tr:hover, +.footable-details.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +/* buttons */ +.footable .btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: 400; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-appearance: button; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; + overflow: visible; + text-transform: none; +} +.footable .btn:hover, +.footable .btn:focus, +.footable .btn.focus { + color: #333; + text-decoration: none; +} +.footable .btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.footable .btn-default:hover, +.footable .btn-default:focus, +.footable .btn-default.focus, +.footable .btn-default:active, +.footable .btn-default.active, +.footable .open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.footable .btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.footable .btn-primary:hover, +.footable .btn-primary:focus, +.footable .btn-primary.focus, +.footable .btn-primary:active, +.footable .btn-primary.active, +.footable .open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +/* button groups */ +.footable .btn-group, .footable .btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.footable .btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.footable .btn-group > .btn:first-child { + margin-left: 0; +} +.footable .btn-group-vertical > .btn, +.footable .btn-group > .btn { + position: relative; + float: left; +} +.footable .btn-group-xs > .btn, +.footable .btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.footable .btn-group-sm > .btn, +.footable .btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.footable .btn-group-lg > .btn, +.footable .btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +/* caret */ +.footable .caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.footable .btn .caret { + margin-left: 0; +} +/* form-group */ +.form-group { + margin-bottom: 15px; +} +/* form-control */ +.footable .form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + margin: 0; + font-family: inherit; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +/* input-group */ +.footable .input-group { + position: relative; + display: table; + border-collapse: separate; +} +.footable .input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.footable .input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.footable .input-group-addon, +.footable .input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.footable .input-group-addon, +.footable .input-group-btn, +.footable .input-group .form-control { + display: table-cell; +} +.footable .input-group-btn > .btn + .btn, +.footable .input-group-btn:last-child > .btn, +.footable .input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.footable .input-group-btn > .btn { + position: relative; +} +.footable .input-group-btn > .btn:hover, +.footable .input-group-btn > .btn:focus, +.footable .input-group-btn > .btn:active { + z-index: 2; +} +.footable .input-group .form-control:first-child, +.footable .input-group-addon:first-child, +.footable .input-group-btn:first-child > .btn, +.footable .input-group-btn:first-child > .btn-group > .btn, +.footable .input-group-btn:first-child > .dropdown-toggle, +.footable .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.footable .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.footable .input-group .form-control:last-child, +.footable .input-group-addon:last-child, +.footable .input-group-btn:last-child > .btn, +.footable .input-group-btn:last-child > .btn-group > .btn, +.footable .input-group-btn:last-child > .dropdown-toggle, +.footable .input-group-btn:first-child > .btn:not(:first-child), +.footable .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +/* checkboxes & radios */ +.footable .radio, +.footable .checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.footable .radio label, +.footable .checkbox label { + max-width: 100%; + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + cursor: pointer; +} +.footable .radio input[type=radio], +.footable .radio-inline input[type=radio], +.footable .checkbox input[type=checkbox], +.footable .checkbox-inline input[type=checkbox] { + position: absolute; + margin: 4px 0 0 -20px; + line-height: normal; +} +/* dropdown-menu */ +.footable .dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.footable .open > .dropdown-menu { + display: block; +} +.footable .dropdown-menu-right { + right: 0; + left: auto; +} +.footable .dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.footable .dropdown-menu > li > a:hover, +.footable .dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +/* pagination */ +.footable .pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.footable .pagination > li { + display: inline; +} +.footable .pagination > li:first-child > a, +.footable .pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.footable .pagination > li > a, +.footable .pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.footable .pagination > li > a:hover, +.footable .pagination > li > span:hover, +.footable .pagination > li > a:focus, +.footable .pagination > li > span:focus { + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.footable .pagination > .active > a, +.footable .pagination > .active > span, +.footable .pagination > .active > a:hover, +.footable .pagination > .active > span:hover, +.footable .pagination > .active > a:focus, +.footable .pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.footable .pagination > .disabled > span, +.footable .pagination > .disabled > span:hover, +.footable .pagination > .disabled > span:focus, +.footable .pagination > .disabled > a, +.footable .pagination > .disabled > a:hover, +.footable .pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +/* labels */ +.footable .label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: 700; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +.footable .label-default { + background-color: #777; +} +/* wells */ +.footable-loader.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.05); + box-shadow: inset 0 1px 1px rgba(0,0,0,.05); +} +/* screen reader only */ +.footable .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; +} +/* form-inline */ +@media (min-width: 768px) { + .footable .form-inline .form-group { + display:inline-block; + margin-bottom: 0; + vertical-align: middle + } + + .footable .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + + .footable .form-inline .input-group { + display: inline-table; + vertical-align: middle + } + + .footable .form-inline .input-group .input-group-addon, + .footable .form-inline .input-group .input-group-btn, + .footable .form-inline .input-group .form-control { + width: auto + } + + .footable .form-inline .input-group>.form-control { + width: 100% + } +} +table.footable, +table.footable-details { + position: relative; + width: 100%; + border-spacing: 0; + border-collapse: collapse; +} +table.footable-details { + margin-bottom: 0; +} +table.footable-hide-fouc { + display: none; +} +table > tbody > tr > td > span.footable-toggle { + margin-right: 8px; + opacity: 0.3; +} +table > tbody > tr > td > span.footable-toggle.last-column { + margin-left: 8px; + float: right; +} +table.table-condensed > tbody > tr > td > span.footable-toggle { + margin-right: 5px; +} +table.footable-details > tbody > tr > th:nth-child(1) { + min-width: 40px; + width: 120px; +} +table.footable-details > tbody > tr > td:nth-child(2) { + word-break: break-all; +} + +table.footable-details > thead > tr:first-child > th, +table.footable-details > thead > tr:first-child > td, +table.footable-details > tbody > tr:first-child > th, +table.footable-details > tbody > tr:first-child > td, +table.footable-details > tfoot > tr:first-child > th, +table.footable-details > tfoot > tr:first-child > td { + border-top-width: 0; +} +table.footable-details.table-bordered > thead > tr:first-child > th, +table.footable-details.table-bordered > thead > tr:first-child > td, +table.footable-details.table-bordered > tbody > tr:first-child > th, +table.footable-details.table-bordered > tbody > tr:first-child > td, +table.footable-details.table-bordered > tfoot > tr:first-child > th, +table.footable-details.table-bordered > tfoot > tr:first-child > td { + border-top-width: 1px; +} + +div.footable-loader { + vertical-align: middle; + text-align: center; + height: 300px; + position: relative; +} +div.footable-loader > span.fooicon { + display: inline-block; + opacity: 0.3; + font-size: 30px; + line-height: 32px; + width: 32px; + height: 32px; + margin-top: -16px; + margin-left: -16px; + position: absolute; + top: 50%; + left: 50%; + -webkit-animation: fooicon-spin-r 2s infinite linear; + animation: fooicon-spin-r 2s infinite linear; +} +table.footable > tbody > tr.footable-empty > td { + vertical-align: middle; + text-align: center; + font-size: 30px; +} +table.footable > tbody > tr > td, +table.footable > tbody > tr > th { + display: none; +} +table.footable > tbody > tr.footable-empty > td, +table.footable > tbody > tr.footable-empty > th, +table.footable > tbody > tr.footable-detail-row > td, +table.footable > tbody > tr.footable-detail-row > th { + display: table-cell; +} +@-webkit-keyframes fooicon-spin-r { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +@keyframes fooicon-spin-r { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +.fooicon { + display: inline-block; + font-size: inherit; + font-family: FontAwesome !important; + font-style: normal; + font-weight: 400; + line-height: 1; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + transform: translate(0, 0); +} +.fooicon:before, +.fooicon:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.fooicon-loader:before { + content: "\f01e"; +} +.fooicon-plus:before { + content: "\f067"; +} +.fooicon-minus:before { + content: "\f068"; +} +.fooicon-search:before { + content: "\f002"; +} +.fooicon-remove:before { + content: "\f00d"; +} +.fooicon-sort:before { + content: "\f0dc"; +} +.fooicon-sort-asc:before { + content: "\f160"; +} +.fooicon-sort-desc:before { + content: "\f161"; +} +.fooicon-pencil:before { + content: "\f040"; +} +.fooicon-trash:before { + content: "\f1f8"; +} +.fooicon-eye-close:before { + content: "\f070"; +} +.fooicon-flash:before { + content: "\f0e7"; +} +.fooicon-cog:before { + content: "\f013"; +} +.fooicon-stats:before { + content: "\f080"; +} diff --git a/media/footable-v3/css/footable.core.standalone.min.css b/media/footable-v3/css/footable.core.standalone.min.css new file mode 100644 index 0000000..ca4cdc6 --- /dev/null +++ b/media/footable-v3/css/footable.core.standalone.min.css @@ -0,0 +1 @@ +.footable .btn,.footable .caret{display:inline-block;vertical-align:middle}.footable-details.table,.footable-details.table *,.footable.table,.footable.table *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.footable-details.table th,.footable.table th{text-align:left}.footable-details.table,.footable.table{width:100%;max-width:100%;margin-bottom:20px}.footable-details.table>caption+thead>tr:first-child>td,.footable-details.table>caption+thead>tr:first-child>th,.footable-details.table>colgroup+thead>tr:first-child>td,.footable-details.table>colgroup+thead>tr:first-child>th,.footable-details.table>thead:first-child>tr:first-child>td,.footable-details.table>thead:first-child>tr:first-child>th,.footable.table>caption+thead>tr:first-child>td,.footable.table>caption+thead>tr:first-child>th,.footable.table>colgroup+thead>tr:first-child>td,.footable.table>colgroup+thead>tr:first-child>th,.footable.table>thead:first-child>tr:first-child>td,.footable.table>thead:first-child>tr:first-child>th{border-top:0}.footable-details.table>tbody>tr>td,.footable-details.table>tbody>tr>th,.footable-details.table>tfoot>tr>td,.footable-details.table>tfoot>tr>th,.footable-details.table>thead>tr>td,.footable-details.table>thead>tr>th,.footable.table>tbody>tr>td,.footable.table>tbody>tr>th,.footable.table>tfoot>tr>td,.footable.table>tfoot>tr>th,.footable.table>thead>tr>td,.footable.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.footable-details.table>thead>tr>td,.footable-details.table>thead>tr>th,.footable.table>thead>tr>td,.footable.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.footable-details.table-condensed>tbody>tr>td,.footable-details.table-condensed>tbody>tr>th,.footable-details.table-condensed>tfoot>tr>td,.footable-details.table-condensed>tfoot>tr>th,.footable-details.table-condensed>thead>tr>td,.footable-details.table-condensed>thead>tr>th,.footable.table-condensed>tbody>tr>td,.footable.table-condensed>tbody>tr>th,.footable.table-condensed>tfoot>tr>td,.footable.table-condensed>tfoot>tr>th,.footable.table-condensed>thead>tr>td,.footable.table-condensed>thead>tr>th{padding:5px}.footable-details.table-bordered,.footable-details.table-bordered>tbody>tr>td,.footable-details.table-bordered>tbody>tr>th,.footable-details.table-bordered>tfoot>tr>td,.footable-details.table-bordered>tfoot>tr>th,.footable-details.table-bordered>thead>tr>td,.footable-details.table-bordered>thead>tr>th,.footable.table-bordered,.footable.table-bordered>tbody>tr>td,.footable.table-bordered>tbody>tr>th,.footable.table-bordered>tfoot>tr>td,.footable.table-bordered>tfoot>tr>th,.footable.table-bordered>thead>tr>td,.footable.table-bordered>thead>tr>th{border:1px solid #ddd}.footable-details.table-bordered>thead>tr>td,.footable-details.table-bordered>thead>tr>th,.footable.table-bordered>thead>tr>td,.footable.table-bordered>thead>tr>th{border-bottom-width:2px}.footable-details.table-striped>tbody>tr:nth-child(odd),.footable.table-striped>tbody>tr:nth-child(odd){background-color:#f9f9f9}.footable-details.table-hover>tbody>tr:hover,.footable.table-hover>tbody>tr:hover{background-color:#f5f5f5}.footable .btn{padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-appearance:button;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px;overflow:visible;text-transform:none}.footable .btn.focus,.footable .btn:focus,.footable .btn:hover{color:#333;text-decoration:none}.footable .btn-default{color:#333;background-color:#fff;border-color:#ccc}.footable .btn-default.active,.footable .btn-default.focus,.footable .btn-default:active,.footable .btn-default:focus,.footable .btn-default:hover,.footable .open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.footable .btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.footable .btn-primary.active,.footable .btn-primary.focus,.footable .btn-primary:active,.footable .btn-primary:focus,.footable .btn-primary:hover,.footable .open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.footable .btn-group,.footable .btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.footable .btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.footable .btn-group>.btn:first-child{margin-left:0}.footable .btn-group-vertical>.btn,.footable .btn-group>.btn{position:relative;float:left}.footable .btn-group-xs>.btn,.footable .btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.footable .btn-group-sm>.btn,.footable .btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.footable .btn-group-lg>.btn,.footable .btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.footable .caret{width:0;height:0;margin-left:2px;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.footable .btn .caret{margin-left:0}.form-group{margin-bottom:15px}.footable .form-control{display:block;width:100%;height:34px;padding:6px 12px;margin:0;font-family:inherit;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.footable .input-group{position:relative;display:table;border-collapse:separate}.footable .input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.footable .input-group-btn{position:relative;font-size:0}.footable .input-group-addon,.footable .input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.footable .input-group .form-control,.footable .input-group-addon,.footable .input-group-btn{display:table-cell}.footable .input-group-btn:last-child>.btn,.footable .input-group-btn:last-child>.btn-group,.footable .input-group-btn>.btn+.btn{margin-left:-1px}.footable .input-group-btn>.btn{position:relative}.footable .input-group-btn>.btn:active,.footable .input-group-btn>.btn:focus,.footable .input-group-btn>.btn:hover{z-index:2}.footable .input-group .form-control:first-child,.footable .input-group-addon:first-child,.footable .input-group-btn:first-child>.btn,.footable .input-group-btn:first-child>.btn-group>.btn,.footable .input-group-btn:first-child>.dropdown-toggle,.footable .input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.footable .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.footable .input-group .form-control:last-child,.footable .input-group-addon:last-child,.footable .input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.footable .input-group-btn:first-child>.btn:not(:first-child),.footable .input-group-btn:last-child>.btn,.footable .input-group-btn:last-child>.btn-group>.btn,.footable .input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.footable .checkbox,.footable .radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.footable .checkbox label,.footable .radio label{max-width:100%;min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.footable .checkbox input[type=checkbox],.footable .checkbox-inline input[type=checkbox],.footable .radio input[type=radio],.footable .radio-inline input[type=radio]{position:absolute;margin:4px 0 0 -20px;line-height:normal}.footable .dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.footable .open>.dropdown-menu{display:block}.footable .dropdown-menu-right{right:0;left:auto}.footable .dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.footable .dropdown-menu>li>a:focus,.footable .dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.footable .pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.footable .pagination>li{display:inline}.footable .pagination>li:first-child>a,.footable .pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.footable .pagination>li>a,.footable .pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.footable .pagination>li>a:focus,.footable .pagination>li>a:hover,.footable .pagination>li>span:focus,.footable .pagination>li>span:hover{color:#23527c;background-color:#eee;border-color:#ddd}.footable .pagination>.active>a,.footable .pagination>.active>a:focus,.footable .pagination>.active>a:hover,.footable .pagination>.active>span,.footable .pagination>.active>span:focus,.footable .pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.footable .pagination>.disabled>a,.footable .pagination>.disabled>a:focus,.footable .pagination>.disabled>a:hover,.footable .pagination>.disabled>span,.footable .pagination>.disabled>span:focus,.footable .pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.footable .label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.footable .label-default{background-color:#777}.footable-loader.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.footable .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}@media (min-width:768px){.footable .form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.footable .form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.footable .form-inline .input-group{display:inline-table;vertical-align:middle}.footable .form-inline .input-group .form-control,.footable .form-inline .input-group .input-group-addon,.footable .form-inline .input-group .input-group-btn{width:auto}.footable .form-inline .input-group>.form-control{width:100%}}table.footable,table.footable-details{position:relative;width:100%;border-spacing:0;border-collapse:collapse}table.footable-details{margin-bottom:0}table.footable-hide-fouc{display:none}table>tbody>tr>td>span.footable-toggle{margin-right:8px;opacity:.3}table>tbody>tr>td>span.footable-toggle.last-column{margin-left:8px;float:right}table.table-condensed>tbody>tr>td>span.footable-toggle{margin-right:5px}table.footable-details>tbody>tr>th:nth-child(1){min-width:40px;width:120px}table.footable-details>tbody>tr>td:nth-child(2){word-break:break-all}table.footable-details>tbody>tr:first-child>td,table.footable-details>tbody>tr:first-child>th,table.footable-details>tfoot>tr:first-child>td,table.footable-details>tfoot>tr:first-child>th,table.footable-details>thead>tr:first-child>td,table.footable-details>thead>tr:first-child>th{border-top-width:0}table.footable-details.table-bordered>tbody>tr:first-child>td,table.footable-details.table-bordered>tbody>tr:first-child>th,table.footable-details.table-bordered>tfoot>tr:first-child>td,table.footable-details.table-bordered>tfoot>tr:first-child>th,table.footable-details.table-bordered>thead>tr:first-child>td,table.footable-details.table-bordered>thead>tr:first-child>th{border-top-width:1px}div.footable-loader{vertical-align:middle;text-align:center;height:300px;position:relative}div.footable-loader>span.fooicon{display:inline-block;opacity:.3;font-size:30px;line-height:32px;width:32px;height:32px;margin-top:-16px;margin-left:-16px;position:absolute;top:50%;left:50%;-webkit-animation:fooicon-spin-r 2s infinite linear;animation:fooicon-spin-r 2s infinite linear}table.footable>tbody>tr.footable-empty>td{vertical-align:middle;text-align:center;font-size:30px}table.footable>tbody>tr>td,table.footable>tbody>tr>th{display:none}table.footable>tbody>tr.footable-detail-row>td,table.footable>tbody>tr.footable-detail-row>th,table.footable>tbody>tr.footable-empty>td,table.footable>tbody>tr.footable-empty>th{display:table-cell}@-webkit-keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fooicon{display:inline-block;font-size:inherit;font-family:FontAwesome!important;font-style:normal;font-weight:400;line-height:1;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0,0)}.fooicon:after,.fooicon:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fooicon-loader:before{content:"\f01e"}.fooicon-plus:before{content:"\f067"}.fooicon-minus:before{content:"\f068"}.fooicon-search:before{content:"\f002"}.fooicon-remove:before{content:"\f00d"}.fooicon-sort:before{content:"\f0dc"}.fooicon-sort-asc:before{content:"\f160"}.fooicon-sort-desc:before{content:"\f161"}.fooicon-pencil:before{content:"\f040"}.fooicon-trash:before{content:"\f1f8"}.fooicon-eye-close:before{content:"\f070"}.fooicon-flash:before{content:"\f0e7"}.fooicon-cog:before{content:"\f013"}.fooicon-stats:before{content:"\f080"} \ No newline at end of file diff --git a/media/footable-v3/css/footable.editing.css b/media/footable-v3/css/footable.editing.css new file mode 100644 index 0000000..e99197c --- /dev/null +++ b/media/footable-v3/css/footable.editing.css @@ -0,0 +1,50 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +td.footable-editing { + width: 90px; + max-width: 90px; +} +table.footable-editing-no-edit td.footable-editing, +table.footable-editing-no-delete td.footable-editing, +table.footable-editing-no-view td.footable-editing { + width: 70px; + max-width: 70px; +} +table.footable-editing-no-edit.footable-editing-no-delete td.footable-editing, +table.footable-editing-no-edit.footable-editing-no-view td.footable-editing, +table.footable-editing-no-delete.footable-editing-no-view td.footable-editing { + width: 50px; + max-width: 50px; +} +table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view td.footable-editing, +table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view th.footable-editing { + width: 0; + max-width: 0; + display: none !important; +} +table.footable-editing-right td.footable-editing, +table.footable-editing-right tr.footable-editing { + text-align: right; +} +table.footable-editing-left td.footable-editing, +table.footable-editing-left tr.footable-editing { + text-align: left; +} +table.footable-editing button.footable-add, +table.footable-editing button.footable-hide, +table.footable-editing-show button.footable-show, +table.footable-editing.footable-editing-always-show button.footable-show, +table.footable-editing.footable-editing-always-show button.footable-hide, +table.footable-editing.footable-editing-always-show.footable-editing-no-add tr.footable-editing { + display: none; +} +table.footable-editing.footable-editing-show button.footable-add, +table.footable-editing.footable-editing-show button.footable-hide, +table.footable-editing.footable-editing-always-show button.footable-add { + display: inline-block; +} diff --git a/media/footable-v3/css/footable.editing.min.css b/media/footable-v3/css/footable.editing.min.css new file mode 100644 index 0000000..5bce15b --- /dev/null +++ b/media/footable-v3/css/footable.editing.min.css @@ -0,0 +1 @@ +td.footable-editing{width:90px;max-width:90px}table.footable-editing-no-delete td.footable-editing,table.footable-editing-no-edit td.footable-editing,table.footable-editing-no-view td.footable-editing{width:70px;max-width:70px}table.footable-editing-no-delete.footable-editing-no-view td.footable-editing,table.footable-editing-no-edit.footable-editing-no-delete td.footable-editing,table.footable-editing-no-edit.footable-editing-no-view td.footable-editing{width:50px;max-width:50px}table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view td.footable-editing,table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view th.footable-editing{width:0;max-width:0;display:none!important}table.footable-editing-right td.footable-editing,table.footable-editing-right tr.footable-editing{text-align:right}table.footable-editing-left td.footable-editing,table.footable-editing-left tr.footable-editing{text-align:left}table.footable-editing button.footable-add,table.footable-editing button.footable-hide,table.footable-editing-show button.footable-show,table.footable-editing.footable-editing-always-show button.footable-hide,table.footable-editing.footable-editing-always-show button.footable-show,table.footable-editing.footable-editing-always-show.footable-editing-no-add tr.footable-editing{display:none}table.footable-editing.footable-editing-always-show button.footable-add,table.footable-editing.footable-editing-show button.footable-add,table.footable-editing.footable-editing-show button.footable-hide{display:inline-block} \ No newline at end of file diff --git a/media/footable-v3/css/footable.filtering.css b/media/footable-v3/css/footable.filtering.css new file mode 100644 index 0000000..4f18006 --- /dev/null +++ b/media/footable-v3/css/footable.filtering.css @@ -0,0 +1,55 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +table.footable > thead > tr.footable-filtering > th { + border-bottom-width: 1px; + font-weight: normal; +} +table.footable > thead > tr.footable-filtering > th, +table.footable.footable-filtering-right > thead > tr.footable-filtering > th { + text-align: right; +} +table.footable.footable-filtering-left > thead > tr.footable-filtering > th { + text-align: left; +} +table.footable.footable-filtering-center > thead > tr.footable-filtering > th { + text-align: center; +} +table.footable > thead > tr.footable-filtering > th div.form-group { + margin-bottom: 0; +} +table.footable > thead > tr.footable-filtering > th div.form-group+div.form-group { + margin-top: 5px; +} +table.footable > thead > tr.footable-filtering > th div.input-group { + width: 100%; +} +table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox { + margin: 0; + display: block; + position: relative; +} +table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox > label { + display: block; + padding-left: 20px; +} +table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox input[type="checkbox"] { + position: absolute; + margin-left: -20px; +} +@media (min-width: 768px) { + table.footable > thead > tr.footable-filtering > th div.input-group { + width: auto; + } + table.footable > thead > tr.footable-filtering > th div.form-group { + margin-left: 2px; + margin-right: 2px; + } + table.footable > thead > tr.footable-filtering > th div.form-group+div.form-group { + margin-top: 0; + } +} \ No newline at end of file diff --git a/media/footable-v3/css/footable.filtering.min.css b/media/footable-v3/css/footable.filtering.min.css new file mode 100644 index 0000000..cd74997 --- /dev/null +++ b/media/footable-v3/css/footable.filtering.min.css @@ -0,0 +1 @@ +table.footable>thead>tr.footable-filtering>th{border-bottom-width:1px;font-weight:400}table.footable.footable-filtering-right>thead>tr.footable-filtering>th,table.footable>thead>tr.footable-filtering>th{text-align:right}table.footable.footable-filtering-left>thead>tr.footable-filtering>th{text-align:left}table.footable.footable-filtering-center>thead>tr.footable-filtering>th{text-align:center}table.footable>thead>tr.footable-filtering>th div.form-group{margin-bottom:0}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:5px}table.footable>thead>tr.footable-filtering>th div.input-group{width:100%}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox{margin:0;display:block;position:relative}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox>label{display:block;padding-left:20px}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox input[type=checkbox]{position:absolute;margin-left:-20px}@media (min-width:768px){table.footable>thead>tr.footable-filtering>th div.input-group{width:auto}table.footable>thead>tr.footable-filtering>th div.form-group{margin-left:2px;margin-right:2px}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:0}} \ No newline at end of file diff --git a/media/footable-v3/css/footable.paging.css b/media/footable-v3/css/footable.paging.css new file mode 100644 index 0000000..f3e111d --- /dev/null +++ b/media/footable-v3/css/footable.paging.css @@ -0,0 +1,31 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +table.footable > tfoot > tr.footable-paging > td > ul.pagination { + margin: 10px 0 0 0; +} +table.footable > tfoot > tr.footable-paging > td > span.label { + display: inline-block; + margin: 0 0 10px 0; + padding: 4px 10px; +} +table.footable > tfoot > tr.footable-paging > td, +table.footable-paging-center > tfoot > tr.footable-paging > td { + text-align: center; +} +table.footable-paging-left > tfoot > tr.footable-paging > td { + text-align: left; +} +table.footable-paging-right > tfoot > tr.footable-paging > td { + text-align: right; +} +ul.pagination > li.footable-page { + display: none; +} +ul.pagination > li.footable-page.visible { + display: inline; +} \ No newline at end of file diff --git a/media/footable-v3/css/footable.paging.min.css b/media/footable-v3/css/footable.paging.min.css new file mode 100644 index 0000000..2b43136 --- /dev/null +++ b/media/footable-v3/css/footable.paging.min.css @@ -0,0 +1 @@ +table.footable>tfoot>tr.footable-paging>td>ul.pagination{margin:10px 0 0}table.footable>tfoot>tr.footable-paging>td>span.label{display:inline-block;margin:0 0 10px;padding:4px 10px}table.footable-paging-center>tfoot>tr.footable-paging>td,table.footable>tfoot>tr.footable-paging>td{text-align:center}table.footable-paging-left>tfoot>tr.footable-paging>td{text-align:left}table.footable-paging-right>tfoot>tr.footable-paging>td{text-align:right}ul.pagination>li.footable-page{display:none}ul.pagination>li.footable-page.visible{display:inline} \ No newline at end of file diff --git a/media/footable-v3/css/footable.sorting.css b/media/footable-v3/css/footable.sorting.css new file mode 100644 index 0000000..8dce90d --- /dev/null +++ b/media/footable-v3/css/footable.sorting.css @@ -0,0 +1,46 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +table.footable > thead > tr > td.footable-sortable, +table.footable > thead > tr > th.footable-sortable, +table.footable > tbody > tr > td.footable-sortable, +table.footable > tbody > tr > th.footable-sortable, +table.footable > tfoot > tr > td.footable-sortable, +table.footable > tfoot > tr > th.footable-sortable { + position: relative; + padding-right: 30px; + cursor: pointer; +} +td.footable-sortable > span.fooicon, +th.footable-sortable > span.fooicon { + position: absolute; + right: 6px; + top: 50%; + margin-top: -7px; + opacity: 0; + transition: opacity 0.3s ease-in; +} +td.footable-sortable:hover > span.fooicon, +th.footable-sortable:hover > span.fooicon { + opacity: 1; +} +td.footable-sortable.footable-asc > span.fooicon, +th.footable-sortable.footable-asc > span.fooicon, +td.footable-sortable.footable-desc > span.fooicon, +th.footable-sortable.footable-desc > span.fooicon { + opacity: 1; +} +/* hides the sort icons when sorting is not allowed */ +table.footable-sorting-disabled td.footable-sortable.footable-asc > span.fooicon, +table.footable-sorting-disabled td.footable-sortable.footable-desc > span.fooicon, +table.footable-sorting-disabled td.footable-sortable:hover > span.fooicon, +table.footable-sorting-disabled th.footable-sortable.footable-asc > span.fooicon, +table.footable-sorting-disabled th.footable-sortable.footable-desc > span.fooicon, +table.footable-sorting-disabled th.footable-sortable:hover > span.fooicon { + opacity: 0; + visibility: hidden; +} \ No newline at end of file diff --git a/media/footable-v3/css/footable.sorting.min.css b/media/footable-v3/css/footable.sorting.min.css new file mode 100644 index 0000000..1592c94 --- /dev/null +++ b/media/footable-v3/css/footable.sorting.min.css @@ -0,0 +1 @@ +table.footable>tbody>tr>td.footable-sortable,table.footable>tbody>tr>th.footable-sortable,table.footable>tfoot>tr>td.footable-sortable,table.footable>tfoot>tr>th.footable-sortable,table.footable>thead>tr>td.footable-sortable,table.footable>thead>tr>th.footable-sortable{position:relative;padding-right:30px;cursor:pointer}td.footable-sortable>span.fooicon,th.footable-sortable>span.fooicon{position:absolute;right:6px;top:50%;margin-top:-7px;opacity:0;transition:opacity .3s ease-in}td.footable-sortable.footable-asc>span.fooicon,td.footable-sortable.footable-desc>span.fooicon,td.footable-sortable:hover>span.fooicon,th.footable-sortable.footable-asc>span.fooicon,th.footable-sortable.footable-desc>span.fooicon,th.footable-sortable:hover>span.fooicon{opacity:1}table.footable-sorting-disabled td.footable-sortable.footable-asc>span.fooicon,table.footable-sorting-disabled td.footable-sortable.footable-desc>span.fooicon,table.footable-sorting-disabled td.footable-sortable:hover>span.fooicon,table.footable-sorting-disabled th.footable-sortable.footable-asc>span.fooicon,table.footable-sorting-disabled th.footable-sortable.footable-desc>span.fooicon,table.footable-sorting-disabled th.footable-sortable:hover>span.fooicon{opacity:0;visibility:hidden} \ No newline at end of file diff --git a/media/footable-v3/css/footable.standalone.css b/media/footable-v3/css/footable.standalone.css new file mode 100644 index 0000000..15be741 --- /dev/null +++ b/media/footable-v3/css/footable.standalone.css @@ -0,0 +1,727 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.0.6 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +/* globals - instead we house them under the .footable.table class as the root of FooTable is always the table itself. */ +.footable.table, .footable.table *, +.footable-details.table, .footable-details.table * { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.footable.table th, +.footable-details.table th { + text-align: left; +} +/* tables */ +.footable.table, +.footable-details.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.footable.table > caption + thead > tr:first-child > th, +.footable.table > colgroup + thead > tr:first-child > th, +.footable.table > thead:first-child > tr:first-child > th, +.footable.table > caption + thead > tr:first-child > td, +.footable.table > colgroup + thead > tr:first-child > td, +.footable.table > thead:first-child > tr:first-child > td, +.footable-details.table > caption + thead > tr:first-child > th, +.footable-details.table > colgroup + thead > tr:first-child > th, +.footable-details.table > thead:first-child > tr:first-child > th, +.footable-details.table > caption + thead > tr:first-child > td, +.footable-details.table > colgroup + thead > tr:first-child > td, +.footable-details.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.footable.table > thead > tr > th, +.footable.table > tbody > tr > th, +.footable.table > tfoot > tr > th, +.footable.table > thead > tr > td, +.footable.table > tbody > tr > td, +.footable.table > tfoot > tr > td, +.footable-details.table > thead > tr > th, +.footable-details.table > tbody > tr > th, +.footable-details.table > tfoot > tr > th, +.footable-details.table > thead > tr > td, +.footable-details.table > tbody > tr > td, +.footable-details.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.footable.table > thead > tr > th, +.footable.table > thead > tr > td, +.footable-details.table > thead > tr > th, +.footable-details.table > thead > tr > td { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.footable.table-condensed > thead > tr > th, +.footable.table-condensed > tbody > tr > th, +.footable.table-condensed > tfoot > tr > th, +.footable.table-condensed > thead > tr > td, +.footable.table-condensed > tbody > tr > td, +.footable.table-condensed > tfoot > tr > td, +.footable-details.table-condensed > thead > tr > th, +.footable-details.table-condensed > tbody > tr > th, +.footable-details.table-condensed > tfoot > tr > th, +.footable-details.table-condensed > thead > tr > td, +.footable-details.table-condensed > tbody > tr > td, +.footable-details.table-condensed > tfoot > tr > td { + padding: 5px; +} +.footable.table-bordered, +.footable-details.table-bordered { + border: 1px solid #ddd; +} +.footable.table-bordered > thead > tr > th, +.footable.table-bordered > tbody > tr > th, +.footable.table-bordered > tfoot > tr > th, +.footable.table-bordered > thead > tr > td, +.footable.table-bordered > tbody > tr > td, +.footable.table-bordered > tfoot > tr > td, +.footable-details.table-bordered > thead > tr > th, +.footable-details.table-bordered > tbody > tr > th, +.footable-details.table-bordered > tfoot > tr > th, +.footable-details.table-bordered > thead > tr > td, +.footable-details.table-bordered > tbody > tr > td, +.footable-details.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.footable.table-bordered > thead > tr > th, +.footable.table-bordered > thead > tr > td, +.footable-details.table-bordered > thead > tr > th, +.footable-details.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.footable.table-striped > tbody > tr:nth-child(odd), +.footable-details.table-striped > tbody > tr:nth-child(odd) { + background-color: #f9f9f9; +} +.footable.table-hover > tbody > tr:hover, +.footable-details.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +/* buttons */ +.footable .btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: 400; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-appearance: button; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; + overflow: visible; + text-transform: none; +} +.footable .btn:hover, +.footable .btn:focus, +.footable .btn.focus { + color: #333; + text-decoration: none; +} +.footable .btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.footable .btn-default:hover, +.footable .btn-default:focus, +.footable .btn-default.focus, +.footable .btn-default:active, +.footable .btn-default.active, +.footable .open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.footable .btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.footable .btn-primary:hover, +.footable .btn-primary:focus, +.footable .btn-primary.focus, +.footable .btn-primary:active, +.footable .btn-primary.active, +.footable .open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +/* caret */ +.footable .caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.footable .btn .caret { + margin-left: 0; +} +/* form-group */ +.form-group { + margin-bottom: 15px; +} +/* form-control */ +.footable .form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + margin: 0; + font-family: inherit; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +/* input-group */ +.footable .input-group { + position: relative; + display: table; + border-collapse: separate; +} +.footable .input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.footable .input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.footable .input-group-addon, +.footable .input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.footable .input-group-addon, +.footable .input-group-btn, +.footable .input-group .form-control { + display: table-cell; +} +.footable .input-group-btn > .btn + .btn, +.footable .input-group-btn:last-child > .btn, +.footable .input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.footable .input-group-btn > .btn { + position: relative; +} +.footable .input-group-btn > .btn:hover, +.footable .input-group-btn > .btn:focus, +.footable .input-group-btn > .btn:active { + z-index: 2; +} +.footable .input-group .form-control:first-child, +.footable .input-group-addon:first-child, +.footable .input-group-btn:first-child > .btn, +.footable .input-group-btn:first-child > .btn-group > .btn, +.footable .input-group-btn:first-child > .dropdown-toggle, +.footable .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.footable .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.footable .input-group .form-control:last-child, +.footable .input-group-addon:last-child, +.footable .input-group-btn:last-child > .btn, +.footable .input-group-btn:last-child > .btn-group > .btn, +.footable .input-group-btn:last-child > .dropdown-toggle, +.footable .input-group-btn:first-child > .btn:not(:first-child), +.footable .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +/* checkboxes & radios */ +.footable .radio, +.footable .checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.footable .radio label, +.footable .checkbox label { + max-width: 100%; + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + cursor: pointer; +} +.footable .radio input[type=radio], +.footable .radio-inline input[type=radio], +.footable .checkbox input[type=checkbox], +.footable .checkbox-inline input[type=checkbox] { + position: absolute; + margin: 4px 0 0 -20px; + line-height: normal; +} +/* dropdown-menu */ +.footable .dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.footable .open > .dropdown-menu { + display: block; +} +.footable .dropdown-menu-right { + right: 0; + left: auto; +} +.footable .dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.footable .dropdown-menu > li > a:hover, +.footable .dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +/* pagination */ +.footable .pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.footable .pagination > li { + display: inline; +} +.footable .pagination > li:first-child > a, +.footable .pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.footable .pagination > li > a, +.footable .pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.footable .pagination > li > a:hover, +.footable .pagination > li > span:hover, +.footable .pagination > li > a:focus, +.footable .pagination > li > span:focus { + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.footable .pagination > .active > a, +.footable .pagination > .active > span, +.footable .pagination > .active > a:hover, +.footable .pagination > .active > span:hover, +.footable .pagination > .active > a:focus, +.footable .pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.footable .pagination > .disabled > span, +.footable .pagination > .disabled > span:hover, +.footable .pagination > .disabled > span:focus, +.footable .pagination > .disabled > a, +.footable .pagination > .disabled > a:hover, +.footable .pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +/* labels */ +.footable .label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: 700; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +.footable .label-default { + background-color: #777; +} +/* wells */ +.footable-loader.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.05); + box-shadow: inset 0 1px 1px rgba(0,0,0,.05); +} +/* screen reader only */ +.footable .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; +} +/* form-inline */ +@media (min-width: 768px) { + .footable .form-inline .form-group { + display:inline-block; + margin-bottom: 0; + vertical-align: middle + } + + .footable .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + + .footable .form-inline .input-group { + display: inline-table; + vertical-align: middle + } + + .footable .form-inline .input-group .input-group-addon, + .footable .form-inline .input-group .input-group-btn, + .footable .form-inline .input-group .form-control { + width: auto + } + + .footable .form-inline .input-group>.form-control { + width: 100% + } +} +table.footable, +table.footable-details { + position: relative; + width: 100%; + border-spacing: 0; + border-collapse: collapse; +} +table.footable-details { + margin-bottom: 0; +} +table > tbody > tr > td > span.footable-toggle { + margin-right: 8px; + opacity: 0.3; +} +table > tbody > tr > td > span.footable-toggle.last-column { + margin-left: 8px; + float: right; +} +table.table-condensed > tbody > tr > td > span.footable-toggle { + margin-right: 5px; +} +table.footable-details > tbody > tr > th:nth-child(1) { + min-width: 40px; + width: 120px; +} +table.footable-details > tbody > tr > td:nth-child(2) { + word-break: break-all; +} + +table.footable-details > thead > tr:first-child > th, +table.footable-details > thead > tr:first-child > td, +table.footable-details > tbody > tr:first-child > th, +table.footable-details > tbody > tr:first-child > td, +table.footable-details > tfoot > tr:first-child > th, +table.footable-details > tfoot > tr:first-child > td { + border-top-width: 0; +} +table.footable-details.table-bordered > thead > tr:first-child > th, +table.footable-details.table-bordered > thead > tr:first-child > td, +table.footable-details.table-bordered > tbody > tr:first-child > th, +table.footable-details.table-bordered > tbody > tr:first-child > td, +table.footable-details.table-bordered > tfoot > tr:first-child > th, +table.footable-details.table-bordered > tfoot > tr:first-child > td { + border-top-width: 1px; +} + +div.footable-loader { + vertical-align: middle; + text-align: center; + height: 300px; + position: relative; +} +div.footable-loader > span.fooicon { + display: inline-block; + opacity: 0.3; + font-size: 30px; + line-height: 32px; + width: 32px; + height: 32px; + margin-top: -16px; + margin-left: -16px; + position: absolute; + top: 50%; + left: 50%; + -webkit-animation: fooicon-spin-r 2s infinite linear; + animation: fooicon-spin-r 2s infinite linear; +} +table.footable > tbody > tr.footable-empty > td { + vertical-align: middle; + text-align: center; + font-size: 30px; +} +table.footable > tbody > tr > td, +table.footable > tbody > tr > th { + display: none; +} +table.footable > tbody > tr.footable-empty > td, +table.footable > tbody > tr.footable-empty > th, +table.footable > tbody > tr.footable-detail-row > td, +table.footable > tbody > tr.footable-detail-row > th { + display: table-cell; +} +@-webkit-keyframes fooicon-spin-r { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +@keyframes fooicon-spin-r { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +.fooicon { + display: inline-block; + font-size: inherit; + font-family: FontAwesome !important; + font-style: normal; + font-weight: 400; + line-height: 1; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + transform: translate(0, 0); +} +.fooicon:before, +.fooicon:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.fooicon-loader:before { + content: "\f01e"; +} +.fooicon-plus:before { + content: "\f067"; +} +.fooicon-minus:before { + content: "\f068"; +} +.fooicon-search:before { + content: "\f002"; +} +.fooicon-remove:before { + content: "\f00d"; +} +.fooicon-sort:before { + content: "\f0dc"; +} +.fooicon-sort-asc:before { + content: "\f160"; +} +.fooicon-sort-desc:before { + content: "\f161"; +} +.fooicon-pencil:before { + content: "\f040"; +} +.fooicon-trash:before { + content: "\f1f8"; +} +.fooicon-eye-close:before { + content: "\f070"; +} +.fooicon-flash:before { + content: "\f0e7"; +} +.fooicon-cog:before { + content: "\f013"; +} +table.footable > thead > tr.footable-filtering > th { + border-bottom-width: 1px; + font-weight: normal; +} +table.footable > thead > tr.footable-filtering > th, +table.footable.footable-filtering-right > thead > tr.footable-filtering > th { + text-align: right; +} +table.footable.footable-filtering-left > thead > tr.footable-filtering > th { + text-align: left; +} +table.footable.footable-filtering-center > thead > tr.footable-filtering > th { + text-align: center; +} +table.footable > thead > tr.footable-filtering > th div.form-group { + margin-bottom: 0; +} +table.footable > thead > tr.footable-filtering > th div.form-group+div.form-group { + margin-top: 5px; +} +table.footable > thead > tr.footable-filtering > th div.input-group { + width: 100%; +} +table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox { + margin: 0; + display: block; + position: relative; +} +table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox > label { + display: block; + padding-left: 20px; +} +table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox input[type="checkbox"] { + position: absolute; + margin-left: -20px; +} +@media (min-width: 768px) { + table.footable > thead > tr.footable-filtering > th div.input-group { + width: auto; + } + table.footable > thead > tr.footable-filtering > th div.form-group { + margin-left: 2px; + margin-right: 2px; + } + table.footable > thead > tr.footable-filtering > th div.form-group+div.form-group { + margin-top: 0; + } +} +td.footable-sortable, +th.footable-sortable { + position: relative; + padding-right: 30px; + cursor: pointer; +} +td.footable-sortable > span.fooicon, +th.footable-sortable > span.fooicon { + position: absolute; + right: 6px; + top: 50%; + margin-top: -7px; + opacity: 0; + transition: opacity 0.3s ease-in; +} +td.footable-sortable:hover > span.fooicon, +th.footable-sortable:hover > span.fooicon { + opacity: 1; +} +td.footable-sortable.footable-asc > span.fooicon, +th.footable-sortable.footable-asc > span.fooicon, +td.footable-sortable.footable-desc > span.fooicon, +th.footable-sortable.footable-desc > span.fooicon { + opacity: 1; +} +table.footable > tfoot > tr.footable-paging > td > ul.pagination { + margin: 10px 0 0 0; +} +table.footable > tfoot > tr.footable-paging > td > span.label { + display: inline-block; + margin: 0 0 10px 0; + padding: 4px 10px; +} +table.footable > tfoot > tr.footable-paging > td, +table.footable-paging-center > tfoot > tr.footable-paging > td { + text-align: center; +} +table.footable-paging-left > tfoot > tr.footable-paging > td { + text-align: left; +} +table.footable-paging-right > tfoot > tr.footable-paging > td { + text-align: right; +} +ul.pagination > li.footable-page { + display: none; +} +ul.pagination > li.footable-page.visible { + display: inline; +} \ No newline at end of file diff --git a/media/footable-v3/css/footable.standalone.min.css b/media/footable-v3/css/footable.standalone.min.css new file mode 100644 index 0000000..615f08a --- /dev/null +++ b/media/footable-v3/css/footable.standalone.min.css @@ -0,0 +1 @@ +.footable .btn,.footable .caret{display:inline-block;vertical-align:middle}.footable-details.table,.footable-details.table *,.footable.table,.footable.table *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.footable-details.table th,.footable.table th{text-align:left}.footable-details.table,.footable.table{width:100%;max-width:100%;margin-bottom:20px}.footable-details.table>caption+thead>tr:first-child>td,.footable-details.table>caption+thead>tr:first-child>th,.footable-details.table>colgroup+thead>tr:first-child>td,.footable-details.table>colgroup+thead>tr:first-child>th,.footable-details.table>thead:first-child>tr:first-child>td,.footable-details.table>thead:first-child>tr:first-child>th,.footable.table>caption+thead>tr:first-child>td,.footable.table>caption+thead>tr:first-child>th,.footable.table>colgroup+thead>tr:first-child>td,.footable.table>colgroup+thead>tr:first-child>th,.footable.table>thead:first-child>tr:first-child>td,.footable.table>thead:first-child>tr:first-child>th{border-top:0}.footable-details.table>tbody>tr>td,.footable-details.table>tbody>tr>th,.footable-details.table>tfoot>tr>td,.footable-details.table>tfoot>tr>th,.footable-details.table>thead>tr>td,.footable-details.table>thead>tr>th,.footable.table>tbody>tr>td,.footable.table>tbody>tr>th,.footable.table>tfoot>tr>td,.footable.table>tfoot>tr>th,.footable.table>thead>tr>td,.footable.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.footable-details.table>thead>tr>td,.footable-details.table>thead>tr>th,.footable.table>thead>tr>td,.footable.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.footable-details.table-condensed>tbody>tr>td,.footable-details.table-condensed>tbody>tr>th,.footable-details.table-condensed>tfoot>tr>td,.footable-details.table-condensed>tfoot>tr>th,.footable-details.table-condensed>thead>tr>td,.footable-details.table-condensed>thead>tr>th,.footable.table-condensed>tbody>tr>td,.footable.table-condensed>tbody>tr>th,.footable.table-condensed>tfoot>tr>td,.footable.table-condensed>tfoot>tr>th,.footable.table-condensed>thead>tr>td,.footable.table-condensed>thead>tr>th{padding:5px}.footable .btn,.footable .form-control{padding:6px 12px;font-size:14px;background-image:none;line-height:1.42857143}.footable-details.table-bordered,.footable-details.table-bordered>tbody>tr>td,.footable-details.table-bordered>tbody>tr>th,.footable-details.table-bordered>tfoot>tr>td,.footable-details.table-bordered>tfoot>tr>th,.footable-details.table-bordered>thead>tr>td,.footable-details.table-bordered>thead>tr>th,.footable.table-bordered,.footable.table-bordered>tbody>tr>td,.footable.table-bordered>tbody>tr>th,.footable.table-bordered>tfoot>tr>td,.footable.table-bordered>tfoot>tr>th,.footable.table-bordered>thead>tr>td,.footable.table-bordered>thead>tr>th{border:1px solid #ddd}.footable-details.table-bordered>thead>tr>td,.footable-details.table-bordered>thead>tr>th,.footable.table-bordered>thead>tr>td,.footable.table-bordered>thead>tr>th{border-bottom-width:2px}.footable-details.table-striped>tbody>tr:nth-child(odd),.footable.table-striped>tbody>tr:nth-child(odd){background-color:#f9f9f9}.footable-details.table-hover>tbody>tr:hover,.footable.table-hover>tbody>tr:hover{background-color:#f5f5f5}.footable .btn{margin-bottom:0;font-weight:400;text-align:center;white-space:nowrap;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-appearance:button;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;border-radius:4px;overflow:visible;text-transform:none}.footable .btn.focus,.footable .btn:focus,.footable .btn:hover{color:#333;text-decoration:none}.footable .btn-default{color:#333;background-color:#fff;border-color:#ccc}.footable .btn-default.active,.footable .btn-default.focus,.footable .btn-default:active,.footable .btn-default:focus,.footable .btn-default:hover,.footable .open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.footable .btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.footable .btn-primary.active,.footable .btn-primary.focus,.footable .btn-primary:active,.footable .btn-primary:focus,.footable .btn-primary:hover,.footable .open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.footable .caret{width:0;height:0;margin-left:2px;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.footable .btn .caret{margin-left:0}.form-group{margin-bottom:15px}.footable .form-control{display:block;width:100%;height:34px;margin:0;font-family:inherit;color:#555;background-color:#fff;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.footable .input-group{position:relative;display:table;border-collapse:separate}.footable .input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.footable .input-group-btn{position:relative;font-size:0}.footable .input-group-addon,.footable .input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.footable .input-group .form-control,.footable .input-group-addon,.footable .input-group-btn{display:table-cell}.footable .input-group-btn:last-child>.btn,.footable .input-group-btn:last-child>.btn-group,.footable .input-group-btn>.btn+.btn{margin-left:-1px}.footable .input-group-btn>.btn{position:relative}.footable .input-group-btn>.btn:active,.footable .input-group-btn>.btn:focus,.footable .input-group-btn>.btn:hover{z-index:2}.footable .input-group .form-control:first-child,.footable .input-group-addon:first-child,.footable .input-group-btn:first-child>.btn,.footable .input-group-btn:first-child>.btn-group>.btn,.footable .input-group-btn:first-child>.dropdown-toggle,.footable .input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.footable .input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.footable .input-group .form-control:last-child,.footable .input-group-addon:last-child,.footable .input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.footable .input-group-btn:first-child>.btn:not(:first-child),.footable .input-group-btn:last-child>.btn,.footable .input-group-btn:last-child>.btn-group>.btn,.footable .input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.footable .checkbox,.footable .radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.footable .checkbox label,.footable .radio label{max-width:100%;min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.footable .checkbox input[type=checkbox],.footable .checkbox-inline input[type=checkbox],.footable .radio input[type=radio],.footable .radio-inline input[type=radio]{position:absolute;margin:4px 0 0 -20px;line-height:normal}.footable .dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.footable .open>.dropdown-menu{display:block}.footable .dropdown-menu-right{right:0;left:auto}.footable .dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.footable .dropdown-menu>li>a:focus,.footable .dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.footable .pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.footable .pagination>li{display:inline}.footable .pagination>li:first-child>a,.footable .pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.footable .pagination>li>a,.footable .pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.footable .pagination>li>a:focus,.footable .pagination>li>a:hover,.footable .pagination>li>span:focus,.footable .pagination>li>span:hover{color:#23527c;background-color:#eee;border-color:#ddd}.footable .pagination>.active>a,.footable .pagination>.active>a:focus,.footable .pagination>.active>a:hover,.footable .pagination>.active>span,.footable .pagination>.active>span:focus,.footable .pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.footable .pagination>.disabled>a,.footable .pagination>.disabled>a:focus,.footable .pagination>.disabled>a:hover,.footable .pagination>.disabled>span,.footable .pagination>.disabled>span:focus,.footable .pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.footable .label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.footable .label-default{background-color:#777}.footable-loader.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.footable .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}table.footable-details,table.footable>thead>tr.footable-filtering>th div.form-group{margin-bottom:0}@media (min-width:768px){.footable .form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.footable .form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.footable .form-inline .input-group{display:inline-table;vertical-align:middle}.footable .form-inline .input-group .form-control,.footable .form-inline .input-group .input-group-addon,.footable .form-inline .input-group .input-group-btn{width:auto}.footable .form-inline .input-group>.form-control{width:100%}}table.footable,table.footable-details{position:relative;width:100%;border-spacing:0;border-collapse:collapse}table>tbody>tr>td>span.footable-toggle{margin-right:8px;opacity:.3}table>tbody>tr>td>span.footable-toggle.last-column{margin-left:8px;float:right}table.table-condensed>tbody>tr>td>span.footable-toggle{margin-right:5px}table.footable-details>tbody>tr>th:nth-child(1){min-width:40px;width:120px}table.footable-details>tbody>tr>td:nth-child(2){word-break:break-all}table.footable-details>tbody>tr:first-child>td,table.footable-details>tbody>tr:first-child>th,table.footable-details>tfoot>tr:first-child>td,table.footable-details>tfoot>tr:first-child>th,table.footable-details>thead>tr:first-child>td,table.footable-details>thead>tr:first-child>th{border-top-width:0}table.footable-details.table-bordered>tbody>tr:first-child>td,table.footable-details.table-bordered>tbody>tr:first-child>th,table.footable-details.table-bordered>tfoot>tr:first-child>td,table.footable-details.table-bordered>tfoot>tr:first-child>th,table.footable-details.table-bordered>thead>tr:first-child>td,table.footable-details.table-bordered>thead>tr:first-child>th{border-top-width:1px}div.footable-loader{vertical-align:middle;text-align:center;height:300px;position:relative}div.footable-loader>span.fooicon{display:inline-block;opacity:.3;font-size:30px;line-height:32px;width:32px;height:32px;margin-top:-16px;margin-left:-16px;position:absolute;top:50%;left:50%;-webkit-animation:fooicon-spin-r 2s infinite linear;animation:fooicon-spin-r 2s infinite linear}table.footable>tbody>tr.footable-empty>td{vertical-align:middle;text-align:center;font-size:30px}table.footable>tbody>tr>td,table.footable>tbody>tr>th{display:none}table.footable>tbody>tr.footable-detail-row>td,table.footable>tbody>tr.footable-detail-row>th,table.footable>tbody>tr.footable-empty>td,table.footable>tbody>tr.footable-empty>th{display:table-cell}@-webkit-keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fooicon{display:inline-block;font-size:inherit;font-family:FontAwesome!important;font-style:normal;font-weight:400;line-height:1;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0,0)}.fooicon:after,.fooicon:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fooicon-loader:before{content:"\f01e"}.fooicon-plus:before{content:"\f067"}.fooicon-minus:before{content:"\f068"}.fooicon-search:before{content:"\f002"}.fooicon-remove:before{content:"\f00d"}.fooicon-sort:before{content:"\f0dc"}.fooicon-sort-asc:before{content:"\f160"}.fooicon-sort-desc:before{content:"\f161"}.fooicon-pencil:before{content:"\f040"}.fooicon-trash:before{content:"\f1f8"}.fooicon-eye-close:before{content:"\f070"}.fooicon-flash:before{content:"\f0e7"}.fooicon-cog:before{content:"\f013"}table.footable>thead>tr.footable-filtering>th{border-bottom-width:1px;font-weight:400}table.footable.footable-filtering-right>thead>tr.footable-filtering>th,table.footable>thead>tr.footable-filtering>th{text-align:right}table.footable.footable-filtering-left>thead>tr.footable-filtering>th{text-align:left}table.footable-paging-center>tfoot>tr.footable-paging>td,table.footable.footable-filtering-center>thead>tr.footable-filtering>th,table.footable>tfoot>tr.footable-paging>td{text-align:center}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:5px}table.footable>thead>tr.footable-filtering>th div.input-group{width:100%}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox{margin:0;display:block;position:relative}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox>label{display:block;padding-left:20px}table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox input[type=checkbox]{position:absolute;margin-left:-20px}@media (min-width:768px){table.footable>thead>tr.footable-filtering>th div.input-group{width:auto}table.footable>thead>tr.footable-filtering>th div.form-group{margin-left:2px;margin-right:2px}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:0}}td.footable-sortable,th.footable-sortable{position:relative;padding-right:30px;cursor:pointer}td.footable-sortable>span.fooicon,th.footable-sortable>span.fooicon{position:absolute;right:6px;top:50%;margin-top:-7px;opacity:0;transition:opacity .3s ease-in}td.footable-sortable.footable-asc>span.fooicon,td.footable-sortable.footable-desc>span.fooicon,td.footable-sortable:hover>span.fooicon,th.footable-sortable.footable-asc>span.fooicon,th.footable-sortable.footable-desc>span.fooicon,th.footable-sortable:hover>span.fooicon{opacity:1}table.footable>tfoot>tr.footable-paging>td>ul.pagination{margin:10px 0 0}table.footable>tfoot>tr.footable-paging>td>span.label{display:inline-block;margin:0 0 10px;padding:4px 10px}table.footable-paging-left>tfoot>tr.footable-paging>td{text-align:left}table.footable-paging-right>tfoot>tr.footable-paging>td{text-align:right}ul.pagination>li.footable-page{display:none}ul.pagination>li.footable-page.visible{display:inline} \ No newline at end of file diff --git a/media/footable-v3/css/index.html b/media/footable-v3/css/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/footable-v3/css/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/footable-v3/index.html b/media/footable-v3/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/footable-v3/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/footable-v3/js/footable.core.js b/media/footable-v3/js/footable.core.js new file mode 100644 index 0000000..7bab629 --- /dev/null +++ b/media/footable-v3/js/footable.core.js @@ -0,0 +1,3507 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +(function($, F){ + // add in console we use in case it's missing + window.console = window.console || { log:function(){}, error:function(){} }; + + /** + * The jQuery plugin initializer. + * @function jQuery.fn.footable + * @param {(object|FooTable.Defaults)} [options] - The options to initialize the plugin with. + * @param {function} [ready] - A callback function to execute for each initialized plugin. + * @returns {jQuery} + */ + $.fn.footable = function (options, ready) { + options = options || {}; + // make sure we only work with tables + return this.filter('table').each(function (i, tbl) { + F.init(tbl, options, ready); + }); + }; + + var debug_defaults = { + events: [] + }; + F.__debug__ = JSON.parse(localStorage.getItem('footable_debug')) || false; + F.__debug_options__ = JSON.parse(localStorage.getItem('footable_debug_options')) || debug_defaults; + + /** + * Gets or sets the internal debug variable which enables some additional logging to the console. + * When enabled this value is stored in the localStorage so it can persist across page reloads. + * @param {boolean} value - Whether or not to enable additional logging. + * @param {object} [options] - Any debug specific options. + * @returns {(boolean|undefined)} + */ + F.debug = function(value, options){ + if (!F.is.boolean(value)) return F.__debug__; + F.__debug__ = value; + if (F.__debug__){ + localStorage.setItem('footable_debug', JSON.stringify(F.__debug__)); + F.__debug_options__ = $.extend(true, {}, debug_defaults, options || {}); + if (F.is.hash(options)){ + localStorage.setItem('footable_debug_options', JSON.stringify(F.__debug_options__)); + } + } else { + localStorage.removeItem('footable_debug'); + localStorage.removeItem('footable_debug_options'); + } + }; + + /** + * Gets the FooTable instance of the supplied table if one exists. + * @param {(jQuery|jQuery.selector|HTMLTableElement)} table - The jQuery table object, selector or the HTMLTableElement to retrieve FooTable from. + * @returns {(FooTable.Table|undefined)} + */ + F.get = function(table){ + return $(table).first().data('__FooTable__'); + }; + + /** + * Initializes a new instance of FooTable on the supplied table. + * @param {(jQuery|jQuery.selector|HTMLTableElement)} table - The jQuery table object, selector or the HTMLTableElement to initialize FooTable on. + * @param {object} options - The options to initialize FooTable with. + * @param {function} [ready] - A callback function to execute once the plugin is initialized. + * @returns {FooTable.Table} + */ + F.init = function(table, options, ready){ + var ft = F.get(table); + if (ft instanceof F.Table) ft.destroy(); + return new F.Table(table, options, ready); + }; + + /** + * Gets the FooTable.Row instance for the supplied element. + * @param {(jQuery|jQuery.selector|HTMLTableElement)} element - A jQuery object, selector or the HTMLElement of an element to retrieve the FooTable.Row for. + * @returns {FooTable.Row} + */ + F.getRow = function(element){ + // to get the FooTable.Row object simply walk up the DOM, find the TR and grab the __FooTableRow__ data value + var $row = $(element).closest('tr'); + // if this is a detail row get the previous row in the table to get the main TR element + if ($row.hasClass('footable-detail-row')){ + $row = $row.prev(); + } + // grab the row object + return $row.data('__FooTableRow__'); + }; + + // The below are external type definitions mainly used as pointers to jQuery docs for important information + /** + * jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API + * that works across a multitude of browsers. With a combination of versatility and extensibility, jQuery has changed the way that millions of people write JavaScript. + * @name jQuery + * @constructor + * @returns {jQuery} + * @see {@link http://api.jquery.com/} + */ + + /** + * This object provides a subset of the methods of the Deferred object (then, done, fail, always, pipe, and state) to prevent users from changing the state of the Deferred. + * @typedef {object} jQuery.Promise + * @see {@link http://api.jquery.com/Types/#Promise} + */ + + /** + * As of jQuery 1.5, the Deferred object provides a way to register multiple callbacks into self-managed callback queues, invoke callback queues as appropriate, + * and relay the success or failure state of any synchronous or asynchronous function. + * @typedef {object} jQuery.Deferred + * @see {@link http://api.jquery.com/Types/#Deferred} + */ + + /** + * jQuery's event system normalizes the event object according to W3C standards. The event object is guaranteed to be passed to the event handler. Most properties from + * the original event are copied over and normalized to the new event object. + * @typedef {object} jQuery.Event + * @see {@link http://api.jquery.com/category/events/event-object/} + */ + + /** + * Provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events. + * @memberof jQuery + * @function when + * @param {...jQuery.Deferred} deferreds - Any number of deferred objects to wait for. + * @returns {jQuery.Promise} + * @see {@link http://api.jquery.com/jQuery.when/} + */ + + /** + * The jQuery.fn namespace used to register plugins with jQuery. + * @memberof jQuery + * @namespace fn + * @see {@link http://learn.jquery.com/plugins/basic-plugin-creation/} + */ +})( + jQuery, + /** + * The core FooTable namespace containing all the plugin code. + * @namespace + */ + FooTable = window.FooTable || {} +); +(function(F){ + var returnTrue = function(){ return true; }; + + /** + * This namespace contains commonly used array utility methods. + * @namespace {object} FooTable.arr + */ + F.arr = {}; + + /** + * Iterates over each item in the supplied array and performs the supplied function passing in the current item as the first argument. + * @memberof FooTable.arr + * @function each + * @param {Array} array - The array to iterate + * @param {function} func - The function to execute for each item. The first argument supplied to this function is the current item and the second is the current index. + */ + F.arr.each = function (array, func) { + if (!F.is.array(array) || !F.is.fn(func)) return; + for (var i = 0, len = array.length; i < len; i++) { + if (func(array[i], i) === false) break; + } + }; + + /** + * Get all items in the supplied array that optionally matches the supplied where function. If no items are found an empty array is returned. + * @memberof FooTable.arr + * @function get + * @param {Array} array - The array to get items from. + * @param {function} where - This function must return a boolean value, true includes the item in the result array. + * @returns {Array} + */ + F.arr.get = function (array, where) { + var result = []; + if (!F.is.array(array)) return result; + if (!F.is.fn(where)) return array; + for (var i = 0, len = array.length; i < len; i++) { + if (where(array[i], i)) result.push(array[i]); + } + return result; + }; + + /** + * Get a boolean value indicating if any item exists in the supplied array that optionally matches the supplied where function. + * @memberof FooTable.arr + * @function any + * @param {Array} array - The array to check. + * @param {function} [where] - [Optional] This function must return a boolean value, true indicates that the current item is a valid match. + * @returns {boolean} + */ + F.arr.any = function (array, where) { + if (!F.is.array(array)) return false; + where = F.is.fn(where) ? where : returnTrue; + for (var i = 0, len = array.length; i < len; i++) { + if (where(array[i], i)) return true; + } + return false; + }; + + /** + * Checks if the supplied value exists in the array. + * @memberof FooTable.arr + * @function contains + * @param {Array} array - The array to check. + * @param {*} value - The value to check for. + * @returns {boolean} + */ + F.arr.contains = function(array, value){ + if (!F.is.array(array) || F.is.undef(value)) return false; + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] == value) return true; + } + return false; + }; + + /** + * Get the first item in the supplied array that optionally matches the supplied where function. If no item is found null is returned. + * @memberof FooTable.arr + * @function first + * @param {Array} array - The array to get the item from. + * @param {function} [where] - [Optional] This function must return a boolean value, true indicates that the current item can be returned. + * @returns {(*|null)} + */ + F.arr.first = function (array, where) { + if (!F.is.array(array)) return null; + where = F.is.fn(where) ? where : returnTrue; + for (var i = 0, len = array.length; i < len; i++) { + if (where(array[i], i)) return array[i]; + } + return null; + }; + + /** + * Creates a new array from the results of the supplied getter function. If no items are found an empty array is returned, to exclude an item from the results return null. + * @memberof FooTable.arr + * @function map + * @param {Array} array - The array to iterate. + * @param {function} getter - This function must return either a new value or null. + * The first argument is the result being returned at this point in the iteration. The second argument is the current item being iterated. + * @returns {(*|null)} + */ + F.arr.map = function (array, getter) { + var result = [], returned = null; + if (!F.is.array(array) || !F.is.fn(getter)) return result; + for (var i = 0, len = array.length; i < len; i++) { + if ((returned = getter(array[i], i)) != null) result.push(returned); + } + return result; + }; + + /** + * Removes items from the array matching the supplied where function. All removed items are returned in a new array. + * @memberof FooTable.arr + * @function remove + * @param {Array} array - The array to iterate and remove items from. + * @param {function} where - This function must return a boolean value, true includes the item in the result array. + * @returns {*} + */ + F.arr.remove = function (array, where) { + var remove = [], removed = []; + if (!F.is.array(array) || !F.is.fn(where)) return removed; + var i = 0, len = array.length; + for (; i < len; i++) { + if (where(array[i], i, removed)){ + remove.push(i); + removed.push(array[i]); + } + } + // sort the indexes to be removed from largest to smallest + remove.sort(function(a, b){ return b - a; }); + i = 0; len = remove.length; + for(; i < len; i++){ + var index = remove[i] - i; + array.splice(index, 1); + } + return removed; + }; + + /** + * Deletes a single item from the array. The item if removed is returned. + * @memberof FooTable.arr + * @function delete + * @param {Array} array - The array to iterate and delete the item from. + * @param {*} item - The item to find and delete. + * @returns {(*|null)} + */ + F.arr.delete = function(array, item){ + var remove = -1, removed = null; + if (!F.is.array(array) || F.is.undef(item)) return removed; + var i = 0, len = array.length; + for (; i < len; i++) { + if (array[i] == item){ + remove = i; + removed = array[i]; + break; + } + } + if (remove != -1) array.splice(remove, 1); + return removed; + }; + + /** + * Replaces a single item in the array with a new one. + * @memberof FooTable.arr + * @function replace + * @param {Array} array - The array to iterate and replace the item in. + * @param {*} oldItem - The item to be replaced. + * @param {*} newItem - The item to be inserted. + */ + F.arr.replace = function(array, oldItem, newItem){ + var index = array.indexOf(oldItem); + if (index !== -1) array[index] = newItem; + }; + +})(FooTable); +(function (F) { + + /** + * This namespace contains commonly used 'is' type methods that return boolean values. + * @namespace FooTable.is + */ + F.is = {}; + + /** + * Checks if the type of the value is the same as that supplied. + * @memberof FooTable.is + * @function type + * @param {*} value - The value to check the type of. + * @param {string} type - The type to check for. + * @returns {boolean} + */ + F.is.type = function (value, type) { + return typeof value === type; + }; + + /** + * Checks if the value is defined. + * @memberof FooTable.is + * @function defined + * @param {*} value - The value to check is defined. + * @returns {boolean} + */ + F.is.defined = function (value) { + return typeof value !== 'undefined'; + }; + + /** + * Checks if the value is undefined. + * @memberof FooTable.is + * @function undef + * @param {*} value - The value to check is undefined. + * @returns {boolean} + */ + F.is.undef = function (value) { + return typeof value === 'undefined'; + }; + + /** + * Checks if the value is an array. + * @memberof FooTable.is + * @function array + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.array = function (value) { + return '[object Array]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is a date. + * @memberof FooTable.is + * @function date + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.date = function (value) { + return '[object Date]' === Object.prototype.toString.call(value) && !isNaN(value.getTime()); + }; + + /** + * Checks if the value is a boolean. + * @memberof FooTable.is + * @function boolean + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.boolean = function (value) { + return '[object Boolean]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is a string. + * @memberof FooTable.is + * @function string + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.string = function (value) { + return '[object String]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is a number. + * @memberof FooTable.is + * @function number + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.number = function (value) { + return '[object Number]' === Object.prototype.toString.call(value) && !isNaN(value); + }; + + /** + * Checks if the value is a function. + * @memberof FooTable.is + * @function fn + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.fn = function (value) { + return (F.is.defined(window) && value === window.alert) || '[object Function]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is an error. + * @memberof FooTable.is + * @function error + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.error = function (value) { + return '[object Error]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is an object. + * @memberof FooTable.is + * @function object + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.object = function (value) { + return '[object Object]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is a hash. + * @memberof FooTable.is + * @function hash + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.hash = function (value) { + return F.is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval; + }; + + /** + * Checks if the supplied object is an HTMLElement + * @memberof FooTable.is + * @function element + * @param {object} obj - The object to check. + * @returns {boolean} + */ + F.is.element = function (obj) { + return typeof HTMLElement === 'object' + ? obj instanceof HTMLElement + : obj && typeof obj === 'object' && obj !== null && obj.nodeType === 1 && typeof obj.nodeName === 'string'; + }; + + /** + * This is a simple check to determine if an object is a jQuery promise object. It simply checks the object has a "then" and "promise" function defined. + * The promise object is created as an object literal inside of jQuery.Deferred. + * It has no prototype, nor any other truly unique properties that could be used to distinguish it. + * This method should be a little more accurate than the internal jQuery one that simply checks for a "promise" method. + * @memberof FooTable.is + * @function promise + * @param {object} obj - The object to check. + * @returns {boolean} + */ + F.is.promise = function(obj){ + return F.is.object(obj) && F.is.fn(obj.then) && F.is.fn(obj.promise); + }; + + /** + * Checks if the supplied object is an instance of a jQuery object. + * @memberof FooTable.is + * @function jq + * @param {object} obj - The object to check. + * @returns {boolean} + */ + F.is.jq = function(obj){ + return F.is.defined(window.jQuery) && obj instanceof jQuery && obj.length > 0; + }; + + /** + * Checks if the supplied object is a moment.js date object. + * @memberof FooTable.is + * @function moment + * @param {object} obj - The object to check. + * @returns {boolean} + */ + F.is.moment = function(obj){ + return F.is.defined(window.moment) && F.is.object(obj) && F.is.boolean(obj._isAMomentObject) + }; + + /** + * Checks if the supplied value is an object and if it is empty. + * @memberof FooTable.is + * @function emptyObject + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.emptyObject = function(value){ + if (!F.is.hash(value)) return false; + for(var prop in value) { + if(value.hasOwnProperty(prop)) + return false; + } + return true; + }; + + /** + * Checks if the supplied value is an array and if it is empty. + * @memberof FooTable.is + * @function emptyArray + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.emptyArray = function(value){ + return F.is.array(value) ? value.length === 0 : true; + }; + + /** + * Checks if the supplied value is a string and if it is empty. + * @memberof FooTable.is + * @function emptyString + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.emptyString = function(value){ + return F.is.string(value) ? value.length === 0 : true; + }; + +})(FooTable); +(function (F) { + /** + * This namespace contains commonly used string utility methods. + * @namespace FooTable.str + */ + F.str = {}; + + /** + * Checks if the supplied string contains the given substring. + * @memberof FooTable.str + * @function contains + * @param {string} str - The string to check. + * @param {string} contains - The string to check for. + * @param {boolean} [ignoreCase=false] - Whether or not to ignore casing when performing the check. + * @returns {boolean} + */ + F.str.contains = function (str, contains, ignoreCase) { + if (F.is.emptyString(str) || F.is.emptyString(contains)) return false; + return contains.length <= str.length + && (ignoreCase ? str.toUpperCase().indexOf(contains.toUpperCase()) : str.indexOf(contains)) !== -1; + }; + + /** + * Checks if the supplied string contains the exact given substring. + * @memberof FooTable.str + * @function contains + * @param {string} str - The string to check. + * @param {string} contains - The string to check for. + * @param {boolean} [ignoreCase=false] - Whether or not to ignore casing when performing the check. + * @returns {boolean} + */ + F.str.containsExact = function (str, contains, ignoreCase) { + if (F.is.emptyString(str) || F.is.emptyString(contains) || contains.length > str.length) return false; + return new RegExp('\\b'+ F.str.escapeRegExp(contains)+'\\b', ignoreCase ? 'i' : '').test(str); + }; + + /** + * Checks if the supplied string contains the given word. + * @memberof FooTable.str + * @function containsWord + * @param {string} str - The string to check. + * @param {string} word - The word to check for. + * @param {boolean} [ignoreCase=false] - Whether or not to ignore casing when performing the check. + * @returns {boolean} + */ + F.str.containsWord = function(str, word, ignoreCase){ + if (F.is.emptyString(str) || F.is.emptyString(word) || str.length < word.length) + return false; + var parts = str.split(/\W/); + for (var i = 0, len = parts.length; i < len; i++){ + if (ignoreCase ? parts[i].toUpperCase() == word.toUpperCase() : parts[i] == word) return true; + } + return false; + }; + + /** + * Returns the remainder of a string split on the first index of the given substring. + * @memberof FooTable.str + * @function from + * @param {string} str - The string to split. + * @param {string} from - The substring to split on. + * @returns {string} + */ + F.str.from = function (str, from) { + if (F.is.emptyString(str)) return str; + return F.str.contains(str, from) ? str.substring(str.indexOf(from) + 1) : str; + }; + + /** + * Checks if a string starts with the supplied prefix. + * @memberof FooTable.str + * @function startsWith + * @param {string} str - The string to check. + * @param {string} prefix - The prefix to check for. + * @returns {boolean} + */ + F.str.startsWith = function (str, prefix) { + if (F.is.emptyString(str)) return str == prefix; + return str.slice(0, prefix.length) == prefix; + }; + + /** + * Takes the supplied string and converts it to camel case. + * @memberof FooTable.str + * @function toCamelCase + * @param {string} str - The string to camel case. + * @returns {string} + */ + F.str.toCamelCase = function (str) { + if (F.is.emptyString(str)) return str; + if (str.toUpperCase() === str) return str.toLowerCase(); + return str.replace(/^([A-Z])|[-\s_](\w)/g, function (match, p1, p2) { + if (F.is.string(p2)) return p2.toUpperCase(); + return p1.toLowerCase(); + }); + }; + + /** + * Generates a random string 9 characters long using the optional prefix if supplied. + * @memberof FooTable.str + * @function random + * @param {string} [prefix] - The prefix to append to the 9 random characters. + * @returns {string} + */ + F.str.random = function(prefix){ + prefix = F.is.emptyString(prefix) ? '' : prefix; + return prefix + Math.random().toString(36).substr(2, 9); + }; + + /** + * Escapes a string for use in a regular expression. + * @memberof FooTable.str + * @function escapeRegExp + * @param {string} str - The string to escape. + * @returns {string} + */ + F.str.escapeRegExp = function(str){ + if (F.is.emptyString(str)) return str; + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + }; + +})(FooTable); +(function (F) { + "use strict"; + + if (!Object.create) { + Object.create = (function () { + var Object = function () {}; + return function (prototype) { + if (arguments.length > 1) + throw Error('Second argument not supported'); + + if (!F.is.object(prototype)) + throw TypeError('Argument must be an object'); + + Object.prototype = prototype; + var result = new Object(); + Object.prototype = null; + return result; + }; + })(); + } + + /** + * This base implementation does nothing except provide access to the {@link FooTable.Class#extend} method. + * @constructs FooTable.Class + * @classdesc This class is based off of John Resig's [Simple JavaScript Inheritance]{@link http://ejohn.org/blog/simple-javascript-inheritance} but it has been updated to be ES 5.1 + * compatible by implementing an [Object.create polyfill]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill} + * for older browsers. + * @see {@link http://ejohn.org/blog/simple-javascript-inheritance} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill} + * @returns {FooTable.Class} + */ + function Class() {} + + var __extendable__ = /xyz/.test(function () {xyz;}) ? /\b_super\b/ : /.*/; + + // this._super() within the context of the new function is a pointer to the original function + // except if the hook param is specified then the this._super variable is the result of the original function + Class.__extend__ = function(proto, name, func, original){ + // to all who venture here, here be dragons! + proto[name] = F.is.fn(original) && __extendable__.test(func) ? + (function (name, fn) { + return function () { + var tmp, ret; + tmp = this._super; + this._super = original; + ret = fn.apply(this, arguments); + this._super = tmp; + return ret; + }; + })(name, func) : func; + }; + + /** + * Creates a new class that inherits from this class which in turn allows itself to be extended or if a name and function is supplied extends only that specific function on the class. + * @param {(object|string)} arg1 - An object containing any new methods/members to implement or the name of the method to extend. + * @param {function} arg2 - If the first argument is a method name then this is the new function to replace it with. + * @returns {FooTable.Class} A new class that inherits from the base class. + * @example The below shows an example of how to implement inheritance using this method. + * var Person = FooTable.Class.extend({ + * construct: function(isDancing){ + * this.dancing = isDancing; + * }, + * dance: function(){ + * return this.dancing; + * } + * }); + * + * var Ninja = Person.extend({ + * construct: function(){ + * this._super( false ); + * }, + * dance: function(){ + * // Call the inherited version of dance() + * return this._super(); + * }, + * swingSword: function(){ + * return true; + * } + * }); + * + * var p = new Person(true); + * p.dance(); // => true + * + * var n = new Ninja(); + * n.dance(); // => false + * n.swingSword(); // => true + * + * // Should all be true + * p instanceof Person && p instanceof FooTable.Class && + * n instanceof Ninja && n instanceof Person && n instanceof FooTable.Class + */ + Class.extend = function (arg1 , arg2) { + var args = Array.prototype.slice.call(arguments); + arg1 = args.shift(); + arg2 = args.shift(); + + function __extend__(proto, name, func, original){ + // to all who venture here, here be dragons! + proto[name] = F.is.fn(original) && __extendable__.test(func) ? + (function (name, fn, ofn) { + return function () { + var tmp, ret; + tmp = this._super; + this._super = ofn; + ret = fn.apply(this, arguments); + this._super = tmp; + return ret; + }; + })(name, func, original) : func; + } + + if (F.is.hash(arg1)){ + var proto = Object.create(this.prototype), + _super = this.prototype; + for (var name in arg1) { + if (name === '__ctor__') continue; + __extend__(proto, name, arg1[name], _super[name]); + } + var obj = F.is.fn(proto.__ctor__) ? proto.__ctor__ : function () { + if (!F.is.fn(this.construct)) + throw new SyntaxError('FooTable class objects must be constructed with the "new" keyword.'); + this.construct.apply(this, arguments); + }; + proto.construct = F.is.fn(proto.construct) ? proto.construct : function(){}; + obj.prototype = proto; + proto.constructor = obj; + obj.extend = Class.extend; + return obj; + } else if (F.is.string(arg1) && F.is.fn(arg2)) { + __extend__(this.prototype, arg1, arg2, this.prototype[arg1]); + } + }; + + F.Class = Class; + + F.ClassFactory = F.Class.extend(/** @lends FooTable.ClassFactory */{ + /** + * This is a simple factory for {@link FooTable.Class} objects allowing them to be registered using a friendly name + * and then new instances can be created using this friendly name. + * @constructs + * @extends FooTable.Class + * @returns {FooTable.ClassFactory} + * @this FooTable.ClassFactory + */ + construct: function(){ + /** + * An object containing all registered classes. + * @type {{}} + */ + this.registered = {}; + }, + /** + * Checks if the factory contains a class registered using the supplied name. + * @instance + * @param {string} name - The name of the class to check. + * @returns {boolean} + * @this FooTable.ClassFactory + */ + contains: function(name){ + return F.is.defined(this.registered[name]); + }, + /** + * Gets an array of all registered names. + * @instance + * @returns {Array.} + * @this FooTable.ClassFactory + */ + names: function(){ + var names = [], name; + for (name in this.registered){ + if (!this.registered.hasOwnProperty(name)) continue; + names.push(name); + } + return names; + }, + /** + * Registers a class object using the supplied friendly name and priority. The priority is only taken into account when loading all registered classes + * using the {@link FooTable.ClassFactory#load} method. + * @instance + * @param {string} name - The friendly name of the class. + * @param {function} klass - The class to register. + * @param {number} priority - This determines the order that the class is created when using the {@link FooTable.ClassFactory#load} method, higher values are loaded first. + * @this FooTable.ClassFactory + */ + register: function(name, klass, priority){ + if (!F.is.string(name) || !F.is.fn(klass)) return; + var current = this.registered[name]; + this.registered[name] = { + name: name, + klass: klass, + priority: F.is.number(priority) ? priority : (F.is.defined(current) ? current.priority : 0) + }; + }, + /** + * Creates new instances of all registered classes using there priority and the supplied arguments to return them in an array. + * @instance + * @param {object} subs - An object containing classes to substitute on load. + * @param {*} arg1 - The first argument to supply when creating new instances of all registered classes. + * @param {*} [argN...] - Any number of additional arguments to supply when creating new instances of all registered classes. + * @returns {Array.} + * @this FooTable.ClassFactory + */ + load: function(subs, arg1, argN){ + var self = this, args = Array.prototype.slice.call(arguments), reg = [], loaded = [], name, klass; + subs = args.shift() || {}; + for (name in self.registered){ + if (!self.registered.hasOwnProperty(name)) continue; + var component = self.registered[name]; + if (subs.hasOwnProperty(name)){ + klass = subs[name]; + if (F.is.string(klass)) klass = F.getFnPointer(subs[name]); + if (F.is.fn(klass)){ + component = {name: name, klass: klass, priority: self.registered[name].priority}; + } + } + reg.push(component); + } + for (name in subs){ + if (!subs.hasOwnProperty(name) || self.registered.hasOwnProperty(name)) continue; + klass = subs[name]; + if (F.is.string(klass)) klass = F.getFnPointer(subs[name]); + if (F.is.fn(klass)){ + reg.push({name: name, klass: klass, priority: 0}); + } + } + reg.sort(function(a, b){ return b.priority - a.priority; }); + F.arr.each(reg, function(r){ + if (F.is.fn(r.klass)){ + loaded.push(self._make(r.klass, args)); + } + }); + return loaded; + }, + /** + * Create a new instance of a single class using the supplied name and arguments. + * @instance + * @param {string} name - The name of the class to create. + * @param {*} arg1 - The first argument to supply to the new instance. + * @param {*} [argN...] - Any number of additional arguments to supply to the new instance. + * @returns {FooTable.Class} + * @this FooTable.ClassFactory + */ + make: function(name, arg1, argN){ + var self = this, args = Array.prototype.slice.call(arguments), reg; + name = args.shift(); + reg = self.registered[name]; + if (F.is.fn(reg.klass)){ + return self._make(reg.klass, args); + } + return null; + }, + /** + * This in effect lets us use the "apply" method on a function using the "new" keyword. + * @instance + * @private + * @param {function} klass + * @param args + * @returns {FooTable.Class} + * @this FooTable.ClassFactory + */ + _make: function(klass, args){ + function Class() { + return klass.apply(this, args); + } + Class.prototype = klass.prototype; + return new Class(); + } + }); + +})(FooTable); +(function($, F){ + + /** + * Converts the supplied cssText string into JSON object. + * @param {string} cssText - The cssText to convert to a JSON object. + * @returns {object} + */ + F.css2json = function(cssText){ + if (F.is.emptyString(cssText)) return {}; + var json = {}, props = cssText.split(';'), pair, key, value; + for (var i = 0, i_len = props.length; i < i_len; i++){ + if (F.is.emptyString(props[i])) continue; + pair = props[i].split(':'); + if (F.is.emptyString(pair[0]) || F.is.emptyString(pair[1])) continue; + key = F.str.toCamelCase($.trim(pair[0])); + value = $.trim(pair[1]); + json[key] = value; + } + return json; + }; + + /** + * Attempts to retrieve a function pointer using the given name. + * @param {string} functionName - The name of the function to fetch a pointer to. + * @returns {(function|object|null)} + */ + F.getFnPointer = function(functionName){ + if (F.is.emptyString(functionName)) return null; + var pointer = window, + parts = functionName.split('.'); + F.arr.each(parts, function(part){ + if (pointer[part]) pointer = pointer[part]; + }); + return F.is.fn(pointer) ? pointer : null; + }; + + /** + * Checks the value for function properties such as the {@link FooTable.Column#formatter} option which could also be specified using just the name + * and attempts to return the correct function pointer or null if none was found matching the value. + * @param {FooTable.Class} self - The class to use as the 'this' keyword within the context of the function. + * @param {(function|string)} value - The actual function or the name of the function for the property. + * @param {function} [def] - A default function to return if none is found. + * @returns {(function|null)} + */ + F.checkFnValue = function(self, value, def){ + def = F.is.fn(def) ? def : null; + function wrap(t, fn, d){ + if (!F.is.fn(fn)) return d; + return function(){ + return fn.apply(t, arguments); + }; + } + return F.is.fn(value) ? wrap(self, value, def) : (F.is.type(value, 'string') ? wrap(self, F.getFnPointer(value), def) : def); + }; + +})(jQuery, FooTable); +(function($, F){ + + F.Cell = F.Class.extend(/** @lends FooTable.Cell */{ + /** + * The cell class containing all the properties for cells. + * @constructs + * @extends FooTable.Class + * @param {FooTable.Table} table - The root {@link FooTable.Table} this cell belongs to. + * @param {FooTable.Row} row - The parent {@link FooTable.Row} this cell belongs to. + * @param {FooTable.Column} column - The {@link FooTable.Column} this cell falls under. + * @param {(*|HTMLElement|jQuery)} valueOrElement - Either the value or the element for the cell. + * @returns {FooTable.Cell} + * @this FooTable.Cell + */ + construct: function (table, row, column, valueOrElement) { + /** + * The root {@link FooTable.Table} for the cell. + * @instance + * @readonly + * @type {FooTable.Table} + */ + this.ft = table; + /** + * The parent {@link FooTable.Row} for the cell. + * @instance + * @readonly + * @type {FooTable.Row} + */ + this.row = row; + /** + * The {@link FooTable.Column} this cell falls under. + * @instance + * @readonly + * @type {FooTable.Column} + */ + this.column = column; + this.created = false; + this.define(valueOrElement); + }, + /** + * This is supplied either the value or the cell element/jQuery object if it exists. + * If supplied the element we need set the $el property and parse the value from it. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or element to define the cell. + * @this FooTable.Cell + */ + define: function(valueOrElement){ + /** + * The jQuery table cell object this instance wraps. + * @instance + * @type {jQuery} + */ + this.$el = F.is.element(valueOrElement) || F.is.jq(valueOrElement) ? $(valueOrElement) : null; + /** + * The jQuery row object that represents this cell in the details table. + * @type {jQuery} + */ + this.$detail = null; + + var hasOptions = F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options) && F.is.defined(valueOrElement.value); + + /** + * The value of the cell. + * @instance + * @type {*} + */ + this.value = this.column.parser.call(this.column, F.is.jq(this.$el) ? this.$el : (hasOptions ? valueOrElement.value : valueOrElement), this.ft.o); + + /** + * Contains any options for the cell. These are the options supplied through the plugin constructor as part of the row object itself. + * @type {object} + */ + this.o = $.extend(true, { + classes: null, + style: null + }, hasOptions ? valueOrElement.options : {}); + /** + * An array of CSS classes for the cell. + * @instance + * @protected + * @type {Array.} + */ + this.classes = F.is.jq(this.$el) && this.$el.attr('class') ? this.$el.attr('class').match(/\S+/g) : (F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : [])); + /** + * The inline styles for the cell. + * @instance + * @protected + * @type {object} + */ + this.style = F.is.jq(this.$el) && this.$el.attr('style') ? F.css2json(this.$el.attr('style')) : (F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {})); + }, + /** + * After the cell has been defined this ensures that the $el and #detail properties are jQuery objects by either creating or updating them. + * @instance + * @protected + * @this FooTable.Cell + */ + $create: function(){ + if (this.created) return; + (this.$el = F.is.jq(this.$el) ? this.$el : $('')) + .data('value', this.value) + .contents().detach().end() + .append(this.format(this.value)); + + this._setClasses(this.$el); + this._setStyle(this.$el); + + this.$detail = $('').addClass(this.row.classes.join(' ')) + .data('__FooTableCell__', this) + .append($('')) + .append($('')); + + this.created = true; + }, + /** + * Collapses this cell and displays it in the details row. + * @instance + * @protected + */ + collapse: function(){ + if (!this.created) return; + this.$detail.children('th').html(this.column.title); + this.$el.clone() + .attr('id', this.$el.attr('id') ? this.$el.attr('id') + '-detail' : undefined) + .css('display', 'table-cell') + .html('') + .append(this.$el.contents().detach()) + .replaceAll(this.$detail.children('td').first()); + + if (!F.is.jq(this.$detail.parent())) + this.$detail.appendTo(this.row.$details.find('.footable-details > tbody')); + }, + /** + * Restores this cell from a detail row back into the normal row. + * @instance + * @protected + */ + restore: function(){ + if (!this.created) return; + if (F.is.jq(this.$detail.parent())){ + var $cell = this.$detail.children('td').first(); + this.$el + .attr('class', $cell.attr('class')) + .attr('style', $cell.attr('style')) + .css('display', (this.column.hidden || !this.column.visible) ? 'none' : 'table-cell') + .append($cell.contents().detach()); + } + this.$detail.detach(); + }, + /** + * Helper method to call this cell's column parser function supplying the required parameters. + * @instance + * @protected + * @returns {*} + * @see FooTable.Column#parser + * @this FooTable.Cell + */ + parse: function(){ + return this.column.parser.call(this.column, this.$el, this.ft.o); + }, + /** + * Helper method to call this cell's column formatter function using the supplied value and any additional required parameters. + * @instance + * @protected + * @param {*} value - The value to format. + * @returns {(string|HTMLElement|jQuery)} + * @see FooTable.Column#formatter + * @this FooTable.Cell + */ + format: function(value){ + return this.column.formatter.call(this.column, value, this.ft.o); + }, + /** + * Allows easy access to getting or setting the cell's value. If the value is set all associated properties are also updated along with the actual element. + * Using this method also allows us to supply an object containing options and the value for the cell. + * @instance + * @param {*} [value] - The value to set for the cell. If not supplied the current value of the cell is returned. + * @param {boolean} [redraw=true] - Whether or not to redraw the row once the value has been set. + * @returns {(*|undefined)} + * @this FooTable.Cell + */ + val: function(value, redraw){ + if (F.is.undef(value)){ + // get + return this.value; + } + // set + var self = this, hasOptions = F.is.hash(value) && F.is.hash(value.options) && F.is.defined(value.value); + this.o = $.extend(true, { + classes: self.classes, + style: self.style + }, hasOptions ? value.options : {}); + + this.value = hasOptions ? value.value : value; + this.classes = F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : []); + this.style = F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {}); + + if (this.created){ + this.$el.data('value', this.value).empty(); + + var $detail = this.$detail.children('td').first().empty(), + $target = F.is.jq(this.$detail.parent()) ? $detail : this.$el; + + $target.append(this.format(this.value)); + + this._setClasses($target); + this._setStyle($target); + + if (F.is.boolean(redraw) ? redraw : true) this.row.draw(); + } + }, + _setClasses: function($el){ + var hasColClasses = !F.is.emptyArray(this.column.classes), + hasClasses = !F.is.emptyArray(this.classes), + classes = null; + $el.removeAttr('class'); + if (!hasColClasses && !hasClasses) return; + if (hasColClasses && hasClasses){ + classes = this.classes.concat(this.column.classes).join(' '); + } else if (hasColClasses) { + classes = this.column.classes.join(' '); + } else if (hasClasses){ + classes = this.classes.join(' '); + } + if (!F.is.emptyString(classes)){ + $el.addClass(classes); + } + }, + _setStyle: function($el){ + var hasColStyle = !F.is.emptyObject(this.column.style), + hasStyle = !F.is.emptyObject(this.style), + style = null; + $el.removeAttr('style'); + if (!hasColStyle && !hasStyle) return; + if (hasColStyle && hasStyle){ + style = $.extend({}, this.column.style, this.style); + } else if (hasColStyle) { + style = this.column.style; + } else if (hasStyle){ + style = this.style; + } + if (F.is.hash(style)){ + $el.css(style); + } + } + }); + +})(jQuery, FooTable); +(function($, F){ + + F.Column = F.Class.extend(/** @lends FooTable.Column */{ + /** + * The column class containing all the properties for columns. All members marked as "readonly" should not be used when defining {@link FooTable.Defaults#columns}. + * @constructs + * @extends FooTable.Class + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this component belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + * @param {string} [type] - The type of column, "text" by default. + * @returns {FooTable.Column} + * @this FooTable.Column + */ + construct: function(instance, definition, type){ + /** + * The root {@link FooTable.Table} for the column. + * @instance + * @readonly + * @type {FooTable.Table} + */ + this.ft = instance; + /** + * The type of data displayed by the column. + * @instance + * @readonly + * @type {string} + */ + this.type = F.is.emptyString(type) ? 'text' : type; + /** + * Whether or not the column was parsed from a standard table row containing data instead of from an actual header row. + * @instance + * @readonly + * @type {boolean} + */ + this.virtual = F.is.boolean(definition.virtual) ? definition.virtual : false; + /** + * The jQuery cell object for the column header. + * @instance + * @readonly + * @type {jQuery} + */ + this.$el = F.is.jq(definition.$el) ? definition.$el : null; + /** + * The index of the column in the table. This is set by the plugin during initialization. + * @instance + * @readonly + * @type {number} + * @default -1 + */ + this.index = F.is.number(definition.index) ? definition.index : -1; + this.define(definition); + this.$create(); + }, + /** + * This is supplied the column definition in the form of a simple object created by merging options supplied via the plugin constructor with those parsed from the DOM. + * @instance + * @protected + * @param {object} definition - The object containing the column definition. + * @this FooTable.Column + */ + define: function(definition){ + /** + * Whether or not this column is hidden from view and appears in the details row. + * @type {boolean} + * @default false + */ + this.hidden = F.is.boolean(definition.hidden) ? definition.hidden : false; + /** + * Whether or not this column is completely hidden from view and will not appear in the details row. + * @type {boolean} + * @default true + */ + this.visible = F.is.boolean(definition.visible) ? definition.visible : true; + + /** + * The name of the column. This name must correspond to the property name of the JSON row data. + * @type {string} + * @default null + */ + this.name = F.is.string(definition.name) ? definition.name : null; + if (this.name == null) this.name = 'col'+(definition.index+1); + /** + * The title to display in the column header, this can be HTML. + * @type {string} + * @default null + */ + this.title = F.is.string(definition.title) ? definition.title : null; + if (!this.virtual && this.title == null && F.is.jq(this.$el)) this.title = this.$el.html(); + if (this.title == null) this.title = 'Column '+(definition.index+1); + /** + * The styles to apply to all cells in this column. + * @type {object} + */ + this.style = F.is.hash(definition.style) ? definition.style : (F.is.string(definition.style) ? F.css2json(definition.style) : {}); + /** + * The classes to apply to all cells in this column. + * @type {Array.} + */ + this.classes = F.is.array(definition.classes) ? definition.classes : (F.is.string(definition.classes) ? definition.classes.match(/\S+/g) : []); + + // override any default functions ensuring when they are executed "this" within the context of the function points to the instance of this object. + this.parser = F.checkFnValue(this, definition.parser, this.parser); + this.formatter = F.checkFnValue(this, definition.formatter, this.formatter); + }, + /** + * After the column has been defined this ensures that the $el property is a jQuery object by either creating or updating the current value. + * @instance + * @protected + * @this FooTable.Column + */ + $create: function(){ + (this.$el = !this.virtual && F.is.jq(this.$el) ? this.$el : $('')).html(this.title).addClass(this.classes.join(' ')).css(this.style); + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.Column#format} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {string} + * @this FooTable.Column + */ + parser: function(valueOrElement){ + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ // use jQuery to get the value + var data = $(valueOrElement).data('value'); + return F.is.defined(data) ? data : $(valueOrElement).html(); + } + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement+''; // use the native toString of the value + return null; // otherwise we have no value so return null + }, + /** + * This is supplied the value retrieved from the {@link FooTable.Column#parse} function and must return a string, HTMLElement or jQuery object. + * The return value from this function is what is displayed in the cell in the table. + * @instance + * @protected + * @param {string} value - The value to format. + * @returns {(string|HTMLElement|jQuery)} + * @this FooTable.Column + */ + formatter: function(value){ + return value == null ? '' : value; + }, + /** + * Creates a cell for this column from the supplied {@link FooTable.Row} object. This allows different column types to return different types of cells. + * @instance + * @protected + * @param {FooTable.Row} row - The row to create the cell from. + * @returns {FooTable.Cell} + * @this FooTable.Column + */ + createCell: function(row){ + var element = F.is.jq(row.$el) ? row.$el.children('td,th').get(this.index) : null, + data = F.is.hash(row.value) ? row.value[this.name] : null; + return new F.Cell(this.ft, row, this, element || data); + } + }); + + F.columns = new F.ClassFactory(); + + F.columns.register('text', F.Column); + +})(jQuery, FooTable); +(function ($, F) { + + F.Component = F.Class.extend(/** @lends FooTable.Component */{ + /** + * The base class for all FooTable components. + * @constructs + * @extends FooTable.Class + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} object for the component. + * @param {boolean} enabled - Whether or not the component is enabled. + * @throws {TypeError} The instance parameter must be an instance of {@link FooTable.Table}. + * @returns {FooTable.Component} + */ + construct: function (instance, enabled) { + if (!(instance instanceof F.Table)) + throw new TypeError('The instance parameter must be an instance of FooTable.Table.'); + + /** + * The parent {@link FooTable.Table} for the component. + * @type {FooTable.Table} + */ + this.ft = instance; + /** + * Whether or not this component is enabled. Disabled components only have there preinit method called allowing for this value to be overridden. + * @type {boolean} + */ + this.enabled = F.is.boolean(enabled) ? enabled : false; + }, + /** + * The preinit method is called during the parent {@link FooTable.Table} constructor call. + * @param {object} data - The jQuery.data() object of the root table. + * @instance + * @protected + * @function + */ + preinit: function(data){}, + /** + * The init method is called during the parent {@link FooTable.Table} constructor call. + * @instance + * @protected + * @function + */ + init: function(){}, + /** + * This method is called from the {@link FooTable.Table#destroy} method. + * @instance + * @protected + * @function + */ + destroy: function(){}, + /** + * This method is called from the {@link FooTable.Table#draw} method. + * @instance + * @protected + * @function + */ + predraw: function(){}, + /** + * This method is called from the {@link FooTable.Table#draw} method. + * @instance + * @protected + * @function + */ + draw: function(){}, + /** + * This method is called from the {@link FooTable.Table#draw} method. + * @instance + * @protected + * @function + */ + postdraw: function(){} + }); + + F.components = new F.ClassFactory(); + +})(jQuery, FooTable); +(function ($, F) { + /** + * Contains all the available options for the FooTable plugin. + * @name FooTable.Defaults + * @function + * @constructor + * @returns {FooTable.Defaults} + */ + F.Defaults = function () { + /** + * Whether or not events raised using the {@link FooTable.Table#raise} method are propagated up the DOM. By default this is set to false and all events bubble up the DOM as per usual + * however the reason for this option is if we have nested tables. If false the parent table would receive all the events raised by it's children and any handlers bound to both the + * parent and child would be triggered which is not the desired behavior. + * @type {boolean} + * @default false + */ + this.stopPropagation = false; + /** + * An object in which the string keys represent one or more space-separated event types and optional namespaces, and the values represent a handler function to be called for the event(s). + * @type {object.} + * @default NULL + * @example This example shows how to pass an object containing the events and handlers. + * "on": { + * "click": function(e){ + * // bind a custom click event to do something whenever the table is clicked + * }, + * "init.ft.table": function(e, ft){ + * // bind to the FooTable initialize event to do something + * } + * } + */ + this.on = null; + }; + + /** + * Contains all the default options for the plugin. + * @type {FooTable.Defaults} + */ + F.defaults = new F.Defaults(); + +})(jQuery, FooTable); +(function($, F){ + + F.Row = F.Class.extend(/** @lends FooTable.Row */{ + /** + * The row class containing all the properties for a row and its' cells. + * @constructs + * @extends FooTable.Class + * @param {FooTable.Table} table - The parent {@link FooTable.Table} this component belongs to. + * @param {Array.} columns - The array of {@link FooTable.Column} for this row. + * @param {(*|HTMLElement|jQuery)} dataOrElement - Either the data for the row (create) or the element (parse) for the row. + * @returns {FooTable.Row} + */ + construct: function (table, columns, dataOrElement) { + /** + * The {@link FooTable.Table} for the row. + * @type {FooTable.Table} + */ + this.ft = table; + /** + * The array of {@link FooTable.Column} for this row. + * @type {Array.} + */ + this.columns = columns; + + this.created = false; + this.define(dataOrElement); + }, + /** + * This is supplied either the object containing the values for the row or the row element/jQuery object if it exists. + * If supplied the element we need to set the $el property and parse the cells from it using the column index. + * If we have an object we parse the cells from it using the column name. + * @param {(object|jQuery)} dataOrElement - The row object or element to define the row. + */ + define: function(dataOrElement){ + /** + * The jQuery table row object this instance wraps. + * @instance + * @protected + * @type {jQuery} + */ + this.$el = F.is.element(dataOrElement) || F.is.jq(dataOrElement) ? $(dataOrElement) : null; + /** + * The jQuery toggle element for the row. + * @instance + * @protected + * @type {jQuery} + */ + this.$toggle = $('', {'class': 'footable-toggle fooicon fooicon-plus'}); + + var isObj = F.is.hash(dataOrElement), + hasOptions = isObj && F.is.hash(dataOrElement.options) && F.is.hash(dataOrElement.value); + + /** + * The value of the row. + * @instance + * @protected + * @type {Object} + */ + this.value = isObj ? (hasOptions ? dataOrElement.value : dataOrElement) : null; + + /** + * Contains any options for the row. + * @type {object} + */ + this.o = $.extend(true, { + expanded: false, + classes: null, + style: null + }, hasOptions ? dataOrElement.options : {}); + + /** + * Whether or not this row is expanded and will display it's detail row when there are any hidden columns. + * @instance + * @protected + * @type {boolean} + */ + this.expanded = F.is.jq(this.$el) ? (this.$el.data('expanded') || this.o.expanded) : this.o.expanded; + /** + * An array of CSS classes for the row. + * @instance + * @protected + * @type {Array.} + */ + this.classes = F.is.jq(this.$el) && this.$el.attr('class') ? this.$el.attr('class').match(/\S+/g) : (F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : [])); + /** + * The inline styles for the row. + * @instance + * @protected + * @type {object} + */ + this.style = F.is.jq(this.$el) && this.$el.attr('style') ? F.css2json(this.$el.attr('style')) : (F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {})); + + /** + * The cells array. This is populated before the call to the {@link FooTable.Row#$create} method. + * @instance + * @type {Array.} + */ + this.cells = this.createCells(); + + // this ensures the value contains the parsed cell values and not the supplied values + var self = this; + self.value = {}; + F.arr.each(self.cells, function(cell){ + self.value[cell.column.name] = cell.val(); + }); + }, + /** + * After the row has been defined this ensures that the $el property is a jQuery object by either creating or updating the current value. + * @instance + * @protected + * @this FooTable.Row + */ + $create: function(){ + if (this.created) return; + (this.$el = F.is.jq(this.$el) ? this.$el : $('')) + .data('__FooTableRow__', this); + + this._setClasses(this.$el); + this._setStyle(this.$el); + + if (this.ft.rows.toggleColumn == 'last') this.$toggle.addClass('last-column'); + + this.$details = $('', { 'class': 'footable-detail-row' }) + .append($('', { colspan: this.ft.columns.visibleColspan }) + .append($('', { 'class': 'footable-details ' + this.ft.classes.join(' ') }) + .append(''))); + + var self = this; + F.arr.each(self.cells, function(cell){ + if (!cell.created) cell.$create(); + self.$el.append(cell.$el); + }); + self.$el.off('click.ft.row').on('click.ft.row', { self: self }, self._onToggle); + this.created = true; + }, + /** + * This is called during the construct method and uses the current column definitions to create an array of {@link FooTable.Cell} objects for the row. + * @instance + * @protected + * @returns {Array.} + * @this FooTable.Row + */ + createCells: function(){ + var self = this; + return F.arr.map(self.columns, function(col){ + return col.createCell(self); + }); + }, + /** + * Allows easy access to getting or setting the row's data. If the data is set all associated properties are also updated along with the actual element. + * Using this method also allows us to supply an object containing options and the data for the row at the same time. + * @instance + * @param {object} [data] - The data to set for the row. If not supplied the current value of the row is returned. + * @param {boolean} [redraw=true] - Whether or not to redraw the row once the value has been set. + * @returns {(*|undefined)} + */ + val: function(data, redraw){ + var self = this; + if (!F.is.hash(data)){ + // get - check the value property and build it from the cells if required. + if (!F.is.hash(this.value) || F.is.emptyObject(this.value)){ + this.value = {}; + F.arr.each(this.cells, function(cell){ + self.value[cell.column.name] = cell.val(); + }); + } + return this.value; + } + // set + this.collapse(false); + var isObj = F.is.hash(data), + hasOptions = isObj && F.is.hash(data.options) && F.is.hash(data.value); + + this.o = $.extend(true, { + expanded: self.expanded, + classes: self.classes, + style: self.style + }, hasOptions ? data.options : {}); + + this.expanded = this.o.expanded; + this.classes = F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : []); + this.style = F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {}); + if (isObj) { + if ( hasOptions ) data = data.value; + if (F.is.hash(this.value)){ + for (var prop in data) { + if (!data.hasOwnProperty(prop)) continue; + this.value[prop] = data[prop]; + } + } else { + this.value = data; + } + } else { + this.value = null; + } + + F.arr.each(this.cells, function(cell){ + if (F.is.defined(self.value[cell.column.name])) cell.val(self.value[cell.column.name], false); + }); + + if (this.created){ + this._setClasses(this.$el); + this._setStyle(this.$el); + if (F.is.boolean(redraw) ? redraw : true) this.draw(); + } + }, + _setClasses: function($el){ + var hasClasses = !F.is.emptyArray(this.classes), + classes = null; + $el.removeAttr('class'); + if (!hasClasses) return; + else classes = this.classes.join(' '); + if (!F.is.emptyString(classes)){ + $el.addClass(classes); + } + }, + _setStyle: function($el){ + var hasStyle = !F.is.emptyObject(this.style), + style = null; + $el.removeAttr('style'); + if (!hasStyle) return; + else style = this.style; + if (F.is.hash(style)){ + $el.css(style); + } + }, + /** + * Sets the current row to an expanded state displaying any hidden columns in a detail row just below it. + * @instance + * @fires FooTable.Row#"expand.ft.row" + */ + expand: function(){ + if (!this.created) return; + var self = this; + /** + * The expand.ft.row event is raised before the the row is expanded. + * Calling preventDefault on this event will stop the row being expanded. + * @event FooTable.Row#"expand.ft.row" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row about to be expanded. + */ + self.ft.raise('expand.ft.row',[self]).then(function(){ + self.__hidden__ = F.arr.map(self.cells, function(cell){ + return cell.column.hidden && cell.column.visible ? cell : null; + }); + + if (self.__hidden__.length > 0){ + self.$details.insertAfter(self.$el) + .children('td').first() + .attr('colspan', self.ft.columns.visibleColspan); + + F.arr.each(self.__hidden__, function(cell){ + cell.collapse(); + }); + } + self.$el.attr('data-expanded', true); + self.$toggle.removeClass('fooicon-plus').addClass('fooicon-minus'); + self.expanded = true; + }); + }, + /** + * Sets the current row to a collapsed state removing the detail row if it exists. + * @instance + * @param {boolean} [setExpanded] - Whether or not to set the {@link FooTable.Row#expanded} property to false. + * @fires FooTable.Row#"collapse.ft.row" + */ + collapse: function(setExpanded){ + if (!this.created) return; + var self = this; + /** + * The collapse.ft.row event is raised before the the row is collapsed. + * Calling preventDefault on this event will stop the row being collapsed. + * @event FooTable.Row#"collapse.ft.row" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row about to be expanded. + */ + self.ft.raise('collapse.ft.row',[self]).then(function(){ + F.arr.each(self.__hidden__, function(cell){ + cell.restore(); + }); + self.$details.detach(); + self.$el.removeAttr('data-expanded'); + self.$toggle.removeClass('fooicon-minus').addClass('fooicon-plus'); + if (F.is.boolean(setExpanded) ? setExpanded : true) self.expanded = false; + }); + }, + /** + * Prior to drawing this moves the details contents back to there original cells and detaches the toggle element from the row. + * @instance + * @param {boolean} [detach] - Whether or not to detach the row. + * @this FooTable.Row + */ + predraw: function(detach){ + if (this.created){ + if (this.expanded){ + this.collapse(false); + } + this.$toggle.detach(); + detach = F.is.boolean(detach) ? detach : true; + if (detach) this.$el.detach(); + } + }, + /** + * Draws the current row and cells. + * @instance + * @this FooTable.Row + */ + draw: function($parent){ + if (!this.created) this.$create(); + if (F.is.jq($parent)) $parent.append(this.$el); + var self = this; + F.arr.each(self.cells, function(cell){ + cell.$el.css('display', (cell.column.hidden || !cell.column.visible ? 'none' : 'table-cell')); + if (self.ft.rows.showToggle && self.ft.columns.hasHidden){ + if ((self.ft.rows.toggleColumn == 'first' && cell.column.index == self.ft.columns.firstVisibleIndex) + || (self.ft.rows.toggleColumn == 'last' && cell.column.index == self.ft.columns.lastVisibleIndex)) { + cell.$el.prepend(self.$toggle); + } + } + cell.$el.add(cell.column.$el).removeClass('footable-first-visible footable-last-visible'); + if (cell.column.index == self.ft.columns.firstVisibleIndex){ + cell.$el.add(cell.column.$el).addClass('footable-first-visible'); + } + if (cell.column.index == self.ft.columns.lastVisibleIndex){ + cell.$el.add(cell.column.$el).addClass('footable-last-visible'); + } + }); + if (this.expanded){ + this.expand(); + } + }, + /** + * Toggles the row between it's expanded and collapsed state if there are hidden columns. + * @instance + * @this FooTable.Row + */ + toggle: function(){ + if (this.created && this.ft.columns.hasHidden){ + if (this.expanded) this.collapse(); + else this.expand(); + } + }, + /** + * Handles the toggle click event for rows. + * @instance + * @param {jQuery.Event} e - The jQuery.Event object for the click event. + * @private + * @this jQuery + */ + _onToggle: function (e) { + var self = e.data.self; + // only execute the toggle if the event.target is one of the approved initiators + if ($(e.target).is(self.ft.rows.toggleSelector)){ + self.toggle(); + } + } + }); + +})(jQuery, FooTable); + +(function ($, F) { + + /** + * An array of all currently loaded instances of the plugin. + * @protected + * @readonly + * @type {Array.} + */ + F.instances = []; + + F.Table = F.Class.extend(/** @lends FooTable.Table */{ + /** + * This class is the core of the plugin and drives the logic of all components. + * @constructs + * @this FooTable.Table + * @extends FooTable.Class + * @param {(HTMLTableElement|jQuery)} element - The element or jQuery table object to bind the plugin to. + * @param {object} options - The options to initialize the plugin with. + * @param {function} [ready] - A callback function to execute once the plugin is initialized. + * @returns {FooTable.Table} + */ + construct: function (element, options, ready) { + //BEGIN MEMBERS + /** + * The timeout ID for the resize event. + * @instance + * @private + * @type {?number} + */ + this._resizeTimeout = null; + /** + * The ID of the FooTable instance. + * @instance + * @type {number} + */ + this.id = F.instances.push(this); + /** + * Whether or not the plugin and all components and add-ons are fully initialized. + * @instance + * @type {boolean} + */ + this.initialized = false; + /** + * The jQuery table object the plugin is bound to. + * @instance + * @type {jQuery} + */ + this.$el = (F.is.jq(element) ? element : $(element)).first(); // ensure one table, one instance + /** + * A loader jQuery instance + * @instance + * @type {jQuery} + */ + this.$loader = $('
    ', { 'class': 'footable-loader' }).append($('', {'class': 'fooicon fooicon-loader'})); + /** + * The options for the plugin. This is a merge of user defined options and the default options. + * @instance + * @type {object} + */ + this.o = $.extend(true, {}, F.defaults, options); + /** + * The jQuery data object for the table at initialization. + * @instance + * @type {object} + */ + this.data = this.$el.data() || {}; + /** + * An array of all CSS classes on the table that do not start with "footable". + * @instance + * @protected + * @type {Array.} + */ + this.classes = []; + /** + * All components for this instance of the plugin. These are executed in the order they appear in the array for the initialize phase and in reverse order for the destroy phase of the plugin. + * @instance + * @protected + * @type {object} + * @prop {Array.} internal - The internal components for the plugin. These are executed either before all other components in the initialize phase or after them in the destroy phase of the plugin. + * @prop {Array.} core - The core components for the plugin. These are executed either after the internal components in the initialize phase or before them in the destroy phase of the plugin. + * @prop {Array.} custom - The custom components for the plugin. These are executed either after the core components in the initialize phase or before them in the destroy phase of the plugin. + */ + this.components = F.components.load((F.is.hash(this.data.components) ? this.data.components : this.o.components), this); + /** + * The breakpoints component for this instance of the plugin. + * @instance + * @type {FooTable.Breakpoints} + */ + this.breakpoints = this.use(FooTable.Breakpoints); + /** + * The columns component for this instance of the plugin. + * @instance + * @type {FooTable.Columns} + */ + this.columns = this.use(FooTable.Columns); + /** + * The rows component for this instance of the plugin. + * @instance + * @type {FooTable.Rows} + */ + this.rows = this.use(FooTable.Rows); + + //END MEMBERS + this._construct(ready); + }, + /** + * Once all properties are set this performs the actual initialization of the plugin calling the {@link FooTable.Table#_preinit} and + * {@link FooTable.Table#_init} methods as well as raising the {@link FooTable.Table#"ready.ft.table"} event. + * @this FooTable.Table + * @instance + * @param {function} [ready] - A callback function to execute once the plugin is initialized. + * @private + * @returns {jQuery.Promise} + * @fires FooTable.Table#"ready.ft.table" + */ + _construct: function(ready){ + var self = this; + this._preinit().then(function(){ + return self._init(); + }).always(function(arg){ + self.$el.show(); + if (F.is.error(arg)){ + console.error('FooTable: unhandled error thrown during initialization.', arg); + } else { + /** + * The postinit.ft.table event is raised after the plugin has been initialized and the table drawn. + * Calling preventDefault on this event will stop the ready callback being executed. + * @event FooTable.Table#"postinit.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('ready.ft.table').then(function(){ + if (F.is.fn(ready)) ready.call(self, self); + }); + } + }); + }, + /** + * The preinit method is called prior to the plugins actual initialization and provides itself and it's components an opportunity to parse any additional option values. + * @instance + * @private + * @returns {jQuery.Promise} + * @fires FooTable.Table#"preinit.ft.table" + */ + _preinit: function(){ + var self = this; + /** + * The preinit.ft.table event is raised before any components. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Table#"preinit.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object from the root table element. + */ + return this.raise('preinit.ft.table', [self.data]).then(function(){ + var classes = (self.$el.attr('class') || '').match(/\S+/g) || []; + + self.o.ajax = F.checkFnValue(self, self.data.ajax, self.o.ajax); + self.o.stopPropagation = F.is.boolean(self.data.stopPropagation) + ? self.data.stopPropagation + : self.o.stopPropagation; + + for (var i = 0, len = classes.length; i < len; i++){ + if (!F.str.startsWith(classes[i], 'footable')) self.classes.push(classes[i]); + } + + self.$el.hide().after(self.$loader); + return self.execute(false, false, 'preinit', self.data); + }); + }, + /** + * Initializes this instance of the plugin and calls the callback function if one is supplied once complete. + * @this FooTable.Table + * @instance + * @private + * @return {jQuery.Promise} + * @fires FooTable.Table#"init.ft.table" + */ + _init: function(){ + var self = this; + /** + * The init.ft.table event is raised before any components are initialized. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Table#"init.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('init.ft.table').then(function(){ + var $thead = self.$el.children('thead'), + $tbody = self.$el.children('tbody'), + $tfoot = self.$el.children('tfoot'); + self.$el.addClass('footable footable-' + self.id); + if (F.is.hash(self.o.on)) self.$el.on(self.o.on); + if ($tfoot.length == 0) self.$el.append($tfoot = $('
    ')); + if ($tbody.length == 0) self.$el.append(''); + if ($thead.length == 0) self.$el.prepend($thead = $('')); + return self.execute(false, true, 'init').then(function(){ + self.$el.data('__FooTable__', self); + if ($tfoot.children('tr').length == 0) $tfoot.remove(); + if ($thead.children('tr').length == 0) $thead.remove(); + + /** + * The postinit.ft.table event is raised after any components are initialized but before the table is + * drawn for the first time. + * Calling preventDefault on this event will disable the initial drawing of the table. + * @event FooTable.Table#"postinit.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('postinit.ft.table').then(function(){ + return self.draw(); + }).always(function(){ + $(window).off('resize.ft'+self.id, self._onWindowResize) + .on('resize.ft'+self.id, { self: self }, self._onWindowResize); + self.initialized = true; + }); + }); + }); + }, + /** + * Destroys this plugin removing it from the table. + * @this FooTable.Table + * @instance + * @fires FooTable.Table#"destroy.ft.table" + */ + destroy: function () { + var self = this; + /** + * The destroy.ft.table event is called before all core components. + * Calling preventDefault on this event will prevent the entire plugin from being destroyed. + * @event FooTable.Table#"destroy.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('destroy.ft.table').then(function(){ + return self.execute(true, true, 'destroy').then(function () { + self.$el.removeData('__FooTable__').removeClass('footable-' + self.id); + if (F.is.hash(self.o.on)) self.$el.off(self.o.on); + $(window).off('resize.ft'+self.id, self._onWindowResize); + self.initialized = false; + }); + }).fail(function(err){ + if (F.is.error(err)){ + console.error('FooTable: unhandled error thrown while destroying the plugin.', err); + } + }); + }, + /** + * Raises an event on this instance supplying the args array as additional parameters to the handlers. + * @this FooTable.Table + * @instance + * @param {string} eventName - The name of the event to raise, this can include namespaces. + * @param {Array} [args] - An array containing additional parameters to be passed to any bound handlers. + * @returns {jQuery.Event} + */ + raise: function(eventName, args){ + var self = this, + debug = F.__debug__ && (F.is.emptyArray(F.__debug_options__.events) || F.arr.any(F.__debug_options__.events, function(name){ return F.str.contains(eventName, name); })); + args = args || []; + args.unshift(this); + return $.Deferred(function(d){ + var evt = $.Event(eventName); + if (self.o.stopPropagation == true){ + self.$el.one(eventName, function (e) {e.stopPropagation();}); + } + if (debug) console.log('FooTable:'+eventName+': ', args); + self.$el.trigger(evt, args); + if (evt.isDefaultPrevented()){ + if (debug) console.log('FooTable: default prevented for the "'+eventName+'" event.'); + d.reject(evt); + } else d.resolve(evt); + }); + }, + /** + * Attempts to retrieve the instance of the supplied component type for this instance. + * @this FooTable.Table + * @instance + * @param {object} type - The content type to retrieve for this instance. + * @returns {(*|null)} + */ + use: function(type){ + for (var i = 0, len = this.components.length; i < len; i++){ + if (this.components[i] instanceof type) return this.components[i]; + } + return null; + }, + /** + * Performs the drawing of the table. + * @this FooTable.Table + * @instance + * @protected + * @returns {jQuery.Promise} + * @fires FooTable.Table#"predraw.ft.table" + * @fires FooTable.Table#"draw.ft.table" + * @fires FooTable.Table#"postdraw.ft.table" + */ + draw: function () { + var self = this; + + // Clone the current table and insert it into the original's place + var $elCopy = self.$el.clone().insertBefore(self.$el); + + // Detach `self.$el` from the DOM, retaining its event handlers + self.$el.detach(); + + // when drawing the order that the components are executed is important so chain the methods but use promises to retain async safety. + return self.execute(false, true, 'predraw').then(function(){ + /** + * The predraw.ft.table event is raised after all core components and add-ons have executed there predraw functions but before they execute there draw functions. + * @event FooTable.Table#"predraw.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('predraw.ft.table').then(function(){ + return self.execute(false, true, 'draw').then(function(){ + /** + * The draw.ft.table event is raised after all core components and add-ons have executed there draw functions. + * @event FooTable.Table#"draw.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('draw.ft.table').then(function(){ + return self.execute(false, true, 'postdraw').then(function(){ + /** + * The postdraw.ft.table event is raised after all core components and add-ons have executed there postdraw functions. + * @event FooTable.Table#"postdraw.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('postdraw.ft.table'); + }); + }); + }); + }); + }).fail(function(err){ + if (F.is.error(err)){ + console.error('FooTable: unhandled error thrown during a draw operation.', err); + } + }).always(function(){ + // Replace the copy that we added above with the modified `self.$el` + $elCopy.replaceWith(self.$el); + self.$loader.remove(); + }); + }, + /** + * Executes the specified method with the optional number of parameters on all components and waits for the promise from each to be resolved before executing the next. + * @this FooTable.Table + * @instance + * @protected + * @param {boolean} reverse - Whether or not to execute the component methods in the reverse order to what they were registered in. + * @param {boolean} enabled - Whether or not to execute the method on enabled components only. + * @param {string} methodName - The name of the method to execute. + * @param {*} [param1] - The first parameter for the method. + * @param {...*} [paramN] - Any number of additional parameters for the method. + * @returns {jQuery.Promise} + */ + execute: function(reverse, enabled, methodName, param1, paramN){ + var self = this, args = Array.prototype.slice.call(arguments); + reverse = args.shift(); + enabled = args.shift(); + var components = enabled ? F.arr.get(self.components, function(c){ return c.enabled; }) : self.components.slice(0); + args.unshift(reverse ? components.reverse() : components); + return self._execute.apply(self, args); + }, + /** + * Executes the specified method with the optional number of parameters on all supplied components waiting for the result of each before executing the next. + * @this FooTable.Table + * @instance + * @private + * @param {Array.} components - The components to call the method on. + * @param {string} methodName - The name of the method to execute + * @param {*} [param1] - The first parameter for the method. + * @param {...*} [paramN] - Any additional parameters for the method. + * @returns {jQuery.Promise} + */ + _execute: function(components, methodName, param1, paramN){ + if (!components || !components.length) return $.when(); + var self = this, args = Array.prototype.slice.call(arguments), + component; + components = args.shift(); + methodName = args.shift(); + component = components.shift(); + + if (!F.is.fn(component[methodName])) + return self._execute.apply(self, [components, methodName].concat(args)); + + return $.Deferred(function(d){ + try { + var result = component[methodName].apply(component, args); + if (F.is.promise(result)){ + return result.then(d.resolve, d.reject); + } else { + d.resolve(result); + } + } catch (err) { + d.reject(err); + } + }).then(function(){ + return self._execute.apply(self, [components, methodName].concat(args)); + }); + }, + /** + * Listens to the window resize event and performs a check to see if the breakpoint has changed. + * @this window + * @instance + * @private + * @fires FooTable.Table#"resize.ft.table" + */ + _onWindowResize: function (e) { + var self = e.data.self; + if (self._resizeTimeout != null) { clearTimeout(self._resizeTimeout); } + self._resizeTimeout = setTimeout(function () { + self._resizeTimeout = null; + /** + * The resize event is raised a short time after window resize operations cease. + * @event FooTable.Table#"resize.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + self.raise('resize.ft.table').then(function(){ + self.breakpoints.check(); + }); + }, 300); + } + }); + +})(jQuery, FooTable); +(function($, F){ + + if (F.is.undef(window.moment)){ + // The DateColumn requires moment.js to parse and format date values. Goto http://momentjs.com/ to get it. + return; + } + + F.DateColumn = F.Column.extend(/** @lends FooTable.DateColumn */{ + /** + * The date column class is used to handle date values. This column is dependent on [moment.js]{@link http://momentjs.com/} to provide date parsing and formatting functionality. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + * @returns {FooTable.DateColumn} + */ + construct: function(instance, definition){ + this._super(instance, definition, 'date'); + /** + * The format string to use when parsing and formatting dates. + * @instance + * @type {string} + */ + this.formatString = F.is.string(definition.formatString) ? definition.formatString : 'MM-DD-YYYY'; + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.DateColumn#format} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(moment|null)} + * @this FooTable.DateColumn + */ + parser: function(valueOrElement){ + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('value'); + valueOrElement = F.is.defined(data) ? data : $(valueOrElement).text(); + if (F.is.string(valueOrElement)) valueOrElement = isNaN(valueOrElement) ? valueOrElement : +valueOrElement; + } + if (F.is.date(valueOrElement)) return moment(valueOrElement); + if (F.is.object(valueOrElement) && F.is.boolean(valueOrElement._isAMomentObject)) return valueOrElement; + if (F.is.string(valueOrElement)){ + // if it looks like a number convert it and do nothing else otherwise create a new moment using the string value and formatString + if (isNaN(valueOrElement)){ + return moment(valueOrElement, this.formatString); + } else { + valueOrElement = +valueOrElement; + } + } + if (F.is.number(valueOrElement)){ + return moment(valueOrElement); + } + return null; + }, + /** + * This is supplied the value retrieved from the {@link FooTable.DateColumn#parser} function and must return a string, HTMLElement or jQuery object. + * The return value from this function is what is displayed in the cell in the table. + * @instance + * @protected + * @param {*} value - The value to format. + * @returns {(string|HTMLElement|jQuery)} + * @this FooTable.DateColumn + */ + formatter: function(value){ + return F.is.object(value) && F.is.boolean(value._isAMomentObject) && value.isValid() ? value.format(this.formatString) : ''; + }, + /** + * This is supplied either the cell value or jQuery object to parse. A string value must be returned from this method and will be used during filtering operations. + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {string} + * @this FooTable.DateColumn + */ + filterValue: function(valueOrElement){ + // if we have an element or a jQuery object use jQuery to get the value + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)) valueOrElement = $(valueOrElement).data('filterValue') || $(valueOrElement).text(); + // if options are supplied with the value + if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){ + if (F.is.string(valueOrElement.options.filterValue)) valueOrElement = valueOrElement.options.filterValue; + if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value; + } + // if the value is a moment object just return the formatted value + if (F.is.object(valueOrElement) && F.is.boolean(valueOrElement._isAMomentObject)) return valueOrElement.format(this.formatString); + // if its a string + if (F.is.string(valueOrElement)){ + // if its not a number return it + if (isNaN(valueOrElement)){ + return valueOrElement; + } else { // otherwise convert it and carry on + valueOrElement = +valueOrElement; + } + } + // if the value is a number or date convert to a moment object and return the formatted result. + if (F.is.number(valueOrElement) || F.is.date(valueOrElement)){ + return moment(valueOrElement).format(this.formatString); + } + // try use the native toString of the value if its not undefined or null + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement+''; + return ''; // otherwise we have no value so return an empty string + } + }); + + F.columns.register('date', F.DateColumn); + +})(jQuery, FooTable); + +(function($, F){ + + F.HTMLColumn = F.Column.extend(/** @lends FooTable.HTMLColumn */{ + /** + * The HTML column class is used to handle any raw HTML columns. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + * @returns {FooTable.HTMLColumn} + */ + construct: function(instance, definition){ + this._super(instance, definition, 'html'); + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.HTMLColumn#format} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(jQuery|null)} + * @this FooTable.HTMLColumn + */ + parser: function(valueOrElement){ + if (F.is.string(valueOrElement)) valueOrElement = $($.trim(valueOrElement)); + if (F.is.element(valueOrElement)) valueOrElement = $(valueOrElement); + if (F.is.jq(valueOrElement)){ + var tagName = valueOrElement.prop('tagName').toLowerCase(); + if (tagName == 'td' || tagName == 'th'){ + var data = valueOrElement.data('value'); + return F.is.defined(data) ? data : valueOrElement.contents(); + } + return valueOrElement; + } + return null; + } + }); + + F.columns.register('html', F.HTMLColumn); + +})(jQuery, FooTable); +(function($, F){ + + F.NumberColumn = F.Column.extend(/** @lends FooTable.NumberColumn */{ + /** + * The number column class is used to handle simple number columns. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + * @returns {FooTable.NumberColumn} + */ + construct: function(instance, definition){ + this._super(instance, definition, 'number'); + this.decimalSeparator = F.is.string(definition.decimalSeparator) ? definition.decimalSeparator : '.'; + this.thousandSeparator = F.is.string(definition.thousandSeparator) ? definition.thousandSeparator : ','; + this.decimalSeparatorRegex = new RegExp(F.str.escapeRegExp(this.decimalSeparator), 'g'); + this.thousandSeparatorRegex = new RegExp(F.str.escapeRegExp(this.thousandSeparator), 'g'); + this.cleanRegex = new RegExp('[^0-9' + F.str.escapeRegExp(this.decimalSeparator) + ']', 'g'); + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.Column#formatter} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(number|null)} + * @this FooTable.NumberColumn + */ + parser: function(valueOrElement){ + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('value'); + valueOrElement = F.is.defined(data) ? data : $(valueOrElement).text().replace(this.cleanRegex, ''); + } + if (F.is.string(valueOrElement)){ + valueOrElement = valueOrElement.replace(this.thousandSeparatorRegex, '').replace(this.decimalSeparatorRegex, '.'); + valueOrElement = parseFloat(valueOrElement); + } + if (F.is.number(valueOrElement)) return valueOrElement; + return null; + }, + /** + * This is supplied the value retrieved from the {@link FooTable.NumberColumn#parse} function and must return a string, HTMLElement or jQuery object. + * The return value from this function is what is displayed in the cell in the table. + * @instance + * @protected + * @param {number} value - The value to format. + * @returns {(string|HTMLElement|jQuery)} + * @this FooTable.NumberColumn + */ + formatter: function(value){ + if (value == null) return ''; + var s = (value + '').split('.'); + if (s.length == 2 && s[0].length > 3) { + s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, this.thousandSeparator); + } + return s.join(this.decimalSeparator); + } + }); + + F.columns.register('number', F.NumberColumn); + +})(jQuery, FooTable); +(function($, F){ + + F.Breakpoint = F.Class.extend(/** @lends FooTable.Breakpoint */{ + /** + * The breakpoint class containing the name and maximum width for the breakpoint. + * @constructs + * @extends FooTable.Class + * @param {string} name - The name of the breakpoint. Must contain no spaces or special characters. + * @param {number} width - The width of the breakpoint in pixels. + * @returns {FooTable.Breakpoint} + */ + construct: function(name, width){ + /** + * The name of the breakpoint. + * @type {string} + */ + this.name = name; + /** + * The maximum width of the breakpoint in pixels. + * @type {number} + */ + this.width = width; + } + }); + +})(jQuery, FooTable); +(function($, F){ + F.Breakpoints = F.Component.extend(/** @lends FooTable.Breakpoints */{ + /** + * Contains the logic to calculate and apply breakpoints for the plugin. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} this component belongs to. + * @returns {FooTable.Breakpoints} + */ + construct: function(table){ + // call the base class constructor + this._super(table, true); + + /* PROTECTED */ + /** + * This provides a shortcut to the {@link FooTable.Table#options} object. + * @protected + * @type {FooTable.Table#options} + */ + this.o = table.o; + + /* PUBLIC */ + /** + * The current breakpoint. + * @type {FooTable.Breakpoint} + */ + this.current = null; + /** + * An array of {@link FooTable.Breakpoint} objects created from parsing the options. + * @type {Array.} + */ + this.array = []; + /** + * Whether or not breakpoints cascade. When set to true all breakpoints larger than the current will be hidden along with it. + * @type {boolean} + */ + this.cascade = this.o.cascade; + /** + * Whether or not to calculate breakpoints on the width of the parent element rather than the viewport. + * @type {boolean} + */ + this.useParentWidth = this.o.useParentWidth; + /** + * This value is updated each time the current breakpoint changes and contains a space delimited string of the names of the current breakpoint and all those smaller than it. + * @type {string} + */ + this.hidden = null; + + /* PRIVATE */ + /** + * This value is set once when the {@link FooTable.Breakpoints#array} is generated and contains a space delimited string of all the breakpoint class names. + * @type {string} + * @private + */ + this._classNames = ''; + + // check if a function was supplied to override the default getWidth + this.getWidth = F.checkFnValue(this, this.o.getWidth, this.getWidth); + }, + + /* PROTECTED */ + /** + * Checks the supplied data and options for the breakpoints component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.Breakpoints#"preinit.ft.breakpoints" + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.breakpoints event is raised before any UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Breakpoints#"preinit.ft.breakpoints" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + return this.ft.raise('preinit.ft.breakpoints', [data]).then(function(){ + self.cascade = F.is.boolean(data.cascade) ? data.cascade : self.cascade; + self.o.breakpoints = F.is.hash(data.breakpoints) ? data.breakpoints : self.o.breakpoints; + self.getWidth = F.checkFnValue(self, data.getWidth, self.getWidth); + if (self.o.breakpoints == null) self.o.breakpoints = { "xs": 480, "sm": 768, "md": 992, "lg": 1200 }; + // Create a nice friendly array to work with out of the breakpoints object. + for (var name in self.o.breakpoints) { + if (!self.o.breakpoints.hasOwnProperty(name)) continue; + self.array.push(new F.Breakpoint(name, self.o.breakpoints[name])); + self._classNames += 'breakpoint-' + name + ' '; + } + // Sort the breakpoints so the largest is checked first + self.array.sort(function (a, b) { + return b.width - a.width; + }); + }); + }, + /** + * Initializes the class parsing the options into a sorted array of {@link FooTable.Breakpoint} objects. + * @instance + * @protected + * @fires FooTable.Breakpoints#"init.ft.breakpoints" + */ + init: function(){ + var self = this; + /** + * The init.ft.breakpoints event is raised before any UI is generated. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Breakpoints#"init.ft.breakpoints" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return this.ft.raise('init.ft.breakpoints').then(function(){ + self.current = self.get(); + }); + }, + /** + * Whenever the table is drawn this ensures the correct breakpoint class is applied to the table. + * @instance + * @protected + */ + draw: function(){ + this.ft.$el.removeClass(this._classNames).addClass('breakpoint-' + this.current.name); + }, + + /* PUBLIC */ + /** + * Calculates the current breakpoint from the {@link FooTable.Breakpoints#array} and sets the {@link FooTable.Breakpoints#current} property. + * @instance + * @returns {FooTable.Breakpoint} + */ + calculate: function(){ + var self = this, current = null, hidden = [], breakpoint, prev = null, width = self.getWidth(); + for (var i = 0, len = self.array.length; i < len; i++) { + breakpoint = self.array[i]; + // if the width is smaller than the smallest breakpoint set the smallest as the current. + // if the width is larger than the largest breakpoint set the largest as the current. + // otherwise if the width is somewhere in between check all breakpoints testing if the width + // is greater than the current but smaller than the previous. + if ((!current && i == len -1) + || (width >= breakpoint.width && (prev instanceof F.Breakpoint ? width < prev.width : true))) { + current = breakpoint; + } + if (!current) hidden.push(breakpoint.name); + prev = breakpoint; + } + hidden.push(current.name); + self.hidden = hidden.join(' '); + return current; + }, + /** + * Supplied a columns breakpoints this returns a boolean value indicating whether or not the column is visible. + * @param {string} breakpoints - A space separated string of breakpoint names. + * @returns {boolean} + */ + visible: function(breakpoints){ + if (F.is.emptyString(breakpoints)) return true; + if (breakpoints === 'all') return false; + var parts = breakpoints.split(' '), i = 0, len = parts.length; + for (; i < len; i++){ + if (this.cascade ? F.str.containsWord(this.hidden, parts[i]) : parts[i] == this.current.name) return false; + } + return true; + }, + /** + * Performs a check between the current breakpoint and the previous breakpoint and performs a redraw if they differ. + * @instance + * @fires FooTable.Breakpoints#"before.ft.breakpoints" + * @fires FooTable.Breakpoints#"after.ft.breakpoints" + */ + check: function(){ + var self = this, bp = self.get(); + if (!(bp instanceof F.Breakpoint) + || bp == self.current) + return; + + /** + * The before.ft.breakpoints event is raised if the breakpoint has changed but before the UI is redrawn and is supplied both the current breakpoint + * and the next "new" one that is about to be applied. + * Calling preventDefault on this event will prevent the next breakpoint from being applied. + * @event FooTable.Breakpoints#"before.ft.breakpoints" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Breakpoint} current - The current breakpoint. + * @param {FooTable.Breakpoint} next - The breakpoint that is about to be applied. + */ + self.ft.raise('before.ft.breakpoints', [self.current, bp]).then(function(){ + var previous = self.current; + self.current = bp; + return self.ft.draw().then(function(){ + /** + * The after.ft.breakpoints event is raised after the breakpoint has changed and the UI is redrawn and is supplied both the "new" current breakpoint + * and the previous one that was replaced. + * @event FooTable.Breakpoints#"after.ft.breakpoints" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Breakpoint} current - The current breakpoint. + * @param {FooTable.Breakpoint} previous - The breakpoint that was just replaced. + */ + self.ft.raise('after.ft.breakpoints', [self.current, previous]); + }); + }); + }, + /** + * Attempts to return a {@link FooTable.Breakpoint} instance when passed a {@link FooTable.Breakpoint}, + * the {@link FooTable.Breakpoint#name} string or if nothing is supplied the current breakpoint. + * @instance + * @param {(FooTable.Breakpoint|string|number)} [breakpoint] - The breakpoint to retrieve. + * @returns {FooTable.Breakpoint} + */ + get: function(breakpoint){ + if (F.is.undef(breakpoint)) return this.calculate(); + if (breakpoint instanceof F.Breakpoint) return breakpoint; + if (F.is.string(breakpoint)) return F.arr.first(this.array, function (bp) { return bp.name == breakpoint; }); + if (F.is.number(breakpoint)) return breakpoint >= 0 && breakpoint < this.array.length ? this.array[breakpoint] : null; + return null; + }, + /** + * Gets the width used to determine breakpoints whether it be from the viewport, parent or a custom function. + * @instance + * @returns {number} + */ + getWidth: function(){ + if (F.is.fn(this.o.getWidth)) return this.o.getWidth(this.ft); + if (this.useParentWidth == true) return this.getParentWidth(); + return this.getViewportWidth(); + }, + /** + * Gets the tables direct parents width. + * @instance + * @returns {number} + */ + getParentWidth: function(){ + return this.ft.$el.parent().width(); + }, + /** + * Gets the current viewport width. + * @instance + * @returns {number} + */ + getViewportWidth: function(){ + return Math.max(document.documentElement.clientWidth, window.innerWidth, 0); + } + }); + + F.components.register('breakpoints', F.Breakpoints, 1000); + +})(jQuery, FooTable); +(function(F){ + /** + * A space delimited string of breakpoint names that specify when the column will be hidden. You can also specify "all" to make a column permanently display in an expandable detail row. + * @type {string} + * @default null + * @example + * breakpoints: "md" + */ + F.Column.prototype.breakpoints = null; + + F.Column.prototype.__breakpoints_define__ = function(definition){ + this.breakpoints = F.is.emptyString(definition.breakpoints) ? null : definition.breakpoints; + }; + + F.Column.extend('define', function(definition){ + this._super(definition); + this.__breakpoints_define__(definition); + }); +})(FooTable); +(function(F){ + /** + * An object containing the breakpoints for the plugin. + * @type {object.} + * @default { "xs": 480, "sm": 768, "md": 992, "lg": 1200 } + */ + F.Defaults.prototype.breakpoints = null; + + /** + * Whether or not breakpoints cascade. When set to true all breakpoints larger than the current will also be hidden along with it. + * @type {boolean} + * @default false + */ + F.Defaults.prototype.cascade = false; + + /** + * Whether or not to calculate breakpoints on the width of the parent element rather than the viewport. + * @type {boolean} + * @default false + */ + F.Defaults.prototype.useParentWidth = false; + + /** + * A function used to override the default getWidth function with a custom one. + * @type {function} + * @default null + * @example + * getWidth: function(instance){ + * if (instance.o.useParentWidth == true) return instance.$el.parent().width(); + * return instance.breakpoints.getViewportWidth(); + * } + */ + F.Defaults.prototype.getWidth = null; +})(FooTable); +(function($, F){ + F.Columns = F.Component.extend(/** @lends FooTable.Columns */{ + /** + * The columns class contains all the logic for handling columns. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} this component belongs to. + * @returns {FooTable.Columns} + */ + construct: function(table){ + // call the base class constructor + this._super(table, true); + + /* PROTECTED */ + /** + * This provides a shortcut to the {@link FooTable.Table#options} object. + * @protected + * @type {FooTable.Table#options} + */ + this.o = table.o; + + /* PUBLIC */ + /** + * An array of {@link FooTable.Column} objects created from parsing the options and/or DOM. + * @type {Array.} + */ + this.array = []; + /** + * The jQuery header row object. + * @type {jQuery} + */ + this.$header = null; + /** + * Whether or not to display the header row. + * @type {boolean} + */ + this.showHeader = table.o.showHeader; + + this._fromHTML = F.is.emptyArray(table.o.columns) && !F.is.promise(table.o.columns); + }, + + /* PROTECTED */ + /** + * This parses the columns from either the tables rows or the supplied options. + * @instance + * @protected + * @param {object} data - The tables jQuery data object. + * @returns {jQuery.Promise} + * @this FooTable.Columns + */ + parse: function(data){ + var self = this; + return $.Deferred(function(d){ + function merge(cols1, cols2){ + var merged = []; + // check if either of the arrays is empty as it can save us having to merge them by index. + if (cols1.length == 0 || cols2.length == 0){ + merged = cols1.concat(cols2); + } else { + // at this point we have two arrays of column definitions, we now need to merge them based on there index properties + // first figure out the highest column index provided so we can loop that many times to merge all columns and provide + // defaults where nothing was specified (fill in the gaps in the array as it were). + var highest = 0; + F.arr.each(cols1.concat(cols2), function(c){ + if (c.index > highest) highest = c.index; + }); + highest++; + for (var i = 0, cols1_c, cols2_c; i < highest; i++){ + cols1_c = {}; + F.arr.each(cols1, function(c){ + if (c.index == i){ + cols1_c = c; + return false; + } + }); + cols2_c = {}; + F.arr.each(cols2, function(c){ + if (c.index == i){ + cols2_c = c; + return false; + } + }); + merged.push($.extend(true, {}, cols1_c, cols2_c)); + } + } + return merged; + } + + var json = [], html = []; + // get the column options from the content + var $header = self.ft.$el.find('tr.footable-header, thead > tr:last:has([data-breakpoints]), tbody > tr:first:has([data-breakpoints]), thead > tr:last, tbody > tr:first').first(), $cell, cdata; + if ($header.length > 0){ + var virtual = $header.parent().is('tbody') && $header.children().length == $header.children('td').length; + if (!virtual) self.$header = $header.addClass('footable-header'); + $header.children('td,th').each(function(i, cell){ + $cell = $(cell); + cdata = $cell.data(); + cdata.index = i; + cdata.$el = $cell; + cdata.virtual = virtual; + html.push(cdata); + }); + if (virtual) self.showHeader = false; + } + // get the supplied column options + if (F.is.array(self.o.columns) && !F.is.emptyArray(self.o.columns)){ + F.arr.each(self.o.columns, function(c, i){ + c.index = i; + json.push(c); + }); + self.parseFinalize(d, merge(json, html)); + } else if (F.is.promise(self.o.columns)){ + self.o.columns.then(function(cols){ + F.arr.each(cols, function(c, i){ + c.index = i; + json.push(c); + }); + self.parseFinalize(d, merge(json, html)); + }, function(xhr){ + d.reject(Error('Columns ajax request error: ' + xhr.status + ' (' + xhr.statusText + ')')); + }); + } else { + self.parseFinalize(d, merge(json, html)); + } + }); + }, + /** + * Used to finalize the parsing of columns it is supplied the parse deferred object which must be resolved with an array of {@link FooTable.Column} objects + * or rejected with an error. + * @instance + * @protected + * @param {jQuery.Deferred} deferred - The deferred object used for parsing. + * @param {Array.} cols - An array of all merged column definitions. + */ + parseFinalize: function(deferred, cols){ + // we now have a merged array of all column definitions supplied to the plugin, time to make the objects. + var self = this, columns = [], column; + F.arr.each(cols, function(def){ + // if we have a column registered using the definition type then create an instance of that column otherwise just create a default text column. + if (column = F.columns.contains(def.type) ? F.columns.make(def.type, self.ft, def) : new F.Column(self.ft, def)) + columns.push(column); + }); + if (F.is.emptyArray(columns)){ + deferred.reject(Error("No columns supplied.")); + } else { + // make sure to sort by the column index as the merge process may have mixed them up + columns.sort(function(a, b){ return a.index - b.index; }); + deferred.resolve(columns); + } + }, + /** + * The columns preinit method is used to parse and check the column options supplied from both static content and through the constructor. + * @instance + * @protected + * @param {object} data - The jQuery data object from the root table element. + * @this FooTable.Columns + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.columns event is raised before any UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Columns#"preinit.ft.columns" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + return self.ft.raise('preinit.ft.columns', [data]).then(function(){ + return self.parse(data).then(function(columns){ + self.array = columns; + self.showHeader = F.is.boolean(data.showHeader) ? data.showHeader : self.showHeader; + }); + }); + }, + /** + * Initializes the columns creating the table header if required. + * @instance + * @protected + * @fires FooTable.Columns#"init.ft.columns" + * @this FooTable.Columns + */ + init: function(){ + var self = this; + /** + * The init.ft.columns event is raised after the header row is created/parsed for column data. + * @event FooTable.Columns#"init.ft.columns" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} instance - The instance of the plugin raising the event. + * @param {Array.} columns - The array of {@link FooTable.Column} objects parsed from the options and/or DOM. + */ + return this.ft.raise('init.ft.columns', [ self.array ]).then(function(){ + self.$create(); + }); + }, + /** + * Destroys the columns component removing any UI generated from the table. + * @instance + * @protected + * @fires FooTable.Columns#"destroy.ft.columns" + */ + destroy: function(){ + /** + * The destroy.ft.columns event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Columns#"destroy.ft.columns" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('destroy.ft.columns').then(function(){ + if (!self._fromHTML) self.$header.remove(); + }); + }, + /** + * The predraw method called from within the {@link FooTable.Table#draw} method. + * @instance + * @protected + * @this FooTable.Columns + */ + predraw: function(){ + var self = this, first = true; + self.visibleColspan = 0; + self.firstVisibleIndex = 0; + self.lastVisibleIndex = 0; + self.hasHidden = false; + F.arr.each(self.array, function(col){ + col.hidden = !self.ft.breakpoints.visible(col.breakpoints); + if (!col.hidden && col.visible){ + if (first){ + self.firstVisibleIndex = col.index; + first = false; + } + self.lastVisibleIndex = col.index; + self.visibleColspan++; + } + if (col.hidden) self.hasHidden = true; + }); + self.ft.$el.toggleClass('breakpoint', self.hasHidden); + }, + /** + * Performs the actual drawing of the columns, hiding or displaying them depending on there breakpoints. + * @instance + * @protected + * @this FooTable.Columns + */ + draw: function(){ + F.arr.each(this.array, function(col){ + col.$el.css('display', (col.hidden || !col.visible ? 'none' : 'table-cell')); + }); + if (!this.showHeader && F.is.jq(this.$header.parent())){ + this.$header.detach(); + } + }, + /** + * Creates the header row for the table from the parsed column definitions. + * @instance + * @protected + * @this FooTable.Columns + */ + $create: function(){ + var self = this; + self.$header = F.is.jq(self.$header) ? self.$header : $('', {'class': 'footable-header'}); + self.$header.children('th,td').detach(); + F.arr.each(self.array, function(col){ + self.$header.append(col.$el); + }); + if (self.showHeader && !F.is.jq(self.$header.parent())){ + self.ft.$el.children('thead').append(self.$header); + } + }, + /** + * Attempts to return a {@link FooTable.Column} instance when passed the {@link FooTable.Column} instance, the {@link FooTable.Column#name} string or the {@link FooTable.Column#index} number. + * If supplied a function this will return an array by iterating all columns passing the index and column itself to the supplied callback as arguments. + * Returning true in the callback will include the column in the result. + * @instance + * @param {(FooTable.Column|string|number|function)} column - The column to retrieve. + * @returns {(Array.|FooTable.Column|null)} The column if one is found otherwise it returns NULL. + * @example + * var column = columns.get('id'); + * if (column instanceof FooTable.Column){ + * // found the "id" column + * } else { + * // no column with a name of "id" exists + * } + * // to get an array of all hidden columns + * var columns = columns.get(function(col){ + * return col.hidden; + * }); + */ + get: function(column){ + if (column instanceof F.Column) return column; + if (F.is.string(column)) return F.arr.first(this.array, function (col) { return col.name == column; }); + if (F.is.number(column)) return F.arr.first(this.array, function (col) { return col.index == column; }); + if (F.is.fn(column)) return F.arr.get(this.array, column); + return null; + }, + /** + * Takes an array of column names, index's or actual {@link FooTable.Column} and ensures that an array of only {@link FooTable.Column} is returned. + * @instance + * @param {(Array.|Array.|Array.)} columns - The array of column names, index's or {@link FooTable.Column} to check. + * @returns {Array.} + */ + ensure: function(columns){ + var self = this, result = []; + if (!F.is.array(columns)) return result; + F.arr.each(columns, function(name){ + result.push(self.get(name)); + }); + return result; + } + }); + + F.components.register('columns', F.Columns, 900); + +})(jQuery, FooTable); +(function(F){ + /** + * An array containing the column options or a jQuery promise that resolves returning the columns. The index of the definitions must match the index of each column as it should appear in the table. For more information on the options available see the {@link FooTable.Column} object. + * @type {(Array.|jQuery.Promise)} + * @default [] + * @example + * columns: [ + * { name: 'id', title: 'ID', type: 'number' }, + * { name: 'name', title: 'Name', sorted: true, direction: 'ASC' } + * { name: 'age', title: 'Age', type: 'number', breakpoints: 'xs' } + * ] + */ + F.Defaults.prototype.columns = []; + + /** + * Specifies whether or not the column headers should be displayed. + * @type {boolean} + * @default true + */ + F.Defaults.prototype.showHeader = true; +})(FooTable); +(function ($, F) { + F.Rows = F.Component.extend(/** @lends FooTable.Rows */{ + /** + * The rows class contains all the logic for handling rows. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} this component belongs to. + * @returns {FooTable.Rows} + */ + construct: function (table) { + // call the base class constructor + this._super(table, true); + + /** + * This provides a shortcut to the {@link FooTable.Table#options} object. + * @instance + * @protected + * @type {FooTable.Table#options} + */ + this.o = table.o; + /** + * The current working array of {@link FooTable.Row} objects. + * @instance + * @protected + * @type {Array.} + * @default [] + */ + this.array = []; + /** + * The base array of rows parsed from either the DOM or the constructor options. + * The {@link FooTable.Rows#current} member is populated with a shallow clone of this array + * during the predraw operation before any core or custom components are executed. + * @instance + * @protected + * @type {Array.} + * @default [] + */ + this.all = []; + /** + * Whether or not to display a toggle in each row when it contains hidden columns. + * @type {boolean} + * @default true + */ + this.showToggle = table.o.showToggle; + /** + * The CSS selector used to filter row click events. If the event.target property matches the selector the row will be toggled. + * @type {string} + * @default "tr,td,.footable-toggle" + */ + this.toggleSelector = table.o.toggleSelector; + /** + * Specifies which column the row toggle is appended to. Supports only two values; "first" and "last" + * @type {string} + */ + this.toggleColumn = table.o.toggleColumn; + /** + * The text to display when the table has no rows. + * @type {string} + */ + this.emptyString = table.o.empty; + /** + * Whether or not the first rows details are expanded by default when displayed on a device that hides any columns. + * @type {boolean} + */ + this.expandFirst = table.o.expandFirst; + /** + * Whether or not all row details are expanded by default when displayed on a device that hides any columns. + * @type {boolean} + */ + this.expandAll = table.o.expandAll; + /** + * The jQuery object that contains the empty row control. + * @type {jQuery} + */ + this.$empty = null; + this._fromHTML = F.is.emptyArray(table.o.rows) && !F.is.promise(table.o.rows); + }, + /** + * This parses the rows from either the tables rows or the supplied options. + * @instance + * @protected + * @returns {jQuery.Promise} + */ + parse: function(){ + var self = this; + return $.Deferred(function(d){ + var $rows = self.ft.$el.children('tbody').children('tr'); + if (F.is.array(self.o.rows) && self.o.rows.length > 0){ + self.parseFinalize(d, self.o.rows); + } else if (F.is.promise(self.o.rows)){ + self.o.rows.then(function(rows){ + self.parseFinalize(d, rows); + }, function(xhr){ + d.reject(Error('Rows ajax request error: ' + xhr.status + ' (' + xhr.statusText + ')')); + }); + } else if (F.is.jq($rows)){ + self.parseFinalize(d, $rows); + $rows.detach(); + } else { + self.parseFinalize(d, []); + } + }); + }, + /** + * Used to finalize the parsing of rows it is supplied the parse deferred object which must be resolved with an array of {@link FooTable.Row} objects + * or rejected with an error. + * @instance + * @protected + * @param {jQuery.Deferred} deferred - The deferred object used for parsing. + * @param {(Array.|jQuery)} rows - An array of row values and options or the jQuery object containing all rows. + */ + parseFinalize: function(deferred, rows){ + var self = this, result = $.map(rows, function(r){ + return new F.Row(self.ft, self.ft.columns.array, r); + }); + deferred.resolve(result); + }, + /** + * The columns preinit method is used to parse and check the column options supplied from both static content and through the constructor. + * @instance + * @protected + * @param {object} data - The jQuery data object from the root table element. + * @fires FooTable.Rows#"preinit.ft.rows" + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.rows event is raised before any UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Rows#"preinit.ft.rows" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + return self.ft.raise('preinit.ft.rows', [data]).then(function(){ + return self.parse().then(function(rows){ + self.all = rows; + self.array = self.all.slice(0); + self.showToggle = F.is.boolean(data.showToggle) ? data.showToggle : self.showToggle; + self.toggleSelector = F.is.string(data.toggleSelector) ? data.toggleSelector : self.toggleSelector; + self.toggleColumn = F.is.string(data.toggleColumn) ? data.toggleColumn : self.toggleColumn; + if (self.toggleColumn != "first" && self.toggleColumn != "last") self.toggleColumn = "first"; + self.emptyString = F.is.string(data.empty) ? data.empty : self.emptyString; + self.expandFirst = F.is.boolean(data.expandFirst) ? data.expandFirst : self.expandFirst; + self.expandAll = F.is.boolean(data.expandAll) ? data.expandAll : self.expandAll; + }); + }); + }, + /** + * Initializes the rows class using the supplied table and options. + * @instance + * @protected + * @fires FooTable.Rows#"init.ft.rows" + */ + init: function () { + var self = this; + /** + * The init.ft.rows event is raised after the the rows are parsed from either the DOM or the options. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Rows#"init.ft.rows" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} instance - The instance of the plugin raising the event. + * @param {Array.} rows - The array of {@link FooTable.Row} objects parsed from the DOM or the options. + */ + return self.ft.raise('init.ft.rows', [self.all]).then(function(){ + self.$create(); + }); + }, + /** + * Destroys the rows component removing any UI generated from the table. + * @instance + * @protected + * @fires FooTable.Rows#"destroy.ft.rows" + */ + destroy: function(){ + /** + * The destroy.ft.rows event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Rows#"destroy.ft.rows" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('destroy.ft.rows').then(function(){ + F.arr.each(self.array, function(row){ + row.predraw(!self._fromHTML); + }); + }); + }, + /** + * Performs the predraw operations that are required including creating the shallow clone of the {@link FooTable.Rows#array} to work with. + * @instance + * @protected + */ + predraw: function(){ + F.arr.each(this.array, function(row){ + row.predraw(); + }); + this.array = this.all.slice(0); + }, + $create: function(){ + this.$empty = $('', { 'class': 'footable-empty' }).append($('").addClass(this.row.classes.join(" ")).data("__FooTableCell__",this).append(a("")).data("__FooTableRow__",this),this._setClasses(this.$el),this._setStyle(this.$el),"last"==this.ft.rows.toggleColumn&&this.$toggle.addClass("last-column"),this.$details=a("",{"class":"footable-detail-row"}).append(a("
    The below shows how this value would be setThe below shows what the default getWidth function would look like.
    This example shows retrieving a column by name assuming a column called "id" exists. The columns object is an instance of {@link FooTable.Columns}.The below shows column definitions for a row defined as { id: Number, name: String, age: Number }. The ID column has a fixed width, the table is initially sorted on the Name column and the Age column will be hidden on phones.
    ').text(this.emptyString)); + }, + /** + * Performs the actual drawing of the table rows. + * @instance + * @protected + */ + draw: function(){ + var self = this, $tbody = self.ft.$el.children('tbody'), first = true; + // if we have rows + if (self.array.length > 0){ + self.$empty.detach(); + // loop through them appending to the tbody and then drawing + F.arr.each(self.array, function(row){ + if ((self.expandFirst && first) || self.expandAll){ + row.expanded = true; + first = false; + } + row.draw($tbody); + }); + } else { + // otherwise display the $empty row + self.$empty.children('td').attr('colspan', self.ft.columns.visibleColspan); + $tbody.append(self.$empty); + } + }, + /** + * Loads a JSON array of row objects into the table + * @instance + * @param {Array.} data - An array of row objects to load. + * @param {boolean} [append=false] - Whether or not to append the new rows to the current rows array or to replace them entirely. + */ + load: function(data, append){ + var self = this, rows = $.map(data, function(r){ + return new F.Row(self.ft, self.ft.columns.array, r); + }); + F.arr.each(this.array, function(row){ + row.predraw(); + }); + this.all = (F.is.boolean(append) ? append : false) ? this.all.concat(rows) : rows; + this.array = this.all.slice(0); + this.ft.draw(); + }, + /** + * Expands all visible rows. + * @instance + */ + expand: function(){ + F.arr.each(this.array, function(row){ + row.expand(); + }); + }, + /** + * Collapses all visible rows. + * @instance + */ + collapse: function(){ + F.arr.each(this.array, function(row){ + row.collapse(); + }); + } + }); + + F.components.register('rows', F.Rows, 800); + +})(jQuery, FooTable); +(function(F){ + /** + * An array of JSON objects containing the row data or a jQuery promise that resolves returning the row data. + * @type {(Array.|jQuery.Promise)} + * @default [] + */ + F.Defaults.prototype.rows = []; + + /** + * A string to display when there are no rows in the table. + * @type {string} + * @default "No results" + */ + F.Defaults.prototype.empty = 'No results'; + + /** + * Whether or not the toggle is appended to each row. + * @type {boolean} + * @default true + */ + F.Defaults.prototype.showToggle = true; + + /** + * The CSS selector used to filter row click events. If the event.target property matches the selector the row will be toggled. + * @type {string} + * @default "tr,td,.footable-toggle" + */ + F.Defaults.prototype.toggleSelector = 'tr,td,.footable-toggle'; + + /** + * Specifies which column to display the row toggle in. The only supported values are "first" or "last". + * @type {string} + * @default "first" + */ + F.Defaults.prototype.toggleColumn = 'first'; + + /** + * Whether or not the first rows details are expanded by default when displayed on a device that hides any columns. + * @type {boolean} + */ + F.Defaults.prototype.expandFirst = false; + + /** + * Whether or not all row details are expanded by default when displayed on a device that hides any columns. + * @type {boolean} + */ + F.Defaults.prototype.expandAll = false; +})(FooTable); +(function(F){ + /** + * Loads a JSON array of row objects into the table + * @param {Array.} data - An array of row objects to load. + * @param {boolean} [append=false] - Whether or not to append the new rows to the current rows array or to replace them entirely. + */ + F.Table.prototype.loadRows = function(data, append){ + this.rows.load(data, append); + }; +})(FooTable); \ No newline at end of file diff --git a/media/footable-v3/js/footable.core.min.js b/media/footable-v3/js/footable.core.min.js new file mode 100644 index 0000000..c452f54 --- /dev/null +++ b/media/footable-v3/js/footable.core.min.js @@ -0,0 +1,9 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +!function(a,b){window.console=window.console||{log:function(){},error:function(){}},a.fn.footable=function(a,c){return a=a||{},this.filter("table").each(function(d,e){b.init(e,a,c)})};var c={events:[]};b.__debug__=JSON.parse(localStorage.getItem("footable_debug"))||!1,b.__debug_options__=JSON.parse(localStorage.getItem("footable_debug_options"))||c,b.debug=function(d,e){return b.is["boolean"](d)?(b.__debug__=d,void(b.__debug__?(localStorage.setItem("footable_debug",JSON.stringify(b.__debug__)),b.__debug_options__=a.extend(!0,{},c,e||{}),b.is.hash(e)&&localStorage.setItem("footable_debug_options",JSON.stringify(b.__debug_options__))):(localStorage.removeItem("footable_debug"),localStorage.removeItem("footable_debug_options")))):b.__debug__},b.get=function(b){return a(b).first().data("__FooTable__")},b.init=function(a,c,d){var e=b.get(a);return e instanceof b.Table&&e.destroy(),new b.Table(a,c,d)},b.getRow=function(b){var c=a(b).closest("tr");return c.hasClass("footable-detail-row")&&(c=c.prev()),c.data("__FooTableRow__")}}(jQuery,FooTable=window.FooTable||{}),function(a){var b=function(){return!0};a.arr={},a.arr.each=function(b,c){if(a.is.array(b)&&a.is.fn(c))for(var d=0,e=b.length;e>d&&c(b[d],d)!==!1;d++);},a.arr.get=function(b,c){var d=[];if(!a.is.array(b))return d;if(!a.is.fn(c))return b;for(var e=0,f=b.length;f>e;e++)c(b[e],e)&&d.push(b[e]);return d},a.arr.any=function(c,d){if(!a.is.array(c))return!1;d=a.is.fn(d)?d:b;for(var e=0,f=c.length;f>e;e++)if(d(c[e],e))return!0;return!1},a.arr.contains=function(b,c){if(!a.is.array(b)||a.is.undef(c))return!1;for(var d=0,e=b.length;e>d;d++)if(b[d]==c)return!0;return!1},a.arr.first=function(c,d){if(!a.is.array(c))return null;d=a.is.fn(d)?d:b;for(var e=0,f=c.length;f>e;e++)if(d(c[e],e))return c[e];return null},a.arr.map=function(b,c){var d=[],e=null;if(!a.is.array(b)||!a.is.fn(c))return d;for(var f=0,g=b.length;g>f;f++)null!=(e=c(b[f],f))&&d.push(e);return d},a.arr.remove=function(b,c){var d=[],e=[];if(!a.is.array(b)||!a.is.fn(c))return e;for(var f=0,g=b.length;g>f;f++)c(b[f],f,e)&&(d.push(f),e.push(b[f]));for(d.sort(function(a,b){return b-a}),f=0,g=d.length;g>f;f++){var h=d[f]-f;b.splice(h,1)}return e},a.arr["delete"]=function(b,c){var d=-1,e=null;if(!a.is.array(b)||a.is.undef(c))return e;for(var f=0,g=b.length;g>f;f++)if(b[f]==c){d=f,e=b[f];break}return-1!=d&&b.splice(d,1),e},a.arr.replace=function(a,b,c){var d=a.indexOf(b);-1!==d&&(a[d]=c)}}(FooTable),function(a){a.is={},a.is.type=function(a,b){return typeof a===b},a.is.defined=function(a){return"undefined"!=typeof a},a.is.undef=function(a){return"undefined"==typeof a},a.is.array=function(a){return"[object Array]"===Object.prototype.toString.call(a)},a.is.date=function(a){return"[object Date]"===Object.prototype.toString.call(a)&&!isNaN(a.getTime())},a.is["boolean"]=function(a){return"[object Boolean]"===Object.prototype.toString.call(a)},a.is.string=function(a){return"[object String]"===Object.prototype.toString.call(a)},a.is.number=function(a){return"[object Number]"===Object.prototype.toString.call(a)&&!isNaN(a)},a.is.fn=function(b){return a.is.defined(window)&&b===window.alert||"[object Function]"===Object.prototype.toString.call(b)},a.is.error=function(a){return"[object Error]"===Object.prototype.toString.call(a)},a.is.object=function(a){return"[object Object]"===Object.prototype.toString.call(a)},a.is.hash=function(b){return a.is.object(b)&&b.constructor===Object&&!b.nodeType&&!b.setInterval},a.is.element=function(a){return"object"==typeof HTMLElement?a instanceof HTMLElement:a&&"object"==typeof a&&null!==a&&1===a.nodeType&&"string"==typeof a.nodeName},a.is.promise=function(b){return a.is.object(b)&&a.is.fn(b.then)&&a.is.fn(b.promise)},a.is.jq=function(b){return a.is.defined(window.jQuery)&&b instanceof jQuery&&b.length>0},a.is.moment=function(b){return a.is.defined(window.moment)&&a.is.object(b)&&a.is["boolean"](b._isAMomentObject)},a.is.emptyObject=function(b){if(!a.is.hash(b))return!1;for(var c in b)if(b.hasOwnProperty(c))return!1;return!0},a.is.emptyArray=function(b){return a.is.array(b)?0===b.length:!0},a.is.emptyString=function(b){return a.is.string(b)?0===b.length:!0}}(FooTable),function(a){a.str={},a.str.contains=function(b,c,d){return a.is.emptyString(b)||a.is.emptyString(c)?!1:c.length<=b.length&&-1!==(d?b.toUpperCase().indexOf(c.toUpperCase()):b.indexOf(c))},a.str.containsExact=function(b,c,d){return a.is.emptyString(b)||a.is.emptyString(c)||c.length>b.length?!1:new RegExp("\\b"+a.str.escapeRegExp(c)+"\\b",d?"i":"").test(b)},a.str.containsWord=function(b,c,d){if(a.is.emptyString(b)||a.is.emptyString(c)||b.lengthf;f++)if(d?e[f].toUpperCase()==c.toUpperCase():e[f]==c)return!0;return!1},a.str.from=function(b,c){return a.is.emptyString(b)?b:a.str.contains(b,c)?b.substring(b.indexOf(c)+1):b},a.str.startsWith=function(b,c){return a.is.emptyString(b)?b==c:b.slice(0,c.length)==c},a.str.toCamelCase=function(b){return a.is.emptyString(b)?b:b.toUpperCase()===b?b.toLowerCase():b.replace(/^([A-Z])|[-\s_](\w)/g,function(b,c,d){return a.is.string(d)?d.toUpperCase():c.toLowerCase()})},a.str.random=function(b){return b=a.is.emptyString(b)?"":b,b+Math.random().toString(36).substr(2,9)},a.str.escapeRegExp=function(b){return a.is.emptyString(b)?b:b.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}}(FooTable),function(a){"use strict";function b(){}Object.create||(Object.create=function(){var b=function(){};return function(c){if(arguments.length>1)throw Error("Second argument not supported");if(!a.is.object(c))throw TypeError("Argument must be an object");b.prototype=c;var d=new b;return b.prototype=null,d}}());var c=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;b.__extend__=function(b,d,e,f){b[d]=a.is.fn(f)&&c.test(e)?function(a,b){return function(){var a,c;return a=this._super,this._super=f,c=b.apply(this,arguments),this._super=a,c}}(d,e):e},b.extend=function(d,e){function f(b,d,e,f){b[d]=a.is.fn(f)&&c.test(e)?function(a,b,c){return function(){var a,d;return a=this._super,this._super=c,d=b.apply(this,arguments),this._super=a,d}}(d,e,f):e}var g=Array.prototype.slice.call(arguments);if(d=g.shift(),e=g.shift(),a.is.hash(d)){var h=Object.create(this.prototype),i=this.prototype;for(var j in d)"__ctor__"!==j&&f(h,j,d[j],i[j]);var k=a.is.fn(h.__ctor__)?h.__ctor__:function(){if(!a.is.fn(this.construct))throw new SyntaxError('FooTable class objects must be constructed with the "new" keyword.');this.construct.apply(this,arguments)};return h.construct=a.is.fn(h.construct)?h.construct:function(){},k.prototype=h,h.constructor=k,k.extend=b.extend,k}a.is.string(d)&&a.is.fn(e)&&f(this.prototype,d,e,this.prototype[d])},a.Class=b,a.ClassFactory=a.Class.extend({construct:function(){this.registered={}},contains:function(b){return a.is.defined(this.registered[b])},names:function(){var a,b=[];for(a in this.registered)this.registered.hasOwnProperty(a)&&b.push(a);return b},register:function(b,c,d){if(a.is.string(b)&&a.is.fn(c)){var e=this.registered[b];this.registered[b]={name:b,klass:c,priority:a.is.number(d)?d:a.is.defined(e)?e.priority:0}}},load:function(b,c,d){var e,f,g=this,h=Array.prototype.slice.call(arguments),i=[],j=[];b=h.shift()||{};for(e in g.registered)if(g.registered.hasOwnProperty(e)){var k=g.registered[e];b.hasOwnProperty(e)&&(f=b[e],a.is.string(f)&&(f=a.getFnPointer(b[e])),a.is.fn(f)&&(k={name:e,klass:f,priority:g.registered[e].priority})),i.push(k)}for(e in b)b.hasOwnProperty(e)&&!g.registered.hasOwnProperty(e)&&(f=b[e],a.is.string(f)&&(f=a.getFnPointer(b[e])),a.is.fn(f)&&i.push({name:e,klass:f,priority:0}));return i.sort(function(a,b){return b.priority-a.priority}),a.arr.each(i,function(b){a.is.fn(b.klass)&&j.push(g._make(b.klass,h))}),j},make:function(b,c,d){var e,f=this,g=Array.prototype.slice.call(arguments);return b=g.shift(),e=f.registered[b],a.is.fn(e.klass)?f._make(e.klass,g):null},_make:function(a,b){function c(){return a.apply(this,b)}return c.prototype=a.prototype,new c}})}(FooTable),function(a,b){b.css2json=function(c){if(b.is.emptyString(c))return{};for(var d,e,f,g={},h=c.split(";"),i=0,j=h.length;j>i;i++)b.is.emptyString(h[i])||(d=h[i].split(":"),b.is.emptyString(d[0])||b.is.emptyString(d[1])||(e=b.str.toCamelCase(a.trim(d[0])),f=a.trim(d[1]),g[e]=f));return g},b.getFnPointer=function(a){if(b.is.emptyString(a))return null;var c=window,d=a.split(".");return b.arr.each(d,function(a){c[a]&&(c=c[a])}),b.is.fn(c)?c:null},b.checkFnValue=function(a,c,d){function e(a,c,d){return b.is.fn(c)?function(){return c.apply(a,arguments)}:d}return d=b.is.fn(d)?d:null,b.is.fn(c)?e(a,c,d):b.is.type(c,"string")?e(a,b.getFnPointer(c),d):d}}(jQuery,FooTable),function(a,b){b.Cell=b.Class.extend({construct:function(a,b,c,d){this.ft=a,this.row=b,this.column=c,this.created=!1,this.define(d)},define:function(c){this.$el=b.is.element(c)||b.is.jq(c)?a(c):null,this.$detail=null;var d=b.is.hash(c)&&b.is.hash(c.options)&&b.is.defined(c.value);this.value=this.column.parser.call(this.column,b.is.jq(this.$el)?this.$el:d?c.value:c,this.ft.o),this.o=a.extend(!0,{classes:null,style:null},d?c.options:{}),this.classes=b.is.jq(this.$el)&&this.$el.attr("class")?this.$el.attr("class").match(/\S+/g):b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.jq(this.$el)&&this.$el.attr("style")?b.css2json(this.$el.attr("style")):b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{}},$create:function(){this.created||((this.$el=b.is.jq(this.$el)?this.$el:a("")).data("value",this.value).contents().detach().end().append(this.format(this.value)),this._setClasses(this.$el),this._setStyle(this.$el),this.$detail=a("
    ")).append(a("")),this.created=!0)},collapse:function(){this.created&&(this.$detail.children("th").html(this.column.title),this.$el.clone().attr("id",this.$el.attr("id")?this.$el.attr("id")+"-detail":void 0).css("display","table-cell").html("").append(this.$el.contents().detach()).replaceAll(this.$detail.children("td").first()),b.is.jq(this.$detail.parent())||this.$detail.appendTo(this.row.$details.find(".footable-details > tbody")))},restore:function(){if(this.created){if(b.is.jq(this.$detail.parent())){var a=this.$detail.children("td").first();this.$el.attr("class",a.attr("class")).attr("style",a.attr("style")).css("display",this.column.hidden||!this.column.visible?"none":"table-cell").append(a.contents().detach())}this.$detail.detach()}},parse:function(){return this.column.parser.call(this.column,this.$el,this.ft.o)},format:function(a){return this.column.formatter.call(this.column,a,this.ft.o)},val:function(c,d){if(b.is.undef(c))return this.value;var e=this,f=b.is.hash(c)&&b.is.hash(c.options)&&b.is.defined(c.value);if(this.o=a.extend(!0,{classes:e.classes,style:e.style},f?c.options:{}),this.value=f?c.value:c,this.classes=b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},this.created){this.$el.data("value",this.value).empty();var g=this.$detail.children("td").first().empty(),h=b.is.jq(this.$detail.parent())?g:this.$el;h.append(this.format(this.value)),this._setClasses(h),this._setStyle(h),(b.is["boolean"](d)?d:!0)&&this.row.draw()}},_setClasses:function(a){var c=!b.is.emptyArray(this.column.classes),d=!b.is.emptyArray(this.classes),e=null;a.removeAttr("class"),(c||d)&&(c&&d?e=this.classes.concat(this.column.classes).join(" "):c?e=this.column.classes.join(" "):d&&(e=this.classes.join(" ")),b.is.emptyString(e)||a.addClass(e))},_setStyle:function(c){var d=!b.is.emptyObject(this.column.style),e=!b.is.emptyObject(this.style),f=null;c.removeAttr("style"),(d||e)&&(d&&e?f=a.extend({},this.column.style,this.style):d?f=this.column.style:e&&(f=this.style),b.is.hash(f)&&c.css(f))}})}(jQuery,FooTable),function(a,b){b.Column=b.Class.extend({construct:function(a,c,d){this.ft=a,this.type=b.is.emptyString(d)?"text":d,this.virtual=b.is["boolean"](c.virtual)?c.virtual:!1,this.$el=b.is.jq(c.$el)?c.$el:null,this.index=b.is.number(c.index)?c.index:-1,this.define(c),this.$create()},define:function(a){this.hidden=b.is["boolean"](a.hidden)?a.hidden:!1,this.visible=b.is["boolean"](a.visible)?a.visible:!0,this.name=b.is.string(a.name)?a.name:null,null==this.name&&(this.name="col"+(a.index+1)),this.title=b.is.string(a.title)?a.title:null,!this.virtual&&null==this.title&&b.is.jq(this.$el)&&(this.title=this.$el.html()),null==this.title&&(this.title="Column "+(a.index+1)),this.style=b.is.hash(a.style)?a.style:b.is.string(a.style)?b.css2json(a.style):{},this.classes=b.is.array(a.classes)?a.classes:b.is.string(a.classes)?a.classes.match(/\S+/g):[],this.parser=b.checkFnValue(this,a.parser,this.parser),this.formatter=b.checkFnValue(this,a.formatter,this.formatter)},$create:function(){(this.$el=!this.virtual&&b.is.jq(this.$el)?this.$el:a("")).html(this.title).addClass(this.classes.join(" ")).css(this.style)},parser:function(c){if(b.is.element(c)||b.is.jq(c)){var d=a(c).data("value");return b.is.defined(d)?d:a(c).html()}return b.is.defined(c)&&null!=c?c+"":null},formatter:function(a){return null==a?"":a},createCell:function(a){var c=b.is.jq(a.$el)?a.$el.children("td,th").get(this.index):null,d=b.is.hash(a.value)?a.value[this.name]:null;return new b.Cell(this.ft,a,this,c||d)}}),b.columns=new b.ClassFactory,b.columns.register("text",b.Column)}(jQuery,FooTable),function(a,b){b.Component=b.Class.extend({construct:function(a,c){if(!(a instanceof b.Table))throw new TypeError("The instance parameter must be an instance of FooTable.Table.");this.ft=a,this.enabled=b.is["boolean"](c)?c:!1},preinit:function(a){},init:function(){},destroy:function(){},predraw:function(){},draw:function(){},postdraw:function(){}}),b.components=new b.ClassFactory}(jQuery,FooTable),function(a,b){b.Defaults=function(){this.stopPropagation=!1,this.on=null},b.defaults=new b.Defaults}(jQuery,FooTable),function(a,b){b.Row=b.Class.extend({construct:function(a,b,c){this.ft=a,this.columns=b,this.created=!1,this.define(c)},define:function(c){this.$el=b.is.element(c)||b.is.jq(c)?a(c):null,this.$toggle=a("",{"class":"footable-toggle fooicon fooicon-plus"});var d=b.is.hash(c),e=d&&b.is.hash(c.options)&&b.is.hash(c.value);this.value=d?e?c.value:c:null,this.o=a.extend(!0,{expanded:!1,classes:null,style:null},e?c.options:{}),this.expanded=b.is.jq(this.$el)?this.$el.data("expanded")||this.o.expanded:this.o.expanded,this.classes=b.is.jq(this.$el)&&this.$el.attr("class")?this.$el.attr("class").match(/\S+/g):b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.jq(this.$el)&&this.$el.attr("style")?b.css2json(this.$el.attr("style")):b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},this.cells=this.createCells();var f=this;f.value={},b.arr.each(f.cells,function(a){f.value[a.column.name]=a.val()})},$create:function(){if(!this.created){(this.$el=b.is.jq(this.$el)?this.$el:a("
    ",{colspan:this.ft.columns.visibleColspan}).append(a("",{"class":"footable-details "+this.ft.classes.join(" ")}).append("")));var c=this;b.arr.each(c.cells,function(a){a.created||a.$create(),c.$el.append(a.$el)}),c.$el.off("click.ft.row").on("click.ft.row",{self:c},c._onToggle),this.created=!0}},createCells:function(){var a=this;return b.arr.map(a.columns,function(b){return b.createCell(a)})},val:function(c,d){var e=this;if(!b.is.hash(c))return b.is.hash(this.value)&&!b.is.emptyObject(this.value)||(this.value={},b.arr.each(this.cells,function(a){e.value[a.column.name]=a.val()})),this.value;this.collapse(!1);var f=b.is.hash(c),g=f&&b.is.hash(c.options)&&b.is.hash(c.value);if(this.o=a.extend(!0,{expanded:e.expanded,classes:e.classes,style:e.style},g?c.options:{}),this.expanded=this.o.expanded,this.classes=b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},f)if(g&&(c=c.value),b.is.hash(this.value))for(var h in c)c.hasOwnProperty(h)&&(this.value[h]=c[h]);else this.value=c;else this.value=null;b.arr.each(this.cells,function(a){b.is.defined(e.value[a.column.name])&&a.val(e.value[a.column.name],!1)}),this.created&&(this._setClasses(this.$el),this._setStyle(this.$el),(b.is["boolean"](d)?d:!0)&&this.draw())},_setClasses:function(a){var c=!b.is.emptyArray(this.classes),d=null;a.removeAttr("class"),c&&(d=this.classes.join(" "),b.is.emptyString(d)||a.addClass(d))},_setStyle:function(a){var c=!b.is.emptyObject(this.style),d=null;a.removeAttr("style"),c&&(d=this.style,b.is.hash(d)&&a.css(d))},expand:function(){if(this.created){var a=this;a.ft.raise("expand.ft.row",[a]).then(function(){a.__hidden__=b.arr.map(a.cells,function(a){return a.column.hidden&&a.column.visible?a:null}),a.__hidden__.length>0&&(a.$details.insertAfter(a.$el).children("td").first().attr("colspan",a.ft.columns.visibleColspan),b.arr.each(a.__hidden__,function(a){a.collapse()})),a.$el.attr("data-expanded",!0),a.$toggle.removeClass("fooicon-plus").addClass("fooicon-minus"),a.expanded=!0})}},collapse:function(a){if(this.created){var c=this;c.ft.raise("collapse.ft.row",[c]).then(function(){b.arr.each(c.__hidden__,function(a){a.restore()}),c.$details.detach(),c.$el.removeAttr("data-expanded"),c.$toggle.removeClass("fooicon-minus").addClass("fooicon-plus"),(b.is["boolean"](a)?a:!0)&&(c.expanded=!1)})}},predraw:function(a){this.created&&(this.expanded&&this.collapse(!1),this.$toggle.detach(),a=b.is["boolean"](a)?a:!0,a&&this.$el.detach())},draw:function(a){this.created||this.$create(),b.is.jq(a)&&a.append(this.$el);var c=this;b.arr.each(c.cells,function(a){a.$el.css("display",a.column.hidden||!a.column.visible?"none":"table-cell"),c.ft.rows.showToggle&&c.ft.columns.hasHidden&&("first"==c.ft.rows.toggleColumn&&a.column.index==c.ft.columns.firstVisibleIndex||"last"==c.ft.rows.toggleColumn&&a.column.index==c.ft.columns.lastVisibleIndex)&&a.$el.prepend(c.$toggle),a.$el.add(a.column.$el).removeClass("footable-first-visible footable-last-visible"),a.column.index==c.ft.columns.firstVisibleIndex&&a.$el.add(a.column.$el).addClass("footable-first-visible"),a.column.index==c.ft.columns.lastVisibleIndex&&a.$el.add(a.column.$el).addClass("footable-last-visible")}),this.expanded&&this.expand()},toggle:function(){this.created&&this.ft.columns.hasHidden&&(this.expanded?this.collapse():this.expand())},_onToggle:function(b){var c=b.data.self;a(b.target).is(c.ft.rows.toggleSelector)&&c.toggle()}})}(jQuery,FooTable),function(a,b){b.instances=[],b.Table=b.Class.extend({construct:function(c,d,e){this._resizeTimeout=null,this.id=b.instances.push(this),this.initialized=!1,this.$el=(b.is.jq(c)?c:a(c)).first(),this.$loader=a("
    ",{"class":"footable-loader"}).append(a("",{"class":"fooicon fooicon-loader"})),this.o=a.extend(!0,{},b.defaults,d),this.data=this.$el.data()||{},this.classes=[],this.components=b.components.load(b.is.hash(this.data.components)?this.data.components:this.o.components,this),this.breakpoints=this.use(FooTable.Breakpoints),this.columns=this.use(FooTable.Columns),this.rows=this.use(FooTable.Rows),this._construct(e)},_construct:function(a){var c=this;this._preinit().then(function(){return c._init()}).always(function(d){return c.$el.show(),b.is.error(d)?void console.error("FooTable: unhandled error thrown during initialization.",d):c.raise("ready.ft.table").then(function(){b.is.fn(a)&&a.call(c,c)})})},_preinit:function(){var a=this;return this.raise("preinit.ft.table",[a.data]).then(function(){var c=(a.$el.attr("class")||"").match(/\S+/g)||[];a.o.ajax=b.checkFnValue(a,a.data.ajax,a.o.ajax),a.o.stopPropagation=b.is["boolean"](a.data.stopPropagation)?a.data.stopPropagation:a.o.stopPropagation;for(var d=0,e=c.length;e>d;d++)b.str.startsWith(c[d],"footable")||a.classes.push(c[d]);return a.$el.hide().after(a.$loader),a.execute(!1,!1,"preinit",a.data)})},_init:function(){var c=this;return c.raise("init.ft.table").then(function(){var d=c.$el.children("thead"),e=c.$el.children("tbody"),f=c.$el.children("tfoot");return c.$el.addClass("footable footable-"+c.id),b.is.hash(c.o.on)&&c.$el.on(c.o.on),0==f.length&&c.$el.append(f=a("
    ")),0==e.length&&c.$el.append(""),0==d.length&&c.$el.prepend(d=a("")),c.execute(!1,!0,"init").then(function(){return c.$el.data("__FooTable__",c),0==f.children("tr").length&&f.remove(),0==d.children("tr").length&&d.remove(),c.raise("postinit.ft.table").then(function(){return c.draw()}).always(function(){a(window).off("resize.ft"+c.id,c._onWindowResize).on("resize.ft"+c.id,{self:c},c._onWindowResize),c.initialized=!0})})})},destroy:function(){var c=this;return c.raise("destroy.ft.table").then(function(){return c.execute(!0,!0,"destroy").then(function(){c.$el.removeData("__FooTable__").removeClass("footable-"+c.id),b.is.hash(c.o.on)&&c.$el.off(c.o.on),a(window).off("resize.ft"+c.id,c._onWindowResize),c.initialized=!1})}).fail(function(a){b.is.error(a)&&console.error("FooTable: unhandled error thrown while destroying the plugin.",a)})},raise:function(c,d){var e=this,f=b.__debug__&&(b.is.emptyArray(b.__debug_options__.events)||b.arr.any(b.__debug_options__.events,function(a){return b.str.contains(c,a)}));return d=d||[],d.unshift(this),a.Deferred(function(b){var g=a.Event(c);1==e.o.stopPropagation&&e.$el.one(c,function(a){a.stopPropagation()}),f&&console.log("FooTable:"+c+": ",d),e.$el.trigger(g,d),g.isDefaultPrevented()?(f&&console.log('FooTable: default prevented for the "'+c+'" event.'),b.reject(g)):b.resolve(g)})},use:function(a){for(var b=0,c=this.components.length;c>b;b++)if(this.components[b]instanceof a)return this.components[b];return null},draw:function(){var a=this,c=a.$el.clone().insertBefore(a.$el);return a.$el.detach(),a.execute(!1,!0,"predraw").then(function(){return a.raise("predraw.ft.table").then(function(){return a.execute(!1,!0,"draw").then(function(){return a.raise("draw.ft.table").then(function(){return a.execute(!1,!0,"postdraw").then(function(){return a.raise("postdraw.ft.table")})})})})}).fail(function(a){b.is.error(a)&&console.error("FooTable: unhandled error thrown during a draw operation.",a)}).always(function(){c.replaceWith(a.$el),a.$loader.remove()})},execute:function(a,c,d,e,f){var g=this,h=Array.prototype.slice.call(arguments);a=h.shift(),c=h.shift();var i=c?b.arr.get(g.components,function(a){return a.enabled}):g.components.slice(0);return h.unshift(a?i.reverse():i),g._execute.apply(g,h)},_execute:function(c,d,e,f){if(!c||!c.length)return a.when();var g,h=this,i=Array.prototype.slice.call(arguments);return c=i.shift(),d=i.shift(),g=c.shift(),b.is.fn(g[d])?a.Deferred(function(a){try{var c=g[d].apply(g,i);if(b.is.promise(c))return c.then(a.resolve,a.reject);a.resolve(c)}catch(e){a.reject(e)}}).then(function(){return h._execute.apply(h,[c,d].concat(i))}):h._execute.apply(h,[c,d].concat(i))},_onWindowResize:function(a){var b=a.data.self;null!=b._resizeTimeout&&clearTimeout(b._resizeTimeout),b._resizeTimeout=setTimeout(function(){b._resizeTimeout=null,b.raise("resize.ft.table").then(function(){b.breakpoints.check()})},300)}})}(jQuery,FooTable),function(a,b){b.is.undef(window.moment)||(b.DateColumn=b.Column.extend({construct:function(a,c){this._super(a,c,"date"),this.formatString=b.is.string(c.formatString)?c.formatString:"MM-DD-YYYY"},parser:function(c){if(b.is.element(c)||b.is.jq(c)){var d=a(c).data("value");c=b.is.defined(d)?d:a(c).text(),b.is.string(c)&&(c=isNaN(c)?c:+c)}if(b.is.date(c))return moment(c);if(b.is.object(c)&&b.is["boolean"](c._isAMomentObject))return c;if(b.is.string(c)){if(isNaN(c))return moment(c,this.formatString);c=+c}return b.is.number(c)?moment(c):null},formatter:function(a){return b.is.object(a)&&b.is["boolean"](a._isAMomentObject)&&a.isValid()?a.format(this.formatString):""},filterValue:function(c){if((b.is.element(c)||b.is.jq(c))&&(c=a(c).data("filterValue")||a(c).text()),b.is.hash(c)&&b.is.hash(c.options)&&(b.is.string(c.options.filterValue)&&(c=c.options.filterValue),b.is.defined(c.value)&&(c=c.value)),b.is.object(c)&&b.is["boolean"](c._isAMomentObject))return c.format(this.formatString);if(b.is.string(c)){if(isNaN(c))return c;c=+c}return b.is.number(c)||b.is.date(c)?moment(c).format(this.formatString):b.is.defined(c)&&null!=c?c+"":""}}),b.columns.register("date",b.DateColumn))}(jQuery,FooTable),function(a,b){b.HTMLColumn=b.Column.extend({construct:function(a,b){this._super(a,b,"html")},parser:function(c){if(b.is.string(c)&&(c=a(a.trim(c))),b.is.element(c)&&(c=a(c)),b.is.jq(c)){var d=c.prop("tagName").toLowerCase();if("td"==d||"th"==d){var e=c.data("value");return b.is.defined(e)?e:c.contents()}return c}return null}}),b.columns.register("html",b.HTMLColumn)}(jQuery,FooTable),function(a,b){b.NumberColumn=b.Column.extend({construct:function(a,c){this._super(a,c,"number"),this.decimalSeparator=b.is.string(c.decimalSeparator)?c.decimalSeparator:".",this.thousandSeparator=b.is.string(c.thousandSeparator)?c.thousandSeparator:",",this.decimalSeparatorRegex=new RegExp(b.str.escapeRegExp(this.decimalSeparator),"g"),this.thousandSeparatorRegex=new RegExp(b.str.escapeRegExp(this.thousandSeparator),"g"),this.cleanRegex=new RegExp("[^0-9"+b.str.escapeRegExp(this.decimalSeparator)+"]","g")},parser:function(c){if(b.is.element(c)||b.is.jq(c)){var d=a(c).data("value");c=b.is.defined(d)?d:a(c).text().replace(this.cleanRegex,"")}return b.is.string(c)&&(c=c.replace(this.thousandSeparatorRegex,"").replace(this.decimalSeparatorRegex,"."),c=parseFloat(c)),b.is.number(c)?c:null},formatter:function(a){if(null==a)return"";var b=(a+"").split(".");return 2==b.length&&b[0].length>3&&(b[0]=b[0].replace(/\B(?=(?:\d{3})+(?!\d))/g,this.thousandSeparator)),b.join(this.decimalSeparator)}}),b.columns.register("number",b.NumberColumn)}(jQuery,FooTable),function(a,b){b.Breakpoint=b.Class.extend({construct:function(a,b){this.name=a,this.width=b}})}(jQuery,FooTable),function(a,b){b.Breakpoints=b.Component.extend({construct:function(a){this._super(a,!0),this.o=a.o,this.current=null,this.array=[],this.cascade=this.o.cascade,this.useParentWidth=this.o.useParentWidth,this.hidden=null,this._classNames="",this.getWidth=b.checkFnValue(this,this.o.getWidth,this.getWidth)},preinit:function(a){var c=this;return this.ft.raise("preinit.ft.breakpoints",[a]).then(function(){c.cascade=b.is["boolean"](a.cascade)?a.cascade:c.cascade,c.o.breakpoints=b.is.hash(a.breakpoints)?a.breakpoints:c.o.breakpoints,c.getWidth=b.checkFnValue(c,a.getWidth,c.getWidth),null==c.o.breakpoints&&(c.o.breakpoints={xs:480,sm:768,md:992,lg:1200});for(var d in c.o.breakpoints)c.o.breakpoints.hasOwnProperty(d)&&(c.array.push(new b.Breakpoint(d,c.o.breakpoints[d])),c._classNames+="breakpoint-"+d+" ");c.array.sort(function(a,b){return b.width-a.width})})},init:function(){var a=this;return this.ft.raise("init.ft.breakpoints").then(function(){a.current=a.get()})},draw:function(){this.ft.$el.removeClass(this._classNames).addClass("breakpoint-"+this.current.name)},calculate:function(){for(var a,c=this,d=null,e=[],f=null,g=c.getWidth(),h=0,i=c.array.length;i>h;h++)a=c.array[h],(!d&&h==i-1||g>=a.width&&(f instanceof b.Breakpoint?gd;d++)if(this.cascade?b.str.containsWord(this.hidden,c[d]):c[d]==this.current.name)return!1;return!0},check:function(){var a=this,c=a.get();c instanceof b.Breakpoint&&c!=a.current&&a.ft.raise("before.ft.breakpoints",[a.current,c]).then(function(){var b=a.current;return a.current=c,a.ft.draw().then(function(){a.ft.raise("after.ft.breakpoints",[a.current,b])})})},get:function(a){return b.is.undef(a)?this.calculate():a instanceof b.Breakpoint?a:b.is.string(a)?b.arr.first(this.array,function(b){return b.name==a}):b.is.number(a)&&a>=0&&af&&(f=a.index)}),f++;for(var g,h,i=0;f>i;i++)g={},b.arr.each(c,function(a){return a.index==i?(g=a,!1):void 0}),h={},b.arr.each(d,function(a){return a.index==i?(h=a,!1):void 0}),e.push(a.extend(!0,{},g,h))}return e}var f,g,h=[],i=[],j=d.ft.$el.find("tr.footable-header, thead > tr:last:has([data-breakpoints]), tbody > tr:first:has([data-breakpoints]), thead > tr:last, tbody > tr:first").first();if(j.length>0){var k=j.parent().is("tbody")&&j.children().length==j.children("td").length;k||(d.$header=j.addClass("footable-header")),j.children("td,th").each(function(b,c){f=a(c),g=f.data(),g.index=b,g.$el=f,g.virtual=k,i.push(g)}),k&&(d.showHeader=!1)}b.is.array(d.o.columns)&&!b.is.emptyArray(d.o.columns)?(b.arr.each(d.o.columns,function(a,b){a.index=b,h.push(a)}),d.parseFinalize(c,e(h,i))):b.is.promise(d.o.columns)?d.o.columns.then(function(a){b.arr.each(a,function(a,b){a.index=b,h.push(a)}),d.parseFinalize(c,e(h,i))},function(a){c.reject(Error("Columns ajax request error: "+a.status+" ("+a.statusText+")"))}):d.parseFinalize(c,e(h,i))})},parseFinalize:function(a,c){var d,e=this,f=[];b.arr.each(c,function(a){(d=b.columns.contains(a.type)?b.columns.make(a.type,e.ft,a):new b.Column(e.ft,a))&&f.push(d)}),b.is.emptyArray(f)?a.reject(Error("No columns supplied.")):(f.sort(function(a,b){return a.index-b.index}),a.resolve(f))},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.columns",[a]).then(function(){return c.parse(a).then(function(d){c.array=d,c.showHeader=b.is["boolean"](a.showHeader)?a.showHeader:c.showHeader})})},init:function(){var a=this;return this.ft.raise("init.ft.columns",[a.array]).then(function(){a.$create()})},destroy:function(){var a=this;this.ft.raise("destroy.ft.columns").then(function(){a._fromHTML||a.$header.remove()})},predraw:function(){var a=this,c=!0;a.visibleColspan=0,a.firstVisibleIndex=0,a.lastVisibleIndex=0,a.hasHidden=!1,b.arr.each(a.array,function(b){b.hidden=!a.ft.breakpoints.visible(b.breakpoints),!b.hidden&&b.visible&&(c&&(a.firstVisibleIndex=b.index,c=!1),a.lastVisibleIndex=b.index,a.visibleColspan++),b.hidden&&(a.hasHidden=!0)}),a.ft.$el.toggleClass("breakpoint",a.hasHidden)},draw:function(){b.arr.each(this.array,function(a){a.$el.css("display",a.hidden||!a.visible?"none":"table-cell")}),!this.showHeader&&b.is.jq(this.$header.parent())&&this.$header.detach()},$create:function(){var c=this;c.$header=b.is.jq(c.$header)?c.$header:a("",{"class":"footable-header" +}),c.$header.children("th,td").detach(),b.arr.each(c.array,function(a){c.$header.append(a.$el)}),c.showHeader&&!b.is.jq(c.$header.parent())&&c.ft.$el.children("thead").append(c.$header)},get:function(a){return a instanceof b.Column?a:b.is.string(a)?b.arr.first(this.array,function(b){return b.name==a}):b.is.number(a)?b.arr.first(this.array,function(b){return b.index==a}):b.is.fn(a)?b.arr.get(this.array,a):null},ensure:function(a){var c=this,d=[];return b.is.array(a)?(b.arr.each(a,function(a){d.push(c.get(a))}),d):d}}),b.components.register("columns",b.Columns,900)}(jQuery,FooTable),function(a){a.Defaults.prototype.columns=[],a.Defaults.prototype.showHeader=!0}(FooTable),function(a,b){b.Rows=b.Component.extend({construct:function(a){this._super(a,!0),this.o=a.o,this.array=[],this.all=[],this.showToggle=a.o.showToggle,this.toggleSelector=a.o.toggleSelector,this.toggleColumn=a.o.toggleColumn,this.emptyString=a.o.empty,this.expandFirst=a.o.expandFirst,this.expandAll=a.o.expandAll,this.$empty=null,this._fromHTML=b.is.emptyArray(a.o.rows)&&!b.is.promise(a.o.rows)},parse:function(){var c=this;return a.Deferred(function(a){var d=c.ft.$el.children("tbody").children("tr");b.is.array(c.o.rows)&&c.o.rows.length>0?c.parseFinalize(a,c.o.rows):b.is.promise(c.o.rows)?c.o.rows.then(function(b){c.parseFinalize(a,b)},function(b){a.reject(Error("Rows ajax request error: "+b.status+" ("+b.statusText+")"))}):b.is.jq(d)?(c.parseFinalize(a,d),d.detach()):c.parseFinalize(a,[])})},parseFinalize:function(c,d){var e=this,f=a.map(d,function(a){return new b.Row(e.ft,e.ft.columns.array,a)});c.resolve(f)},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.rows",[a]).then(function(){return c.parse().then(function(d){c.all=d,c.array=c.all.slice(0),c.showToggle=b.is["boolean"](a.showToggle)?a.showToggle:c.showToggle,c.toggleSelector=b.is.string(a.toggleSelector)?a.toggleSelector:c.toggleSelector,c.toggleColumn=b.is.string(a.toggleColumn)?a.toggleColumn:c.toggleColumn,"first"!=c.toggleColumn&&"last"!=c.toggleColumn&&(c.toggleColumn="first"),c.emptyString=b.is.string(a.empty)?a.empty:c.emptyString,c.expandFirst=b.is["boolean"](a.expandFirst)?a.expandFirst:c.expandFirst,c.expandAll=b.is["boolean"](a.expandAll)?a.expandAll:c.expandAll})})},init:function(){var a=this;return a.ft.raise("init.ft.rows",[a.all]).then(function(){a.$create()})},destroy:function(){var a=this;this.ft.raise("destroy.ft.rows").then(function(){b.arr.each(a.array,function(b){b.predraw(!a._fromHTML)})})},predraw:function(){b.arr.each(this.array,function(a){a.predraw()}),this.array=this.all.slice(0)},$create:function(){this.$empty=a("",{"class":"footable-empty"}).append(a("'); + self.ft.$el.append($tfoot); + } + self.$row = $('', { 'class': 'footable-editing' }).append(self.$cell).appendTo($tfoot); + }, + /** + * Creates the show button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonShow: function(){ + return ''; + }, + /** + * Creates the hide button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonHide: function(){ + return ''; + }, + /** + * Creates the add button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonAdd: function(){ + return ' '; + }, + /** + * Creates the edit button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonEdit: function(){ + return ' '; + }, + /** + * Creates the delete button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonDelete: function(){ + return ''; + }, + /** + * Creates the view button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonView: function(){ + return ' '; + }, + /** + * Creates the button group for the row buttons. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $rowButtons: function(){ + if (F.is.jq(this._$buttons)) return this._$buttons.clone(); + this._$buttons = $('
    '); + if (this.allowView) this._$buttons.append(this.$buttonView()); + if (this.allowEdit) this._$buttons.append(this.$buttonEdit()); + if (this.allowDelete) this._$buttons.append(this.$buttonDelete()); + return this._$buttons; + }, + /** + * Performs the drawing of the component. + */ + draw: function(){ + this.$cell.attr('colspan', this.ft.columns.visibleColspan); + }, + /** + * Handles the edit button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"edit.ft.editing" + */ + _onEditClick: function(e){ + e.preventDefault(); + var self = e.data.self, row = $(this).closest('tr').data('__FooTableRow__'); + if (row instanceof F.Row){ + /** + * The edit.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"edit.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row to be edited. + */ + self.ft.raise('edit.ft.editing', [row]).then(function(){ + self.callbacks.editRow.call(self.ft, row); + }); + } + }, + /** + * Handles the delete button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"delete.ft.editing" + */ + _onDeleteClick: function(e){ + e.preventDefault(); + var self = e.data.self, row = $(this).closest('tr').data('__FooTableRow__'); + if (row instanceof F.Row){ + /** + * The delete.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"delete.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row to be deleted. + */ + self.ft.raise('delete.ft.editing', [row]).then(function(){ + self.callbacks.deleteRow.call(self.ft, row); + }); + } + }, + /** + * Handles the view button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"view.ft.editing" + */ + _onViewClick: function(e){ + e.preventDefault(); + var self = e.data.self, row = $(this).closest('tr').data('__FooTableRow__'); + if (row instanceof F.Row){ + /** + * The view.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"view.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row to be viewed. + */ + self.ft.raise('view.ft.editing', [row]).then(function(){ + self.callbacks.viewRow.call(self.ft, row); + }); + } + }, + /** + * Handles the add button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"add.ft.editing" + */ + _onAddClick: function(e){ + e.preventDefault(); + var self = e.data.self; + /** + * The add.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"add.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + self.ft.raise('add.ft.editing').then(function(){ + self.callbacks.addRow.call(self.ft); + }); + }, + /** + * Handles the show button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"show.ft.editing" + */ + _onShowClick: function(e){ + e.preventDefault(); + var self = e.data.self; + /** + * The show.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"show.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + self.ft.raise('show.ft.editing').then(function(){ + self.ft.$el.addClass('footable-editing-show'); + self.column.visible = true; + self.ft.draw(); + }); + }, + /** + * Handles the hide button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"show.ft.editing" + */ + _onHideClick: function(e){ + e.preventDefault(); + var self = e.data.self; + /** + * The hide.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"hide.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + self.ft.raise('hide.ft.editing').then(function(){ + self.ft.$el.removeClass('footable-editing-show'); + self.column.visible = false; + self.ft.draw(); + }); + } + }); + + F.components.register('editing', F.Editing, 850); + +})(jQuery, FooTable); + +(function($, F){ + + F.EditingColumn = F.Column.extend(/** @lends FooTable.EditingColumn */{ + /** + * The Editing column class is used to create the column containing the editing buttons. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {FooTable.Editing} editing - The parent {@link FooTable.Editing} component this column is used with. + * @param {object} definition - An object containing all the properties to set for the column. + * @returns {FooTable.EditingColumn} + */ + construct: function(instance, editing, definition){ + this._super(instance, definition, 'editing'); + this.editing = editing; + }, + /** + * After the column has been defined this ensures that the $el property is a jQuery object by either creating or updating the current value. + * @instance + * @protected + * @this FooTable.Column + */ + $create: function(){ + (this.$el = !this.virtual && F.is.jq(this.$el) ? this.$el : $('"),b.ft.$el.append(d)),b.$row=a("",{"class":"footable-editing"}).append(b.$cell).appendTo(d)},$buttonShow:function(){return'"},$buttonHide:function(){return'"},$buttonAdd:function(){return' "},$buttonEdit:function(){return' "},$buttonDelete:function(){return'"},$buttonView:function(){return' "},$rowButtons:function(){return b.is.jq(this._$buttons)?this._$buttons.clone():(this._$buttons=a('
    '),this.allowView&&this._$buttons.append(this.$buttonView()),this.allowEdit&&this._$buttons.append(this.$buttonEdit()),this.allowDelete&&this._$buttons.append(this.$buttonDelete()),this._$buttons)},draw:function(){this.$cell.attr("colspan",this.ft.columns.visibleColspan)},_onEditClick:function(c){c.preventDefault();var d=c.data.self,e=a(this).closest("tr").data("__FooTableRow__");e instanceof b.Row&&d.ft.raise("edit.ft.editing",[e]).then(function(){d.callbacks.editRow.call(d.ft,e)})},_onDeleteClick:function(c){c.preventDefault();var d=c.data.self,e=a(this).closest("tr").data("__FooTableRow__");e instanceof b.Row&&d.ft.raise("delete.ft.editing",[e]).then(function(){d.callbacks.deleteRow.call(d.ft,e)})},_onViewClick:function(c){c.preventDefault();var d=c.data.self,e=a(this).closest("tr").data("__FooTableRow__");e instanceof b.Row&&d.ft.raise("view.ft.editing",[e]).then(function(){d.callbacks.viewRow.call(d.ft,e)})},_onAddClick:function(a){a.preventDefault();var b=a.data.self;b.ft.raise("add.ft.editing").then(function(){b.callbacks.addRow.call(b.ft)})},_onShowClick:function(a){a.preventDefault();var b=a.data.self;b.ft.raise("show.ft.editing").then(function(){b.ft.$el.addClass("footable-editing-show"),b.column.visible=!0,b.ft.draw()})},_onHideClick:function(a){a.preventDefault();var b=a.data.self;b.ft.raise("hide.ft.editing").then(function(){b.ft.$el.removeClass("footable-editing-show"),b.column.visible=!1,b.ft.draw()})}}),b.components.register("editing",b.Editing,850)}(jQuery,FooTable),function(a,b){b.EditingColumn=b.Column.extend({construct:function(a,b,c){this._super(a,c,"editing"),this.editing=b},$create:function(){(this.$el=!this.virtual&&b.is.jq(this.$el)?this.$el:a("', {'class': 'footable-filtering'}).prependTo(self.ft.$el.children('thead')); + self.$cell = $(' + * query = "Dave AND Mary" - "Dave" is the left side of the query. + * query = "Dave AND Mary OR John" - "Dave and Mary" is the left side of the query. + */ + this.left = null; + /** + * The right side of the query if one exists. OR takes precedence over AND. + * @type {FooTable.Query} + * @example + * query = "Dave AND Mary" - "Mary" is the right side of the query. + * query = "Dave AND Mary OR John" - "John" is the right side of the query. + */ + this.right = null; + /** + * The parsed parts of the query. This contains the information used to actually perform a match against a string. + * @type {Array} + */ + this.parts = []; + /** + * The type of operand to apply to the results of the individual parts of the query. + * @type {string} + */ + this.operator = null; + this.val(query); + }, + /** + * Gets or sets the value for the query. During set the value is parsed setting all properties as required. + * @param {string} [value] - If supplied the value to set for this query. + * @returns {(string|undefined)} + */ + val: function(value){ + // get + if (F.is.emptyString(value)) return this._value; + + // set + if (F.is.emptyString(this._original)) this._original = value; + else if (this._original == value) return; + + this._value = value; + this._parse(); + }, + /** + * Tests the supplied string against the query. + * @param {string} str - The string to test. + * @returns {boolean} + */ + match: function(str){ + if (F.is.emptyString(this.operator) || this.operator === 'OR') + return this._left(str, false) || this._match(str, false) || this._right(str, false); + if (this.operator === 'AND') + return this._left(str, true) && this._match(str, true) && this._right(str, true); + }, + /** + * Matches this queries parts array against the supplied string. + * @param {string} str - The string to test. + * @param {boolean} def - The default value to return based on the operand. + * @returns {boolean} + * @private + */ + _match: function(str, def){ + var self = this, result = false, empty = F.is.emptyString(str); + if (F.is.emptyArray(self.parts) && self.left instanceof F.Query) return def; + if (F.is.emptyArray(self.parts)) return result; + if (self.space === 'OR'){ + // with OR we give the str every part to test and if any match it is a success, we do exit early if a negated match occurs + F.arr.each(self.parts, function(p){ + if (p.empty && empty){ + result = true; + if (p.negate){ + result = false; + return result; + } + } else { + var match = (p.exact ? F.str.containsExact : F.str.contains)(str, p.query, self.ignoreCase); + if (match && !p.negate) result = true; + if (match && p.negate) { + result = false; + return result; + } + } + }); + } else { + // otherwise with AND we check until the first failure and then exit + result = true; + F.arr.each(self.parts, function(p){ + if (p.empty){ + if ((!empty && !p.negate) || (empty && p.negate)) result = false; + return result; + } else { + var match = (p.exact ? F.str.containsExact : F.str.contains)(str, p.query, self.ignoreCase); + if ((!match && !p.negate) || (match && p.negate)) result = false; + return result; + } + }); + } + return result; + }, + /** + * Matches the left side of the query if one exists with the supplied string. + * @param {string} str - The string to test. + * @param {boolean} def - The default value to return based on the operand. + * @returns {boolean} + * @private + */ + _left: function(str, def){ + return (this.left instanceof F.Query) ? this.left.match(str) : def; + }, + /** + * Matches the right side of the query if one exists with the supplied string. + * @param {string} str - The string to test. + * @param {boolean} def - The default value to return based on the operand. + * @returns {boolean} + * @private + */ + _right: function(str, def){ + return (this.right instanceof F.Query) ? this.right.match(str) : def; + }, + /** + * Parses the private {@link FooTable.Query#_value} property and populates the object. + * @private + */ + _parse: function(){ + if (F.is.emptyString(this._value)) return; + // OR takes precedence so test for it first + if (/\sOR\s/.test(this._value)){ + // we have an OR so split the value on the first occurrence of OR to get the left and right sides of the statement + this.operator = 'OR'; + var or = this._value.split(/(?:\sOR\s)(.*)?/); + this.left = new F.Query(or[0], this.space, this.connectors, this.ignoreCase); + this.right = new F.Query(or[1], this.space, this.connectors, this.ignoreCase); + } else if (/\sAND\s/.test(this._value)) { + // there are no more OR's so start with AND + this.operator = 'AND'; + var and = this._value.split(/(?:\sAND\s)(.*)?/); + this.left = new F.Query(and[0], this.space, this.connectors, this.ignoreCase); + this.right = new F.Query(and[1], this.space, this.connectors, this.ignoreCase); + } else { + // we have no more statements to parse so set the parts array by parsing each part of the remaining query + var self = this; + this.parts = F.arr.map(this._value.match(/(?:[^\s"]+|"[^"]*")+/g), function(str){ + return self._part(str); + }); + } + }, + /** + * Parses a single part of a query into an object to use during matching. + * @param {string} str - The string representation of the part. + * @returns {{query: string, negate: boolean, phrase: boolean, exact: boolean}} + * @private + */ + _part: function(str){ + var p = { + query: str, + negate: false, + phrase: false, + exact: false, + empty: false + }; + // support for NEGATE operand - (minus sign). Remove this first so we can get onto phrase checking + if (F.str.startsWith(p.query, '-')){ + p.query = F.str.from(p.query, '-'); + p.negate = true; + } + // support for PHRASES (exact matches) + if (/^"(.*?)"$/.test(p.query)){ // if surrounded in quotes strip them and nothing else + p.query = p.query.replace(/^"(.*?)"$/, '$1'); + p.phrase = true; + p.exact = true; + } else if (this.connectors && /(?:\w)+?([-_\+\.])(?:\w)+?/.test(p.query)) { // otherwise replace supported phrase connectors (-_+.) with spaces + p.query = p.query.replace(/(?:\w)+?([-_\+\.])(?:\w)+?/g, function(match, p1){ + return match.replace(p1, ' '); + }); + p.phrase = true; + } + p.empty = p.phrase && F.is.emptyString(p.query); + return p; + } + }); + +})(FooTable); +(function(F){ + + /** + * The value used by the filtering component during filter operations. Must be a string and can be set using the data-filter-value attribute on the cell itself. + * If this is not supplied it is set to the result of the toString method called on the value for the cell. Added by the {@link FooTable.Filtering} component. + * @type {string} + * @default null + */ + F.Cell.prototype.filterValue = null; + + // this is used to define the filtering specific properties on cell creation + F.Cell.prototype.__filtering_define__ = function(valueOrElement){ + this.filterValue = this.column.filterValue.call(this.column, valueOrElement); + }; + + // this is used to update the filterValue property whenever the cell value is changed + F.Cell.prototype.__filtering_val__ = function(value){ + if (F.is.defined(value)){ + // set only + this.filterValue = this.column.filterValue.call(this.column, value); + } + }; + + // overrides the public define method and replaces it with our own + F.Cell.extend('define', function(valueOrElement){ + this._super(valueOrElement); + this.__filtering_define__(valueOrElement); + }); + // overrides the public val method and replaces it with our own + F.Cell.extend('val', function(value){ + var val = this._super(value); + this.__filtering_val__(value); + return val; + }); +})(FooTable); +(function($, F){ + /** + * Whether or not the column can be used during filtering. Added by the {@link FooTable.Filtering} component. + * @type {boolean} + * @default true + */ + F.Column.prototype.filterable = true; + + /** + * This is supplied either the cell value or jQuery object to parse. A string value must be returned from this method and will be used during filtering operations. + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {string} + * @this FooTable.Column + */ + F.Column.prototype.filterValue = function(valueOrElement){ + // if we have an element or a jQuery object use jQuery to get the value + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('filterValue'); + return F.is.defined(data) ? ''+data : $(valueOrElement).text(); + } + // if options are supplied with the value + if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){ + if (F.is.string(valueOrElement.options.filterValue)) return valueOrElement.options.filterValue; + if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value; + } + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement+''; // use the native toString of the value + return ''; // otherwise we have no value so return an empty string + }; + + // this is used to define the filtering specific properties on column creation + F.Column.prototype.__filtering_define__ = function(definition){ + this.filterable = F.is.boolean(definition.filterable) ? definition.filterable : this.filterable; + this.filterValue = F.checkFnValue(this, definition.filterValue, this.filterValue); + }; + + // overrides the public define method and replaces it with our own + F.Column.extend('define', function(definition){ + this._super(definition); // call the base so we don't have to redefine any previously set properties + this.__filtering_define__(definition); // then call our own + }); +})(jQuery, FooTable); +(function(F){ + /** + * An object containing the filtering options for the plugin. Added by the {@link FooTable.Filtering} component. + * @type {object} + * @prop {boolean} enabled=false - Whether or not to allow filtering on the table. + * @prop {({name: string, query: (string|FooTable.Query), columns: (Array.|Array.|Array.)}|Array.)} filters - The filters to apply to the current {@link FooTable.Rows#array}. + * @prop {number} delay=1200 - The delay in milliseconds before the query is auto applied after a change (any value equal to or less than zero will disable this). + * @prop {number} min=1 - The minimum number of characters allowed in the search input before it is auto applied. + * @prop {string} space="AND" - Specifies how whitespace in a filter query is handled. + * @prop {string} placeholder="Search" - The string used as the placeholder for the search input. + * @prop {string} dropdownTitle=null - The title to display at the top of the search input column select. + * @prop {string} position="right" - The string used to specify the alignment of the search input. + * @prop {string} connectors=true - Whether or not to replace phrase connectors (+.-_) with space before executing the query. + * @prop {boolean} ignoreCase=true - Whether or not ignore case when matching. + * @prop {boolean} exactMatch=false - Whether or not search queries are treated as phrases when matching. + */ + F.Defaults.prototype.filtering = { + enabled: false, + filters: [], + delay: 1200, + min: 1, + space: 'AND', + placeholder: 'Search', + dropdownTitle: null, + position: 'right', + connectors: true, + ignoreCase: true, + exactMatch: false + }; +})(FooTable); +(function(F){ + /** + * Checks if the row is filtered using the supplied filters. + * @this FooTable.Row + * @param {Array.} filters - The filters to apply. + * @returns {boolean} + */ + F.Row.prototype.filtered = function(filters){ + var result = true, self = this; + F.arr.each(filters, function(f){ + if ((result = f.matchRow(self)) == false) return false; + }); + return result; + }; +})(FooTable); \ No newline at end of file diff --git a/media/footable-v3/js/footable.filtering.min.js b/media/footable-v3/js/footable.filtering.min.js new file mode 100644 index 0000000..7f4f63b --- /dev/null +++ b/media/footable-v3/js/footable.filtering.min.js @@ -0,0 +1,8 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +!function(a){a.Filter=a.Class.extend({construct:function(b,c,d,e,f,g,h){this.name=b,this.space=!a.is.string(e)||"OR"!=e&&"AND"!=e?"AND":e,this.connectors=a.is["boolean"](f)?f:!0,this.ignoreCase=a.is["boolean"](g)?g:!0,this.hidden=a.is["boolean"](h)?h:!1,this.query=c instanceof a.Query?c:new a.Query(c,this.space,this.connectors,this.ignoreCase),this.columns=d},match:function(b){return a.is.string(b)?(a.is.string(this.query)&&(this.query=new a.Query(this.query,this.space,this.connectors,this.ignoreCase)),this.query instanceof a.Query?this.query.match(b):!1):!1},matchRow:function(b){var c=this,d=a.arr.map(b.cells,function(b){return a.arr.contains(c.columns,b.column)?b.filterValue:null}).join(" ");return c.match(d)}})}(FooTable),function(a,b){b.Filtering=b.Component.extend({construct:function(a){this._super(a,a.o.filtering.enabled),this.filters=a.o.filtering.filters,this.delay=a.o.filtering.delay,this.min=a.o.filtering.min,this.space=a.o.filtering.space,this.connectors=a.o.filtering.connectors,this.ignoreCase=a.o.filtering.ignoreCase,this.exactMatch=a.o.filtering.exactMatch,this.placeholder=a.o.filtering.placeholder,this.dropdownTitle=a.o.filtering.dropdownTitle,this.position=a.o.filtering.position,this.$row=null,this.$cell=null,this.$dropdown=null,this.$input=null,this.$button=null,this._filterTimeout=null,this._exactRegExp=/^"(.*?)"$/},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.filtering").then(function(){c.ft.$el.hasClass("footable-filtering")&&(c.enabled=!0),c.enabled=b.is["boolean"](a.filtering)?a.filtering:c.enabled,c.enabled&&(c.space=b.is.string(a.filterSpace)?a.filterSpace:c.space,c.min=b.is.number(a.filterMin)?a.filterMin:c.min,c.connectors=b.is["boolean"](a.filterConnectors)?a.filterConnectors:c.connectors,c.ignoreCase=b.is["boolean"](a.filterIgnoreCase)?a.filterIgnoreCase:c.ignoreCase,c.exactMatch=b.is["boolean"](a.filterExactMatch)?a.filterExactMatch:c.exactMatch,c.delay=b.is.number(a.filterDelay)?a.filterDelay:c.delay,c.placeholder=b.is.string(a.filterPlaceholder)?a.filterPlaceholder:c.placeholder,c.dropdownTitle=b.is.string(a.filterDropdownTitle)?a.filterDropdownTitle:c.dropdownTitle,c.filters=b.is.array(a.filterFilters)?c.ensure(a.filterFilters):c.ensure(c.filters),c.ft.$el.hasClass("footable-filtering-left")&&(c.position="left"),c.ft.$el.hasClass("footable-filtering-center")&&(c.position="center"),c.ft.$el.hasClass("footable-filtering-right")&&(c.position="right"),c.position=b.is.string(a.filterPosition)?a.filterPosition:c.position)},function(){c.enabled=!1})},init:function(){var a=this;return a.ft.raise("init.ft.filtering").then(function(){a.$create()},function(){a.enabled=!1})},destroy:function(){var a=this;return a.ft.raise("destroy.ft.filtering").then(function(){a.ft.$el.removeClass("footable-filtering").find("thead > tr.footable-filtering").remove()})},$create:function(){var c,d=this,e=a("
    ",{"class":"form-group footable-filtering-search"}).append(a("
    ",{"class":"footable-filtering"}).prependTo(d.ft.$el.children("thead")),d.$cell=a("").addClass(this.row.classes.join(" ")).data("__FooTableCell__",this).append(a("")).data("__FooTableRow__",this),this._setClasses(this.$el),this._setStyle(this.$el),"last"==this.ft.rows.toggleColumn&&this.$toggle.addClass("last-column"),this.$details=a("",{"class":"footable-detail-row"}).append(a("
    ").text(this.emptyString))},draw:function(){var a=this,c=a.ft.$el.children("tbody"),d=!0;a.array.length>0?(a.$empty.detach(),b.arr.each(a.array,function(b){(a.expandFirst&&d||a.expandAll)&&(b.expanded=!0,d=!1),b.draw(c)})):(a.$empty.children("td").attr("colspan",a.ft.columns.visibleColspan),c.append(a.$empty))},load:function(c,d){var e=this,f=a.map(c,function(a){return new b.Row(e.ft,e.ft.columns.array,a)});b.arr.each(this.array,function(a){a.predraw()}),this.all=(b.is["boolean"](d)?d:!1)?this.all.concat(f):f,this.array=this.all.slice(0),this.ft.draw()},expand:function(){b.arr.each(this.array,function(a){a.expand()})},collapse:function(){b.arr.each(this.array,function(a){a.collapse()})}}),b.components.register("rows",b.Rows,800)}(jQuery,FooTable),function(a){a.Defaults.prototype.rows=[],a.Defaults.prototype.empty="No results",a.Defaults.prototype.showToggle=!0,a.Defaults.prototype.toggleSelector="tr,td,.footable-toggle",a.Defaults.prototype.toggleColumn="first",a.Defaults.prototype.expandFirst=!1,a.Defaults.prototype.expandAll=!1}(FooTable),function(a){a.Table.prototype.loadRows=function(a,b){this.rows.load(a,b)}}(FooTable); \ No newline at end of file diff --git a/media/footable-v3/js/footable.editing.js b/media/footable-v3/js/footable.editing.js new file mode 100644 index 0000000..ec2e186 --- /dev/null +++ b/media/footable-v3/js/footable.editing.js @@ -0,0 +1,802 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +(function($, F){ + + F.Editing = F.Component.extend(/** @lends FooTable.Editing */{ + /** + * The editing component adds a column with edit and delete buttons to each row as well as a single add row button in the footer. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component. + * @returns {FooTable.Editing} + */ + construct: function(table){ + // call the base constructor + this._super(table, table.o.editing.enabled); + + /** + * Whether or not to automatically page to a new row when it is added to the table. + * @type {boolean} + */ + this.pageToNew = table.o.editing.pageToNew; + + /** + * Whether or not the editing column and add row button are always visible. + * @type {boolean} + */ + this.alwaysShow = table.o.editing.alwaysShow; + + /** + * The options for the editing column. @see {@link FooTable.EditingColumn} for more info. + * @type {object} + * @prop {string} classes="footable-editing" - A space separated string of class names to apply to all cells in the column. + * @prop {string} name="editing" - The name of the column. + * @prop {string} title="" - The title displayed in the header row of the table for the column. + * @prop {boolean} filterable=false - Whether or not the column should be filterable when using the filtering component. + * @prop {boolean} sortable=false - Whether or not the column should be sortable when using the sorting component. + */ + this.column = $.extend(true, {}, table.o.editing.column, {visible: this.alwaysShow}); + + /** + * The position of the editing column in the table as well as the alignment of the buttons. + * @type {string} + */ + this.position = table.o.editing.position; + + + /** + * The text that appears in the show button. This can contain HTML. + * @type {string} + */ + this.showText = table.o.editing.showText; + + /** + * The text that appears in the hide button. This can contain HTML. + * @type {string} + */ + this.hideText = table.o.editing.hideText; + + /** + * The text that appears in the add button. This can contain HTML. + * @type {string} + */ + this.addText = table.o.editing.addText; + + /** + * The text that appears in the edit button. This can contain HTML. + * @type {string} + */ + this.editText = table.o.editing.editText; + + /** + * The text that appears in the delete button. This can contain HTML. + * @type {string} + */ + this.deleteText = table.o.editing.deleteText; + + /** + * The text that appears in the view button. This can contain HTML. + * @type {string} + */ + this.viewText = table.o.editing.viewText; + + /** + * Whether or not to show the Add Row button. + * @type {boolean} + */ + this.allowAdd = table.o.editing.allowAdd; + + /** + * Whether or not to show the Edit Row button. + * @type {boolean} + */ + this.allowEdit = table.o.editing.allowEdit; + + /** + * Whether or not to show the Delete Row button. + * @type {boolean} + */ + this.allowDelete = table.o.editing.allowDelete; + + /** + * Whether or not to show the View Row button. + * @type {boolean} + */ + this.allowView = table.o.editing.allowView; + + /** + * Caches the row button elements to help with performance. + * @type {(null|jQuery)} + * @private + */ + this._$buttons = null; + + /** + * This object is used to contain the callbacks for the add, edit and delete row buttons. + * @type {object} + * @prop {function} addRow + * @prop {function} editRow + * @prop {function} deleteRow + * @prop {function} viewRow + */ + this.callbacks = { + addRow: F.checkFnValue(this, table.o.editing.addRow), + editRow: F.checkFnValue(this, table.o.editing.editRow), + deleteRow: F.checkFnValue(this, table.o.editing.deleteRow), + viewRow: F.checkFnValue(this, table.o.editing.viewRow) + }; + }, + /* PROTECTED */ + /** + * Checks the supplied data and options for the editing component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.Editing#"preinit.ft.editing" + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.editing event is raised before the UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Editing#"preinit.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + this.ft.raise('preinit.ft.editing', [data]).then(function(){ + if (self.ft.$el.hasClass('footable-editing')) + self.enabled = true; + + self.enabled = F.is.boolean(data.editing) + ? data.editing + : self.enabled; + + if (!self.enabled) return; + + self.pageToNew = F.is.boolean(data.editingPageToNew) ? data.editingPageToNew : self.pageToNew; + + self.alwaysShow = F.is.boolean(data.editingAlwaysShow) ? data.editingAlwaysShow : self.alwaysShow; + + self.position = F.is.string(data.editingPosition) ? data.editingPosition : self.position; + + self.showText = F.is.string(data.editingShowText) ? data.editingShowText : self.showText; + + self.hideText = F.is.string(data.editingHideText) ? data.editingHideText : self.hideText; + + self.addText = F.is.string(data.editingAddText) ? data.editingAddText : self.addText; + + self.editText = F.is.string(data.editingEditText) ? data.editingEditText : self.editText; + + self.deleteText = F.is.string(data.editingDeleteText) ? data.editingDeleteText : self.deleteText; + + self.viewText = F.is.string(data.editingViewText) ? data.editingViewText : self.viewText; + + self.allowAdd = F.is.boolean(data.editingAllowAdd) ? data.editingAllowAdd : self.allowAdd; + + self.allowEdit = F.is.boolean(data.editingAllowEdit) ? data.editingAllowEdit : self.allowEdit; + + self.allowDelete = F.is.boolean(data.editingAllowDelete) ? data.editingAllowDelete : self.allowDelete; + + self.allowView = F.is.boolean(data.editingAllowView) ? data.editingAllowView : self.allowView; + + self.column = new F.EditingColumn(self.ft, self, $.extend(true, {}, self.column, data.editingColumn, {visible: self.alwaysShow})); + + if (self.ft.$el.hasClass('footable-editing-left')) + self.position = 'left'; + + if (self.ft.$el.hasClass('footable-editing-right')) + self.position = 'right'; + + if (self.position === 'right'){ + self.column.index = self.ft.columns.array.length; + } else { + self.column.index = 0; + for (var i = 0, len = self.ft.columns.array.length; i < len; i++){ + self.ft.columns.array[i].index += 1; + } + } + self.ft.columns.array.push(self.column); + self.ft.columns.array.sort(function(a, b){ return a.index - b.index; }); + + self.callbacks.addRow = F.checkFnValue(self, data.editingAddRow, self.callbacks.addRow); + self.callbacks.editRow = F.checkFnValue(self, data.editingEditRow, self.callbacks.editRow); + self.callbacks.deleteRow = F.checkFnValue(self, data.editingDeleteRow, self.callbacks.deleteRow); + self.callbacks.viewRow = F.checkFnValue(self, data.editingViewRow, self.callbacks.viewRow); + }, function(){ + self.enabled = false; + }); + }, + /** + * Initializes the editing component for the plugin using the supplied table and options. + * @instance + * @protected + * @fires FooTable.Editing#"init.ft.editing" + */ + init: function(){ + /** + * The init.ft.editing event is raised before its UI is generated. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Editing#"init.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('init.ft.editing').then(function(){ + self.$create(); + }, function(){ + self.enabled = false; + }); + }, + /** + * Destroys the editing component removing any UI generated from the table. + * @instance + * @protected + * @fires FooTable.Editing#"destroy.ft.editing" + */ + destroy: function () { + /** + * The destroy.ft.editing event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Editing#"destroy.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('destroy.ft.editing').then(function(){ + self.ft.$el.removeClass('footable-editing footable-editing-always-show footable-editing-no-add footable-editing-no-edit footable-editing-no-delete footable-editing-no-view') + .off('click.ft.editing').find('tfoot > tr.footable-editing').remove(); + }); + }, + /** + * Creates the editing UI from the current options setting the various jQuery properties of this component. + * @instance + * @protected + */ + $create: function(){ + var self = this, position = self.position === 'right' ? 'footable-editing-right' : 'footable-editing-left'; + self.ft.$el.addClass('footable-editing').addClass(position) + .on('click.ft.editing', '.footable-show', {self: self}, self._onShowClick) + .on('click.ft.editing', '.footable-hide', {self: self}, self._onHideClick) + .on('click.ft.editing', '.footable-edit', {self: self}, self._onEditClick) + .on('click.ft.editing', '.footable-delete', {self: self}, self._onDeleteClick) + .on('click.ft.editing', '.footable-view', {self: self}, self._onViewClick) + .on('click.ft.editing', '.footable-add', {self: self}, self._onAddClick); + + self.$cell = $('').attr('colspan', self.ft.columns.visibleColspan).append(self.$buttonShow()); + if (self.allowAdd){ + self.$cell.append(self.$buttonAdd()); + } + self.$cell.append(self.$buttonHide()); + + if (self.alwaysShow){ + self.ft.$el.addClass('footable-editing-always-show'); + } + + if (!self.allowAdd) self.ft.$el.addClass('footable-editing-no-add'); + if (!self.allowEdit) self.ft.$el.addClass('footable-editing-no-edit'); + if (!self.allowDelete) self.ft.$el.addClass('footable-editing-no-delete'); + if (!self.allowView) self.ft.$el.addClass('footable-editing-no-view'); + + var $tfoot = self.ft.$el.children('tfoot'); + if ($tfoot.length == 0){ + $tfoot = $('
    ', {'class': 'footable-editing'})).html(this.title); + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and + * will be provided to the {@link FooTable.EditingColumn#format} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(jQuery)} + */ + parser: function(valueOrElement){ + if (F.is.string(valueOrElement)) valueOrElement = $($.trim(valueOrElement)); + if (F.is.element(valueOrElement)) valueOrElement = $(valueOrElement); + if (F.is.jq(valueOrElement)){ + var tagName = valueOrElement.prop('tagName').toLowerCase(); + if (tagName == 'td' || tagName == 'th') return valueOrElement.data('value') || valueOrElement.contents(); + return valueOrElement; + } + return null; + }, + /** + * Creates a cell to be used in the supplied row for this column. + * @param {FooTable.Row} row - The row to create the cell for. + * @returns {FooTable.Cell} + */ + createCell: function(row){ + var $buttons = this.editing.$rowButtons(), $cell = $('').append($buttons); + if (F.is.jq(row.$el)){ + if (this.index === 0){ + $cell.prependTo(row.$el); + } else { + $cell.insertAfter(row.$el.children().eq(this.index-1)); + } + } + return new F.Cell(this.ft, row, this, $cell || $cell.html()); + } + }); + + F.columns.register('editing', F.EditingColumn); + +})(jQuery, FooTable); +(function($, F) { + + /** + * An object containing the editing options for the plugin. Added by the {@link FooTable.Editing} component. + * @type {object} + * @prop {boolean} enabled=false - Whether or not to allow editing on the table. + * @prop {boolean} pageToNew=true - Whether or not to automatically page to a new row when it is added to the table. + * @prop {string} position="right" - The position of the editing column in the table as well as the alignment of the buttons. + * @prop {boolean} alwaysShow=false - Whether or not the editing column and add row button are always visible. + * @prop {function} addRow - The callback function to execute when the add row button is clicked. + * @prop {function} editRow - The callback function to execute when the edit row button is clicked. + * @prop {function} deleteRow - The callback function to execute when the delete row button is clicked. + * @prop {function} viewRow - The callback function to execute when the view row button is clicked. + * @prop {string} showText - The text that appears in the show button. This can contain HTML. + * @prop {string} hideText - The text that appears in the hide button. This can contain HTML. + * @prop {string} addText - The text that appears in the add button. This can contain HTML. + * @prop {string} editText - The text that appears in the edit button. This can contain HTML. + * @prop {string} deleteText - The text that appears in the delete button. This can contain HTML. + * @prop {string} viewText - The text that appears in the view button. This can contain HTML. + * @prop {boolean} allowAdd - Whether or not to show the Add Row button. + * @prop {boolean} allowEdit - Whether or not to show the Edit Row button. + * @prop {boolean} allowDelete - Whether or not to show the Delete Row button. + * @prop {boolean} allowView - Whether or not to show the View Row button. + * @prop {object} column - The options for the editing column. @see {@link FooTable.EditingColumn} for more info. + * @prop {string} column.classes="footable-editing" - A space separated string of class names to apply to all cells in the column. + * @prop {string} column.name="editing" - The name of the column. + * @prop {string} column.title="" - The title displayed in the header row of the table for the column. + * @prop {boolean} column.filterable=false - Whether or not the column should be filterable when using the filtering component. + * @prop {boolean} column.sortable=false - Whether or not the column should be sortable when using the sorting component. + */ + F.Defaults.prototype.editing = { + enabled: false, + pageToNew: true, + position: 'right', + alwaysShow: false, + addRow: function(){}, + editRow: function(row){}, + deleteRow: function(row){}, + viewRow: function(row){}, + showText: ' Edit rows', + hideText: 'Cancel', + addText: 'New row', + editText: '', + deleteText: '', + viewText: '', + allowAdd: true, + allowEdit: true, + allowDelete: true, + allowView: false, + column: { + classes: 'footable-editing', + name: 'editing', + title: '', + filterable: false, + sortable: false + } + }; + +})(jQuery, FooTable); + +(function($, F){ + + if (F.is.defined(F.Paging)){ + /** + * Holds a shallow clone of the un-paged {@link FooTable.Rows#array} value before paging occurs and superfluous rows are removed. Added by the {@link FooTable.Editing} component. + * @instance + * @public + * @type {Array} + */ + F.Paging.prototype.unpaged = []; + + // override the default predraw method with one that sets the unpaged property. + F.Paging.extend('predraw', function(){ + this.unpaged = this.ft.rows.array.slice(0); // create a shallow clone for later use + this._super(); // call the original method + }); + } + +})(jQuery, FooTable); +(function($, F){ + + /** + * Adds the row to the table. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + * @returns {jQuery.Deferred} + */ + F.Row.prototype.add = function(redraw){ + redraw = F.is.boolean(redraw) ? redraw : true; + var self = this; + return $.Deferred(function(d){ + var index = self.ft.rows.all.push(self) - 1; + if (redraw){ + return self.ft.draw().then(function(){ + d.resolve(index); + }); + } else { + d.resolve(index); + } + }); + }; + + /** + * Removes the row from the table. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + * @returns {jQuery.Deferred} + */ + F.Row.prototype.delete = function(redraw){ + redraw = F.is.boolean(redraw) ? redraw : true; + var self = this; + return $.Deferred(function(d){ + var index = self.ft.rows.all.indexOf(self); + if (F.is.number(index) && index >= 0 && index < self.ft.rows.all.length){ + self.ft.rows.all.splice(index, 1); + if (redraw){ + return self.ft.draw().then(function(){ + d.resolve(self); + }); + } + } + d.resolve(self); + }); + }; + + if (F.is.defined(F.Paging)){ + // override the default add method with one that supports paging + F.Row.extend('add', function(redraw){ + redraw = F.is.boolean(redraw) ? redraw : true; + var self = this, + added = this._super(redraw), + editing = self.ft.use(F.Editing), + paging; + if (editing && editing.pageToNew && (paging = self.ft.use(F.Paging)) && redraw){ + return added.then(function(){ + var index = paging.unpaged.indexOf(self); // find this row in the unpaged array (this array will be sorted and filtered) + var page = Math.ceil((index + 1) / paging.size); // calculate the page the new row is on + if (paging.current !== page){ // goto the page if we need to + return paging.goto(page); + } + }); + } + return added; + }); + } + + if (F.is.defined(F.Sorting)){ + // override the default val method with one that supports sorting and paging + F.Row.extend('val', function(data, redraw){ + redraw = F.is.boolean(redraw) ? redraw : true; + var result = this._super(data); + if (!F.is.hash(data)){ + return result; + } + var self = this; + if (redraw){ + self.ft.draw().then(function(){ + var editing = self.ft.use(F.Editing), paging; + if (F.is.defined(F.Paging) && editing && editing.pageToNew && (paging = self.ft.use(F.Paging))){ + var index = paging.unpaged.indexOf(self); // find this row in the unpaged array (this array will be sorted and filtered) + var page = Math.ceil((index + 1) / paging.size); // calculate the page the new row is on + if (paging.current !== page){ // goto the page if we need to + return paging.goto(page); + } + } + }); + } + return result; + }); + } + +})(jQuery, FooTable); +(function(F){ + + /** + * Adds a row to the underlying {@link FooTable.Rows#all} array. + * @param {(object|FooTable.Row)} dataOrRow - A hash containing the row values or an actual {@link FooTable.Row} object. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + */ + F.Rows.prototype.add = function(dataOrRow, redraw){ + var row = dataOrRow; + if (F.is.hash(dataOrRow)){ + row = new FooTable.Row(this.ft, this.ft.columns.array, dataOrRow); + } + if (row instanceof FooTable.Row){ + row.add(redraw); + } + }; + + /** + * Updates a row in the underlying {@link FooTable.Rows#all} array. + * @param {(number|FooTable.Row)} indexOrRow - The index to update or the actual {@link FooTable.Row} object. + * @param {object} data - A hash containing the new row values. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + */ + F.Rows.prototype.update = function(indexOrRow, data, redraw){ + var len = this.ft.rows.all.length, + row = indexOrRow; + if (F.is.number(indexOrRow) && indexOrRow >= 0 && indexOrRow < len){ + row = this.ft.rows.all[indexOrRow]; + } + if (row instanceof FooTable.Row && F.is.hash(data)){ + row.val(data, redraw); + } + }; + + /** + * Deletes a row from the underlying {@link FooTable.Rows#all} array. + * @param {(number|FooTable.Row)} indexOrRow - The index to delete or the actual {@link FooTable.Row} object. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + */ + F.Rows.prototype.delete = function(indexOrRow, redraw){ + var len = this.ft.rows.all.length, + row = indexOrRow; + if (F.is.number(indexOrRow) && indexOrRow >= 0 && indexOrRow < len){ + row = this.ft.rows.all[indexOrRow]; + } + if (row instanceof FooTable.Row){ + row.delete(redraw); + } + }; + +})(FooTable); diff --git a/media/footable-v3/js/footable.editing.min.js b/media/footable-v3/js/footable.editing.min.js new file mode 100644 index 0000000..c7b23ca --- /dev/null +++ b/media/footable-v3/js/footable.editing.min.js @@ -0,0 +1,8 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +!function(a,b){b.Editing=b.Component.extend({construct:function(c){this._super(c,c.o.editing.enabled),this.pageToNew=c.o.editing.pageToNew,this.alwaysShow=c.o.editing.alwaysShow,this.column=a.extend(!0,{},c.o.editing.column,{visible:this.alwaysShow}),this.position=c.o.editing.position,this.showText=c.o.editing.showText,this.hideText=c.o.editing.hideText,this.addText=c.o.editing.addText,this.editText=c.o.editing.editText,this.deleteText=c.o.editing.deleteText,this.viewText=c.o.editing.viewText,this.allowAdd=c.o.editing.allowAdd,this.allowEdit=c.o.editing.allowEdit,this.allowDelete=c.o.editing.allowDelete,this.allowView=c.o.editing.allowView,this._$buttons=null,this.callbacks={addRow:b.checkFnValue(this,c.o.editing.addRow),editRow:b.checkFnValue(this,c.o.editing.editRow),deleteRow:b.checkFnValue(this,c.o.editing.deleteRow),viewRow:b.checkFnValue(this,c.o.editing.viewRow)}},preinit:function(c){var d=this;this.ft.raise("preinit.ft.editing",[c]).then(function(){if(d.ft.$el.hasClass("footable-editing")&&(d.enabled=!0),d.enabled=b.is["boolean"](c.editing)?c.editing:d.enabled,d.enabled){if(d.pageToNew=b.is["boolean"](c.editingPageToNew)?c.editingPageToNew:d.pageToNew,d.alwaysShow=b.is["boolean"](c.editingAlwaysShow)?c.editingAlwaysShow:d.alwaysShow,d.position=b.is.string(c.editingPosition)?c.editingPosition:d.position,d.showText=b.is.string(c.editingShowText)?c.editingShowText:d.showText,d.hideText=b.is.string(c.editingHideText)?c.editingHideText:d.hideText,d.addText=b.is.string(c.editingAddText)?c.editingAddText:d.addText,d.editText=b.is.string(c.editingEditText)?c.editingEditText:d.editText,d.deleteText=b.is.string(c.editingDeleteText)?c.editingDeleteText:d.deleteText,d.viewText=b.is.string(c.editingViewText)?c.editingViewText:d.viewText,d.allowAdd=b.is["boolean"](c.editingAllowAdd)?c.editingAllowAdd:d.allowAdd,d.allowEdit=b.is["boolean"](c.editingAllowEdit)?c.editingAllowEdit:d.allowEdit,d.allowDelete=b.is["boolean"](c.editingAllowDelete)?c.editingAllowDelete:d.allowDelete,d.allowView=b.is["boolean"](c.editingAllowView)?c.editingAllowView:d.allowView,d.column=new b.EditingColumn(d.ft,d,a.extend(!0,{},d.column,c.editingColumn,{visible:d.alwaysShow})),d.ft.$el.hasClass("footable-editing-left")&&(d.position="left"),d.ft.$el.hasClass("footable-editing-right")&&(d.position="right"),"right"===d.position)d.column.index=d.ft.columns.array.length;else{d.column.index=0;for(var e=0,f=d.ft.columns.array.length;f>e;e++)d.ft.columns.array[e].index+=1}d.ft.columns.array.push(d.column),d.ft.columns.array.sort(function(a,b){return a.index-b.index}),d.callbacks.addRow=b.checkFnValue(d,c.editingAddRow,d.callbacks.addRow),d.callbacks.editRow=b.checkFnValue(d,c.editingEditRow,d.callbacks.editRow),d.callbacks.deleteRow=b.checkFnValue(d,c.editingDeleteRow,d.callbacks.deleteRow),d.callbacks.viewRow=b.checkFnValue(d,c.editingViewRow,d.callbacks.viewRow)}},function(){d.enabled=!1})},init:function(){var a=this;this.ft.raise("init.ft.editing").then(function(){a.$create()},function(){a.enabled=!1})},destroy:function(){var a=this;this.ft.raise("destroy.ft.editing").then(function(){a.ft.$el.removeClass("footable-editing footable-editing-always-show footable-editing-no-add footable-editing-no-edit footable-editing-no-delete footable-editing-no-view").off("click.ft.editing").find("tfoot > tr.footable-editing").remove()})},$create:function(){var b=this,c="right"===b.position?"footable-editing-right":"footable-editing-left";b.ft.$el.addClass("footable-editing").addClass(c).on("click.ft.editing",".footable-show",{self:b},b._onShowClick).on("click.ft.editing",".footable-hide",{self:b},b._onHideClick).on("click.ft.editing",".footable-edit",{self:b},b._onEditClick).on("click.ft.editing",".footable-delete",{self:b},b._onDeleteClick).on("click.ft.editing",".footable-view",{self:b},b._onViewClick).on("click.ft.editing",".footable-add",{self:b},b._onAddClick),b.$cell=a("").attr("colspan",b.ft.columns.visibleColspan).append(b.$buttonShow()),b.allowAdd&&b.$cell.append(b.$buttonAdd()),b.$cell.append(b.$buttonHide()),b.alwaysShow&&b.ft.$el.addClass("footable-editing-always-show"),b.allowAdd||b.ft.$el.addClass("footable-editing-no-add"),b.allowEdit||b.ft.$el.addClass("footable-editing-no-edit"),b.allowDelete||b.ft.$el.addClass("footable-editing-no-delete"),b.allowView||b.ft.$el.addClass("footable-editing-no-view");var d=b.ft.$el.children("tfoot");0==d.length&&(d=a("
    ",{"class":"footable-editing"})).html(this.title)},parser:function(c){if(b.is.string(c)&&(c=a(a.trim(c))),b.is.element(c)&&(c=a(c)),b.is.jq(c)){var d=c.prop("tagName").toLowerCase();return"td"==d||"th"==d?c.data("value")||c.contents():c}return null},createCell:function(c){var d=this.editing.$rowButtons(),e=a("").append(d);return b.is.jq(c.$el)&&(0===this.index?e.prependTo(c.$el):e.insertAfter(c.$el.children().eq(this.index-1))),new b.Cell(this.ft,c,this,e||e.html())}}),b.columns.register("editing",b.EditingColumn)}(jQuery,FooTable),function(a,b){b.Defaults.prototype.editing={enabled:!1,pageToNew:!0,position:"right",alwaysShow:!1,addRow:function(){},editRow:function(a){},deleteRow:function(a){},viewRow:function(a){},showText:' Edit rows',hideText:"Cancel",addText:"New row",editText:'',deleteText:'',viewText:'',allowAdd:!0,allowEdit:!0,allowDelete:!0,allowView:!1,column:{classes:"footable-editing",name:"editing",title:"",filterable:!1,sortable:!1}}}(jQuery,FooTable),function(a,b){b.is.defined(b.Paging)&&(b.Paging.prototype.unpaged=[],b.Paging.extend("predraw",function(){this.unpaged=this.ft.rows.array.slice(0),this._super()}))}(jQuery,FooTable),function(a,b){b.Row.prototype.add=function(c){c=b.is["boolean"](c)?c:!0;var d=this;return a.Deferred(function(a){var b=d.ft.rows.all.push(d)-1;return c?d.ft.draw().then(function(){a.resolve(b)}):void a.resolve(b)})},b.Row.prototype["delete"]=function(c){c=b.is["boolean"](c)?c:!0;var d=this;return a.Deferred(function(a){var e=d.ft.rows.all.indexOf(d);return b.is.number(e)&&e>=0&&e=0&&e>b&&(f=this.ft.rows.all[b]),f instanceof FooTable.Row&&a.is.hash(c)&&f.val(c,d)},a.Rows.prototype["delete"]=function(b,c){var d=this.ft.rows.all.length,e=b;a.is.number(b)&&b>=0&&d>b&&(e=this.ft.rows.all[b]),e instanceof FooTable.Row&&e["delete"](c)}}(FooTable); \ No newline at end of file diff --git a/media/footable-v3/js/footable.filtering.js b/media/footable-v3/js/footable.filtering.js new file mode 100644 index 0000000..5b077a7 --- /dev/null +++ b/media/footable-v3/js/footable.filtering.js @@ -0,0 +1,1044 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +(function(F){ + F.Filter = F.Class.extend(/** @lends FooTable.Filter */{ + /** + * The filter object contains the query to filter by and the columns to apply it to. + * @constructs + * @extends FooTable.Class + * @param {string} name - The name for the filter. + * @param {(string|FooTable.Query)} query - The query for the filter. + * @param {Array.} columns - The columns to apply the query to. + * @param {string} [space="AND"] - How the query treats space chars. + * @param {boolean} [connectors=true] - Whether or not to replace phrase connectors (+.-_) with spaces. + * @param {boolean} [ignoreCase=true] - Whether or not ignore case when matching. + * @param {boolean} [hidden=true] - Whether or not this is a hidden filter. + * @returns {FooTable.Filter} + */ + construct: function(name, query, columns, space, connectors, ignoreCase, hidden){ + /** + * The name of the filter. + * @instance + * @type {string} + */ + this.name = name; + /** + * A string specifying how the filter treats space characters. Can be either "OR" or "AND". + * @instance + * @type {string} + */ + this.space = F.is.string(space) && (space == 'OR' || space == 'AND') ? space : 'AND'; + /** + * Whether or not to replace phrase connectors (+.-_) with spaces before executing the query. + * @instance + * @type {boolean} + */ + this.connectors = F.is.boolean(connectors) ? connectors : true; + /** + * Whether or not ignore case when matching. + * @instance + * @type {boolean} + */ + this.ignoreCase = F.is.boolean(ignoreCase) ? ignoreCase : true; + /** + * Whether or not this is a hidden filter. + * @instance + * @type {boolean} + */ + this.hidden = F.is.boolean(hidden) ? hidden : false; + /** + * The query for the filter. + * @instance + * @type {(string|FooTable.Query)} + */ + this.query = query instanceof F.Query ? query : new F.Query(query, this.space, this.connectors, this.ignoreCase); + /** + * The columns to apply the query to. + * @instance + * @type {Array.} + */ + this.columns = columns; + }, + /** + * Checks if the current filter matches the supplied string. + * If the current query property is a string it will be auto converted to a {@link FooTable.Query} object to perform the match. + * @instance + * @param {string} str - The string to check. + * @returns {boolean} + */ + match: function(str){ + if (!F.is.string(str)) return false; + if (F.is.string(this.query)){ + this.query = new F.Query(this.query, this.space, this.connectors, this.ignoreCase); + } + return this.query instanceof F.Query ? this.query.match(str) : false; + }, + /** + * Checks if the current filter matches the supplied {@link FooTable.Row}. + * @instance + * @param {FooTable.Row} row - The row to check. + * @returns {boolean} + */ + matchRow: function(row){ + var self = this, text = F.arr.map(row.cells, function(cell){ + return F.arr.contains(self.columns, cell.column) ? cell.filterValue : null; + }).join(' '); + return self.match(text); + } + }); + +})(FooTable); +(function ($, F) { + F.Filtering = F.Component.extend(/** @lends FooTable.Filtering */{ + /** + * The filtering component adds a search input and column selector dropdown to the table allowing users to filter the using space delimited queries. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component. + * @returns {FooTable.Filtering} + */ + construct: function (table) { + // call the constructor of the base class + this._super(table, table.o.filtering.enabled); + + /* PUBLIC */ + /** + * The filters to apply to the current {@link FooTable.Rows#array}. + * @instance + * @type {Array.} + */ + this.filters = table.o.filtering.filters; + /** + * The delay in milliseconds before the query is auto applied after a change. + * @instance + * @type {number} + */ + this.delay = table.o.filtering.delay; + /** + * The minimum number of characters allowed in the search input before it is auto applied. + * @instance + * @type {number} + */ + this.min = table.o.filtering.min; + /** + * Specifies how whitespace in a filter query is handled. + * @instance + * @type {string} + */ + this.space = table.o.filtering.space; + /** + * Whether or not to replace phrase connectors (+.-_) with spaces before executing the query. + * @instance + * @type {boolean} + */ + this.connectors = table.o.filtering.connectors; + /** + * Whether or not ignore case when matching. + * @instance + * @type {boolean} + */ + this.ignoreCase = table.o.filtering.ignoreCase; + /** + * Whether or not search queries are treated as phrases when matching. + * @instance + * @type {boolean} + */ + this.exactMatch = table.o.filtering.exactMatch; + /** + * The placeholder text to display within the search $input. + * @instance + * @type {string} + */ + this.placeholder = table.o.filtering.placeholder; + /** + * The title to display at the top of the search input column select. + * @type {string} + */ + this.dropdownTitle = table.o.filtering.dropdownTitle; + /** + * The position of the $search input within the filtering rows cell. + * @type {string} + */ + this.position = table.o.filtering.position; + /** + * The jQuery row object that contains all the filtering specific elements. + * @instance + * @type {jQuery} + */ + this.$row = null; + /** + * The jQuery cell object that contains the search input and column selector. + * @instance + * @type {jQuery} + */ + this.$cell = null; + /** + * The jQuery object of the column selector dropdown. + * @instance + * @type {jQuery} + */ + this.$dropdown = null; + /** + * The jQuery object of the search input. + * @instance + * @type {jQuery} + */ + this.$input = null; + /** + * The jQuery object of the search button. + * @instance + * @type {jQuery} + */ + this.$button = null; + + /* PRIVATE */ + /** + * The timeout ID for the filter changed event. + * @instance + * @private + * @type {?number} + */ + this._filterTimeout = null; + /** + * The regular expression used to check for encapsulating quotations. + * @instance + * @private + * @type {RegExp} + */ + this._exactRegExp = /^"(.*?)"$/; + }, + + /* PROTECTED */ + /** + * Checks the supplied data and options for the filtering component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.Filtering#"preinit.ft.filtering" + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.filtering event is raised before the UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Filtering#"preinit.ft.filtering" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + return self.ft.raise('preinit.ft.filtering').then(function(){ + // first check if filtering is enabled via the class being applied + if (self.ft.$el.hasClass('footable-filtering')) + self.enabled = true; + // then check if the data-filtering-enabled attribute has been set + self.enabled = F.is.boolean(data.filtering) + ? data.filtering + : self.enabled; + + // if filtering is not enabled exit early as we don't need to do anything else + if (!self.enabled) return; + + self.space = F.is.string(data.filterSpace) + ? data.filterSpace + : self.space; + + self.min = F.is.number(data.filterMin) + ? data.filterMin + : self.min; + + self.connectors = F.is.boolean(data.filterConnectors) + ? data.filterConnectors + : self.connectors; + + self.ignoreCase = F.is.boolean(data.filterIgnoreCase) + ? data.filterIgnoreCase + : self.ignoreCase; + + self.exactMatch = F.is.boolean(data.filterExactMatch) + ? data.filterExactMatch + : self.exactMatch; + + self.delay = F.is.number(data.filterDelay) + ? data.filterDelay + : self.delay; + + self.placeholder = F.is.string(data.filterPlaceholder) + ? data.filterPlaceholder + : self.placeholder; + + self.dropdownTitle = F.is.string(data.filterDropdownTitle) + ? data.filterDropdownTitle + : self.dropdownTitle; + + self.filters = F.is.array(data.filterFilters) + ? self.ensure(data.filterFilters) + : self.ensure(self.filters); + + if (self.ft.$el.hasClass('footable-filtering-left')) + self.position = 'left'; + if (self.ft.$el.hasClass('footable-filtering-center')) + self.position = 'center'; + if (self.ft.$el.hasClass('footable-filtering-right')) + self.position = 'right'; + + self.position = F.is.string(data.filterPosition) + ? data.filterPosition + : self.position; + },function(){ + self.enabled = false; + }); + }, + /** + * Initializes the filtering component for the plugin. + * @instance + * @protected + * @fires FooTable.Filtering#"init.ft.filtering" + */ + init: function () { + var self = this; + /** + * The init.ft.filtering event is raised before its UI is generated. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Filtering#"init.ft.filtering" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.ft.raise('init.ft.filtering').then(function(){ + self.$create(); + }, function(){ + self.enabled = false; + }); + }, + /** + * Destroys the filtering component removing any UI from the table. + * @instance + * @protected + * @fires FooTable.Filtering#"destroy.ft.filtering" + */ + destroy: function () { + /** + * The destroy.ft.filtering event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Filtering#"destroy.ft.filtering" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + return self.ft.raise('destroy.ft.filtering').then(function(){ + self.ft.$el.removeClass('footable-filtering') + .find('thead > tr.footable-filtering').remove(); + }); + }, + /** + * Creates the filtering UI from the current options setting the various jQuery properties of this component. + * @instance + * @protected + * @this FooTable.Filtering + */ + $create: function () { + var self = this; + // generate the cell that actually contains all the UI. + var $form_grp = $('
    ', {'class': 'form-group footable-filtering-search'}) + .append($('
    ').attr('colspan', self.ft.columns.visibleColspan).appendTo(self.$row); + self.$form = $('
    ', {'class': 'form-inline'}).append($form_grp).appendTo(self.$cell); + + self.$input = $('', {type: 'text', 'class': 'form-control', placeholder: self.placeholder}); + + self.$button = $('
    The below shows what is meant by the "left" side of a queryThe below shows what is meant by the "right" side of a query
    ").attr("colspan",d.ft.columns.visibleColspan).appendTo(d.$row),d.$form=a("",{"class":"form-inline"}).append(e).appendTo(d.$cell),d.$input=a("",{type:"text","class":"form-control",placeholder:d.placeholder}),d.$button=a("")).data("value",this.value).contents().detach().end().append(this.format(this.value)),this._setClasses(this.$el),this._setStyle(this.$el),this.$detail=a("
    ")).append(a("")),this.created=!0)},collapse:function(){this.created&&(this.$detail.children("th").html(this.column.title),this.$detail.children("td").first().attr("class",this.$el.attr("class")).attr("style",this.$el.attr("style")).css("display","table-cell").append(this.$el.contents().detach()),b.is.jq(this.$detail.parent())||this.$detail.appendTo(this.row.$details.find(".footable-details > tbody")))},restore:function(){if(this.created){if(b.is.jq(this.$detail.parent())){var a=this.$detail.children("td").first();this.$el.attr("class",a.attr("class")).attr("style",a.attr("style")).css("display",this.column.hidden||!this.column.visible?"none":"table-cell").append(a.contents().detach())}this.$detail.detach()}},parse:function(){return this.column.parser.call(this.column,this.$el,this.ft.o)},format:function(a){return this.column.formatter.call(this.column,a,this.ft.o)},val:function(c,d){if(b.is.undef(c))return this.value;var e=this,f=b.is.hash(c)&&b.is.hash(c.options)&&b.is.defined(c.value);if(this.o=a.extend(!0,{classes:e.classes,style:e.style},f?c.options:{}),this.value=f?c.value:c,this.classes=b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},this.created){this.$el.data("value",this.value).empty();var g=this.$detail.children("td").first().empty(),h=b.is.jq(this.$detail.parent())?g:this.$el;h.append(this.format(this.value)),this._setClasses(h),this._setStyle(h),(b.is["boolean"](d)?d:!0)&&this.row.draw()}},_setClasses:function(a){var c=!b.is.emptyArray(this.column.classes),d=!b.is.emptyArray(this.classes),e=null;a.removeAttr("class"),(c||d)&&(c&&d?e=this.classes.concat(this.column.classes).join(" "):c?e=this.column.classes.join(" "):d&&(e=this.classes.join(" ")),b.is.emptyString(e)||a.addClass(e))},_setStyle:function(c){var d=!b.is.emptyObject(this.column.style),e=!b.is.emptyObject(this.style),f=null;c.removeAttr("style"),(d||e)&&(d&&e?f=a.extend({},this.column.style,this.style):d?f=this.column.style:e&&(f=this.style),b.is.hash(f)&&c.css(f))}})}(jQuery,FooTable),function(a,b){b.Column=b.Class.extend({construct:function(a,c,d){this.ft=a,this.type=b.is.emptyString(d)?"text":d,this.virtual=b.is["boolean"](c.virtual)?c.virtual:!1,this.$el=b.is.jq(c.$el)?c.$el:null,this.index=b.is.number(c.index)?c.index:-1,this.define(c),this.$create()},define:function(a){this.hidden=b.is["boolean"](a.hidden)?a.hidden:!1,this.visible=b.is["boolean"](a.visible)?a.visible:!0,this.name=b.is.string(a.name)?a.name:null,null==this.name&&(this.name="col"+(a.index+1)),this.title=b.is.string(a.title)?a.title:null,!this.virtual&&null==this.title&&b.is.jq(this.$el)&&(this.title=this.$el.html()),null==this.title&&(this.title="Column "+(a.index+1)),this.style=b.is.hash(a.style)?a.style:b.is.string(a.style)?b.css2json(a.style):{},this.classes=b.is.array(a.classes)?a.classes:b.is.string(a.classes)?a.classes.match(/\S+/g):[],this.parser=b.checkFnValue(this,a.parser,this.parser),this.formatter=b.checkFnValue(this,a.formatter,this.formatter)},$create:function(){(this.$el=!this.virtual&&b.is.jq(this.$el)?this.$el:a("")).html(this.title)},parser:function(c){return b.is.element(c)||b.is.jq(c)?a(c).data("value")||a(c).text():b.is.defined(c)&&null!=c?c+"":null},formatter:function(a){return null==a?"":a},createCell:function(a){var c=b.is.jq(a.$el)?a.$el.children("td,th").get(this.index):null,d=b.is.hash(a.value)?a.value[this.name]:null;return new b.Cell(this.ft,a,this,c||d)}}),b.columns=new b.ClassFactory,b.columns.register("text",b.Column)}(jQuery,FooTable),function(a,b){b.Defaults=function(){this.stopPropagation=!1,this.on=null},b.defaults=new b.Defaults}(jQuery,FooTable),function(a,b){b.Row=b.Class.extend({construct:function(a,b,c){this.ft=a,this.columns=b,this.created=!1,this.define(c),this.val()},define:function(c){this.$el=b.is.element(c)||b.is.jq(c)?a(c):null,this.$toggle=a("",{"class":"footable-toggle fooicon fooicon-plus"});var d=b.is.hash(c),e=d&&b.is.hash(c.options)&&b.is.hash(c.value);this.value=d?e?c.value:c:null,this.o=a.extend(!0,{expanded:!1,classes:null,style:null},e?c.options:{}),this.expanded=b.is.jq(this.$el)?this.$el.data("expanded")||this.o.expanded:this.o.expanded,this.classes=b.is.jq(this.$el)&&this.$el.attr("class")?this.$el.attr("class").match(/\S+/g):b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.jq(this.$el)&&this.$el.attr("style")?b.css2json(this.$el.attr("style")):b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},this.cells=this.createCells()},$create:function(){if(!this.created){(this.$el=b.is.jq(this.$el)?this.$el:a("
    ",{colspan:this.ft.columns.visibleColspan}).append(a("",{"class":"footable-details "+this.ft.classes.join(" ")}).append("")));var c=this;b.arr.each(c.cells,function(a){a.created||a.$create(),c.$el.append(a.$el)}),c.$el.off("click.ft.row").on("click.ft.row",{self:c},c._onToggle),this.created=!0}},createCells:function(){var a=this;return b.arr.map(a.columns,function(b){return b.createCell(a)})},val:function(c){var d=this;if(!b.is.hash(c))return b.is.hash(this.value)&&!b.is.emptyObject(this.value)||(this.value={},b.arr.each(this.cells,function(a){d.value[a.column.name]=a.val()})),this.value;this.collapse(!1);var e=b.is.hash(c),f=e&&b.is.hash(c.options)&&b.is.hash(c.value);this.o=a.extend(!0,{expanded:d.expanded,classes:d.classes,style:d.style},f?c.options:{}),this.expanded=this.o.expanded,this.classes=b.is.array(this.o.classes)?this.o.classes:b.is.string(this.o.classes)?this.o.classes.match(/\S+/g):[],this.style=b.is.hash(this.o.style)?this.o.style:b.is.string(this.o.style)?b.css2json(this.o.style):{},this.value=e?f?c.value:c:null,b.arr.each(this.cells,function(a){b.is.defined(d.value[a.column.name])&&a.val(d.value[a.column.name],!1)}),this.created&&(this._setClasses(this.$el),this._setStyle(this.$el),this.draw())},_setClasses:function(a){var c=!b.is.emptyArray(this.classes),d=null;a.removeAttr("class"),c&&(d=this.classes.join(" "),b.is.emptyString(d)||a.addClass(d))},_setStyle:function(a){var c=!b.is.emptyObject(this.style),d=null;a.removeAttr("style"),c&&(d=this.style,b.is.hash(d)&&a.css(d))},expand:function(){this.created&&(this.__hidden__=b.arr.map(this.cells,function(a){return a.column.hidden&&a.column.visible?a:null}),this.__hidden__.length>0&&(this.$details.insertAfter(this.$el).children("td").first().attr("colspan",this.ft.columns.visibleColspan),b.arr.each(this.__hidden__,function(a){a.collapse()})),this.$el.attr("data-expanded",!0),this.$toggle.removeClass("fooicon-plus").addClass("fooicon-minus"),this.expanded=!0)},collapse:function(a){this.created&&(b.arr.each(this.__hidden__,function(a){a.restore()}),this.$details.detach(),this.$el.removeAttr("data-expanded"),this.$toggle.removeClass("fooicon-minus").addClass("fooicon-plus"),(b.is["boolean"](a)?a:!0)&&(this.expanded=!1))},predraw:function(){this.created&&(this.expanded&&this.collapse(!1),this.$toggle.detach(),this.$el.detach())},draw:function(a){this.created||this.$create(),b.is.jq(a)&&a.append(this.$el);var c=this;b.arr.each(c.cells,function(a){a.$el.css("display",a.column.hidden||!a.column.visible?"none":"table-cell"),c.ft.rows.showToggle&&c.ft.columns.hasHidden&&("first"==c.ft.rows.toggleColumn&&a.column.index==c.ft.columns.firstVisibleIndex||"last"==c.ft.rows.toggleColumn&&a.column.index==c.ft.columns.lastVisibleIndex)&&a.$el.prepend(c.$toggle)}),this.expanded&&this.expand()},toggle:function(){this.created&&this.ft.columns.hasHidden&&(this.expanded?this.collapse():this.expand())},_onToggle:function(b){var c=b.data.self;a(b.target).is("tr,td,.footable-toggle")&&c.toggle()}})}(jQuery,FooTable),function(a,b){b.instances=[],b.Table=b.Class.extend({construct:function(c,d,e){this._resizeTimeout=null,this.id=b.instances.push(this),this.initialized=!1,this.$el=(b.is.jq(c)?c:a(c)).first(),this.o=a.extend(!0,{},b.defaults,d),this.classes=[],this.breakpoints=b.components.internal.make("breakpoints",this),this.columns=b.components.internal.make("columns",this),this.rows=b.components.internal.make("rows",this),this.components={internal:[this.breakpoints,this.columns,this.rows],core:b.components.core.load(this),custom:b.components.load(this)};var f=this;f._preinit().then(function(){return f._init().then(function(){b.is.fn(e)&&e.call(f,f)})},function(a){b.is.error(a)&&console.error("FooTable: unhandled error thrown during initialization.",a)})},_preinit:function(){var c=this;return this.raise("preinit.ft.table").then(function(){var d=c.$el.attr("class").match(/\S+/g),e=c.$el.data()||{};c.o.ajax=b.checkFnValue(c,e.ajax,c.o.ajax),c.o.stopPropagation=b.is["boolean"](e.stopPropagation)?e.stopPropagation:c.o.stopPropagation;for(var f=0,g=d.length;g>f;f++)b.str.startsWith(d[f],"footable")||c.classes.push(d[f]);var h=a("
    ",{"class":"footable-loader"}).append(a("",{"class":"fooicon fooicon-loader"}));return c.$el.hide().after(h),c.execute(!1,!1,"preinit",e).always(function(){c.$el.show(),h.remove()})})},_init:function(){var c=this;return c.raise("init.ft.table").then(function(){var d=c.$el.children("thead"),e=c.$el.children("tbody"),f=c.$el.children("tfoot");return c.$el.addClass("footable footable-"+c.id),b.is.hash(c.o.on)&&c.$el.on(c.o.on),0==f.length&&c.$el.append(f=a("
    ")),0==e.length&&c.$el.append(""),0==d.length&&c.$el.prepend(d=a("")),c.execute(!1,!0,"init").then(function(){return c.$el.data("__FooTable__",c),0==f.children("tr").length&&f.remove(),0==d.children("tr").length&&d.remove(),c.draw().then(function(){a(window).off("resize.ft"+c.id,c._onWindowResize).on("resize.ft"+c.id,{self:c},c._onWindowResize),c.initialized=!0})})})},destroy:function(){var a=this;return a.raise("destroy.ft.table").then(function(){return a.execute(!0,!0,"destroy").then(function(){a.$el.removeData("__FooTable__"),b.is.hash(a.o.on)&&a.$el.off(a.o.on),a.initialized=!1})},function(a){b.is.error(a)&&console.error("FooTable: unhandled error thrown while destroying the plugin.",a)})},raise:function(c,d){var e=this,f=b.__debug__&&(b.is.emptyArray(b.__debug_options__.events)||b.arr.any(b.__debug_options__.events,function(a){return b.str.contains(c,a)}));return d=d||[],d.unshift(this),a.Deferred(function(b){var g=a.Event(c);1==e.o.stopPropagation&&e.$el.one(c,function(a){a.stopPropagation()}),f&&console.log("FooTable:"+c+": ",d),e.$el.trigger(g,d),g.isDefaultPrevented()?(f&&console.log('FooTable: default prevented for the "'+c+'" event.'),b.reject(g)):b.resolve(g)})},use:function(a){for(var b=this.components.internal.concat(this.components.core,this.components.custom),c=0,d=b.length;d>c;c++)if(b[c]instanceof a)return b[c];return null},draw:function(){var a=this;return a.execute(!1,!0,"predraw").then(function(){return a.raise("predraw.ft.table").then(function(){return a.execute(!1,!0,"draw").then(function(){return a.raise("draw.ft.table").then(function(){return a.execute(!1,!0,"postdraw").then(function(){a.raise("postdraw.ft.table")})})})})},function(a){b.is.error(a)&&console.error("FooTable: unhandled error thrown during a draw operation.",a)})},execute:function(a,c,d,e,f){var g=this,h=Array.prototype.slice.call(arguments);a=h.shift(),c=h.shift();var i=g.components.internal.slice(0),j=c?b.arr.get(g.components.core,function(a){return a.enabled}):g.components.core.slice(0),k=c?b.arr.get(g.components.custom,function(a){return a.enabled}):g.components.custom.slice(0);return h.unshift(a?k.reverse():i),g._execute.apply(g,h).then(function(){return h.shift(),h.unshift(a?j.reverse():j),g._execute.apply(g,h).then(function(){return h.shift(),h.unshift(a?i.reverse():k),g._execute.apply(g,h)})},function(a){b.is.error(a)&&console.error('FooTable: unhandled error thrown while executing "'+d+'".',a)})},_execute:function(c,d,e,f){if(!c||!c.length)return a.when();var g,h=this,i=Array.prototype.slice.call(arguments);return c=i.shift(),d=i.shift(),g=c.shift(),b.is.fn(g[d])?a.Deferred(function(a){try{var c=g[d].apply(g,i);if(b.is.promise(c))return c.then(a.resolve,a.reject);a.resolve(c)}catch(e){a.reject(e)}}).then(function(){return h._execute.apply(h,[c,d].concat(i))}):h._execute.apply(h,[c,d].concat(i))},_onWindowResize:function(a){var b=a.data.self;null!=b._resizeTimeout&&clearTimeout(b._resizeTimeout),b._resizeTimeout=setTimeout(function(){b._resizeTimeout=null,b.raise("resize.ft.table").then(function(){b.breakpoints.check()})},300)}})}(jQuery,FooTable),function(a,b){b.is.undef(window.moment)||(b.DateColumn=b.Column.extend({construct:function(a,c){this._super(a,c,"date"),this.formatString=b.is.string(c.formatString)?c.formatString:"MM-DD-YYYY"},parser:function(c){if((b.is.element(c)||b.is.jq(c))&&(c=a(c).data("value")||a(c).text(),b.is.string(c)&&(c=isNaN(c)?c:+c)),b.is.date(c))return moment(c);if(b.is.object(c)&&b.is["boolean"](c._isAMomentObject))return c;if(b.is.string(c)){if(isNaN(c))return moment(c,this.formatString);c=+c}return b.is.number(c)?moment(c):null},formatter:function(a){return b.is.object(a)&&b.is["boolean"](a._isAMomentObject)?a.format(this.formatString):""},filterValue:function(c){if((b.is.element(c)||b.is.jq(c))&&(c=a(c).data("filterValue")||a(c).text()),b.is.hash(c)&&b.is.hash(c.options)&&(b.is.string(c.options.filterValue)&&(c=c.options.filterValue),b.is.defined(c.value)&&(c=c.value)),b.is.object(c)&&b.is["boolean"](c._isAMomentObject))return c.format(this.formatString);if(b.is.string(c)){if(isNaN(c))return c;c=+c}return b.is.number(c)||b.is.date(c)?moment(c).format(this.formatString):b.is.defined(c)&&null!=c?c+"":""}}),b.columns.register("date",b.DateColumn))}(jQuery,FooTable),function(a,b){b.HTMLColumn=b.Column.extend({construct:function(a,b){this._super(a,b,"html")},parser:function(c){if(b.is.string(c)&&(c=a(a.trim(c))),b.is.element(c)&&(c=a(c)),b.is.jq(c)){var d=c.prop("tagName").toLowerCase();return"td"==d||"th"==d?c.data("value")||c.contents():c}return null}}),b.columns.register("html",b.HTMLColumn)}(jQuery,FooTable),function(a,b){b.NumberColumn=b.Column.extend({construct:function(a,c){this._super(a,c,"number"),this.decimalSeparator=b.is.string(c.decimalSeparator)?c.decimalSeparator:".",this.thousandSeparator=b.is.string(c.thousandSeparator)?c.thousandSeparator:".",this.decimalSeparatorRegex=new RegExp(b.str.escapeRegExp(this.decimalSeparator),"g"),this.thousandSeparatorRegex=new RegExp(b.str.escapeRegExp(this.thousandSeparator),"g"),this.cleanRegex=new RegExp("[^0-9"+b.str.escapeRegExp(this.decimalSeparator)+"]","g")},parser:function(c){return(b.is.element(c)||b.is.jq(c))&&(c=a(c).data("value")||a(c).text().replace(this.cleanRegex,"")),b.is.string(c)&&(c=c.replace(this.thousandSeparatorRegex,"").replace(this.decimalSeparatorRegex,"."),c=parseFloat(c)),b.is.number(c)?c:null},formatter:function(a){if(null==a)return"";var b=(a+"").split(".");return 2==b.length&&b[0].length>3&&(b[0]=b[0].replace(/\B(?=(?:\d{3})+(?!\d))/g,this.thousandSeparator)),b.join(this.decimalSeparator)}}),b.columns.register("number",b.NumberColumn)}(jQuery,FooTable),function(a,b){b.Component=b.Class.extend({construct:function(a,c){if(!(a instanceof b.Table))throw new TypeError("The instance parameter must be an instance of FooTable.Table.");this.ft=a,this.enabled=b.is["boolean"](c)?c:!1},preinit:null,init:null,destroy:null,predraw:null,draw:null,postdraw:null}),b.components=new b.ClassFactory,b.components.core=new b.ClassFactory,b.components.internal=new b.ClassFactory}(jQuery,FooTable),function(a,b){b.Breakpoint=b.Class.extend({construct:function(a,b){this.name=a,this.width=b}})}(jQuery,FooTable),function(a,b){b.Breakpoints=b.Component.extend({construct:function(a){this._super(a,!0),this.o=a.o,this.current=null,this.array=[],this.cascade=this.o.cascade,this.useParentWidth=this.o.useParentWidth,this.hidden=null,this._classNames="",this.getWidth=b.checkFnValue(this,this.o.getWidth,this.getWidth)},preinit:function(a){var c=this;return this.ft.raise("preinit.ft.breakpoints",[a]).then(function(){c.cascade=b.is["boolean"](a.cascade)?a.cascade:c.cascade,c.o.breakpoints=b.is.hash(a.breakpoints)?a.breakpoints:c.o.breakpoints,c.getWidth=b.checkFnValue(c,a.getWidth,c.getWidth),null==c.o.breakpoints&&(c.o.breakpoints={xs:480,sm:768,md:992,lg:1200});for(var d in c.o.breakpoints)c.o.breakpoints.hasOwnProperty(d)&&(c.array.push(new b.Breakpoint(d,c.o.breakpoints[d])),c._classNames+="breakpoint-"+d+" ");c.array.sort(function(a,b){return b.width-a.width})})},init:function(){var a=this;return this.ft.raise("init.ft.breakpoints").then(function(){a.current=a.get()})},draw:function(){this.ft.$el.removeClass(this._classNames).addClass("breakpoint-"+this.current.name)},calculate:function(){for(var a,c=this,d=null,e=[],f=null,g=c.getWidth(),h=0,i=c.array.length;i>h;h++)a=c.array[h],(!d&&h==i-1||g>=a.width&&(f instanceof b.Breakpoint?g<=f.width:!0))&&(d=a),d||e.push(a.name),f=a;return e.push(d.name),c.hidden=e.join(" "),d},visible:function(a){if(b.is.emptyString(a))return!0;if("all"===a)return!1;for(var c=a.split(" "),d=0,e=c.length;e>d;d++)if(this.cascade?b.str.containsWord(this.hidden,c[d]):c[d]==this.current.name)return!1;return!0},check:function(){var a=this,c=a.get();c instanceof b.Breakpoint&&c!=a.current&&a.ft.raise("before.ft.breakpoints",[a.current,c]).then(function(){var b=a.current;return a.current=c,a.ft.draw().then(function(){a.ft.raise("after.ft.breakpoints",[a.current,b])})})},get:function(a){return b.is.undef(a)?this.calculate():a instanceof b.Breakpoint?a:b.is.string(a)?b.arr.first(this.array,function(b){return b.name==a}):b.is.number(a)&&a>=0&&af&&(f=a.index)}),f++;for(var g,h,i=0;f>i;i++)g={},b.arr.each(c,function(a){return a.index==i?(g=a,!1):void 0}),h={},b.arr.each(d,function(a){return a.index==i?(h=a,!1):void 0}),e.push(a.extend(!0,{},g,h))}return e}var f,g,h=[],i=[],j=d.ft.$el.find("tr.footable-header");if(0==j.length&&(j=d.ft.$el.find("thead > tr:last:has([data-breakpoints])")),0==j.length&&(j=d.ft.$el.find("tbody > tr:first:has([data-breakpoints])")),j.length>0){var k=j.parent().is("tbody")&&j.children().length==j.children("td").length;k||(d.$header=j.addClass("footable-header")),j.children("td,th").each(function(b,c){f=a(c),g=f.data(),g.index=b,g.$el=f,g.virtual=k,i.push(g)}),k&&(d.showHeader=!1)}b.is.array(d.o.columns)?(b.arr.each(d.o.columns,function(a,b){a.index=b,h.push(a)}),d.parseFinalize(c,e(h,i))):b.is.promise(d.o.columns)?d.o.columns.then(function(a){b.arr.each(a,function(a,b){a.index=b,h.push(a)}),d.parseFinalize(c,e(h,i))},function(a){c.reject(Error("Columns ajax request error: "+a.status+" ("+a.statusText+")"))}):d.parseFinalize(c,e(h,i))})},parseFinalize:function(a,c){var d,e=this,f=[];b.arr.each(c,function(a){(d=b.columns.contains(a.type)?b.columns.make(a.type,e.ft,a):new b.Column(e.ft,a))&&f.push(d)}),b.is.emptyArray(f)?a.reject(Error("No columns supplied.")):(f.sort(function(a,b){return a.index-b.index}),a.resolve(f))},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.columns",[a]).then(function(){return c.parse(a).then(function(d){c.array=d,c.showHeader=b.is["boolean"](a.showHeader)?a.showHeader:c.showHeader})})},init:function(){var a=this;return this.ft.raise("init.ft.columns",[a.array]).then(function(){a.$create()})},predraw:function(){var a=this,c=!0;a.visibleColspan=0,a.firstVisibleIndex=0,a.lastVisibleIndex=0,a.hasHidden=!1,b.arr.each(a.array,function(b){b.hidden=!a.ft.breakpoints.visible(b.breakpoints),!b.hidden&&b.visible&&(c&&(a.firstVisibleIndex=b.index,c=!1),a.lastVisibleIndex=b.index, +a.visibleColspan++),b.hidden&&(a.hasHidden=!0)})},draw:function(){b.arr.each(this.array,function(a){a.$el.css("display",a.hidden||!a.visible?"none":"table-cell")}),!this.showHeader&&b.is.jq(this.$header.parent())&&this.$header.detach()},$create:function(){var c=this;c.$header=b.is.jq(c.$header)?c.$header:a("",{"class":"footable-header"}),c.$header.children("th,td").detach(),b.arr.each(c.array,function(a){c.$header.append(a.$el)}),c.showHeader&&!b.is.jq(c.$header.parent())&&c.ft.$el.children("thead").append(c.$header)},get:function(a){return a instanceof b.Column?a:b.is.string(a)?b.arr.first(this.array,function(b){return b.name==a}):b.is.number(a)?b.arr.first(this.array,function(b){return b.index==a}):b.is.fn(a)?b.arr.get(this.array,a):null},ensure:function(a){var c=this,d=[];return b.is.array(a)?(b.arr.each(a,function(a){d.push(c.get(a))}),d):d}}),b.components.internal.register("columns",b.Columns,5)}(jQuery,FooTable),function(a){a.Defaults.prototype.columns=[],a.Defaults.prototype.showHeader=!0}(FooTable),function(a,b){b.Rows=b.Component.extend({construct:function(a){this._super(a,!0),this.o=a.o,this.array=[],this.all=[],this.showToggle=a.o.showToggle,this.toggleColumn=a.o.toggleColumn,this.emptyString=a.o.empty,this.expandFirst=a.o.expandFirst,this.$empty=null},parse:function(){var c=this;return a.Deferred(function(a){var d=c.ft.$el.children("tbody").children("tr");b.is.jq(d)?(c.parseFinalize(a,d),d.detach()):b.is.array(c.o.rows)&&c.o.rows.length>0?c.parseFinalize(a,c.o.rows):b.is.promise(c.o.rows)?c.o.rows.then(function(b){c.parseFinalize(a,b)},function(b){a.reject(Error("Rows ajax request error: "+b.status+" ("+b.statusText+")"))}):c.parseFinalize(a,[])})},parseFinalize:function(c,d){var e=this,f=a.map(d,function(a){return new b.Row(e.ft,e.ft.columns.array,a)});c.resolve(f)},preinit:function(a){var c=this;return c.ft.raise("preinit.ft.rows",[a]).then(function(){return c.parse().then(function(d){c.all=d,c.array=c.all.slice(0),c.showToggle=b.is["boolean"](a.showToggle)?a.showToggle:c.showToggle,c.toggleColumn=b.is.string(a.toggleColumn)?a.toggleColumn:c.toggleColumn,"first"!=c.toggleColumn&&"last"!=c.toggleColumn&&(c.toggleColumn="first"),c.emptyString=b.is.string(a.empty)?a.empty:c.emptyString,c.expandFirst=b.is["boolean"](a.expandFirst)?a.expandFirst:c.expandFirst})})},init:function(){var a=this;return a.ft.raise("init.ft.rows",[a.all]).then(function(){a.$create()})},predraw:function(){b.arr.each(this.array,function(a){a.predraw()}),this.array=this.all.slice(0)},$create:function(){this.$empty=a("",{"class":"footable-empty"}).append(a("",{"class":"footable-filtering"}).prependTo(d.ft.$el.children("thead")),d.$cell=a(""),c.ft.$el.append(f)),c.$row=a("",{"class":"footable-paging"}).append(c.$cell).appendTo(f),c.$pagination=a("
      ",{"class":"pagination"}).on("click.footable","a.footable-page-link",{self:c},c._onPageClicked),c.$count=a("",{"class":"label label-default"}),c.$pagination.empty(),d&&(c.$pagination.append(e("first",c.strings.first,"footable-page-nav")),c.$pagination.append(e("prev",c.strings.prev,"footable-page-nav")),c.limit>0&&c.limit0&&c.limit",{"class":"divider"}),c.$count),c._total=c.total}},first:function(){return this._set(1)},prev:function(){return this._set(this.current-1>0?this.current-1:1)},next:function(){return this._set(this.current+1this.total?this.total:1>a?1:a)},prevPages:function(){var a=this.$pagination.children("li.footable-page.visible:first").data("page")-1;this._setVisible(a,!0),this._setNavigation(!1)},nextPages:function(){var a=this.$pagination.children("li.footable-page.visible:last").data("page")+1;this._setVisible(a,!1),this._setNavigation(!1)},pageSize:function(a){return b.is.number(a)?(this.size=a,this.total=Math.ceil(this.ft.rows.all.length/this.size),b.is.jq(this.$row)&&this.$row.remove(),this.$create(),void this.ft.draw()):this.size},_set:function(c){var d=this,e=new b.Pager(d.total,d.current,d.size,c,c>d.current);return d.ft.raise("before.ft.paging",[e]).then(function(){return e.page=e.page>e.total?e.total:e.page,e.page=e.page<1?1:e.page,d.current==c?a.when():(d._previous=d.current,d.current=e.page,d.ft.draw().then(function(){d.ft.raise("after.ft.paging",[e])}))})},_setNavigation:function(a){1==this.current?this.$pagination.children('li[data-page="first"],li[data-page="prev"]').addClass("disabled"):this.$pagination.children('li[data-page="first"],li[data-page="prev"]').removeClass("disabled"),this.current==this.total?this.$pagination.children('li[data-page="next"],li[data-page="last"]').addClass("disabled"):this.$pagination.children('li[data-page="next"],li[data-page="last"]').removeClass("disabled"),1==(this.$pagination.children("li.footable-page.visible:first").data("page")||1)?this.$pagination.children('li[data-page="prev-limit"]').addClass("disabled"):this.$pagination.children('li[data-page="prev-limit"]').removeClass("disabled"),(this.$pagination.children("li.footable-page.visible:last").data("page")||this.limit)==this.total?this.$pagination.children('li[data-page="next-limit"]').addClass("disabled"):this.$pagination.children('li[data-page="next-limit"]').removeClass("disabled"),this.limit>0&&this.total0&&this.total>this.limit){if(!this.$pagination.children('li.footable-page[data-page="'+a+'"]').hasClass("visible")){var c=0,d=0;1==b?(d=a>this.total?this.total:a,c=d-this.limit):(c=1>a?0:a-1,d=c+this.limit),0>c&&(c=0,d=this.limit>this.total?this.total:this.limit),d>this.total&&(d=this.total,c=this.total-this.limit<0?0:this.total-this.limit),this.$pagination.children("li.footable-page").removeClass("visible").slice(c,d).addClass("visible")}}else this.$pagination.children("li.footable-page").removeClass("visible").slice(0,this.total).addClass("visible");var e=this.size*(a-1)+1,f=this.size*a;0==this.ft.rows.array.length?(e=0,f=0):f=f>this._total?this._total:f,this._setCount(a,this.total,e,f,this._total)},_setCount:function(a,b,c,d,e){this.$count.text(this.countFormat.replace(/\{CP}/g,a).replace(/\{TP}/g,b).replace(/\{PF}/g,c).replace(/\{PL}/g,d).replace(/\{TR}/g,e))},_onPageClicked:function(b){if(b.preventDefault(),!a(b.target).closest("li").is(".active,.disabled")){var c=b.data.self,d=a(this).data("page");switch(d){case"first":return void c.first();case"prev":return void c.prev();case"next":return void c.next();case"last":return void c.last();case"prev-limit":return void c.prevPages();case"next-limit":return void c.nextPages();default:return void c._set(d)}}}}),b.components.core.register("paging",b.Paging,0)}(jQuery,FooTable),function(a){a.Defaults.prototype.paging={enabled:!1,countFormat:"{CP} of {TP}",current:1,limit:5,position:"center",size:10,strings:{first:"«",prev:"‹",next:"›",last:"»",prevPages:"...",nextPages:"..."}}}(FooTable),function(a){a.Table.prototype.gotoPage=function(b){return this.use(a.Paging)["goto"](b)},a.Table.prototype.nextPage=function(){return this.use(a.Paging).next()},a.Table.prototype.prevPage=function(){return this.use(a.Paging).prev()},a.Table.prototype.firstPage=function(){return this.use(a.Paging).first()},a.Table.prototype.lastPage=function(){return this.use(a.Paging).last()},a.Table.prototype.nextPages=function(){return this.use(a.Paging).nextPages()},a.Table.prototype.prevPages=function(){return this.use(a.Paging).prevPages()},a.Table.prototype.pageSize=function(b){return this.use(a.Paging).pageSize(b)}}(FooTable); \ No newline at end of file diff --git a/media/footable-v3/js/footable.paging.js b/media/footable-v3/js/footable.paging.js new file mode 100644 index 0000000..65de708 --- /dev/null +++ b/media/footable-v3/js/footable.paging.js @@ -0,0 +1,761 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +(function($, F){ + + F.Pager = F.Class.extend(/** @lends FooTable.Pager */{ + /** + * The pager object contains the page number and direction to page to. + * @constructs + * @extends FooTable.Class + * @param {number} total - The total number of pages available. + * @param {number} current - The current page number. + * @param {number} size - The number of rows per page. + * @param {number} page - The page number to goto. + * @param {boolean} forward - A boolean indicating the direction of paging, TRUE = forward, FALSE = back. + * @returns {FooTable.Pager} + */ + construct: function(total, current, size, page, forward){ + /** + * The total number of pages available. + * @type {number} + */ + this.total = total; + /** + * The current page number. + * @type {number} + */ + this.current = current; + /** + * The number of rows per page. + * @type {number} + */ + this.size = size; + /** + * The page number to goto. + * @type {number} + */ + this.page = page; + /** + * A boolean indicating the direction of paging, TRUE = forward, FALSE = back. + * @type {boolean} + */ + this.forward = forward; + } + }); + +})(jQuery, FooTable); +(function($, F){ + F.Paging = F.Component.extend(/** @lends FooTable.Paging */{ + /** + * The paging component adds a pagination control to the table allowing users to navigate table rows via pages. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component. + * @returns {FooTable.Filtering} + */ + construct: function(table){ + // call the base constructor + this._super(table, table.o.paging.enabled); + + /* PROTECTED */ + /** + * An object containing the strings used by the paging buttons. + * @type {{ first: string, prev: string, next: string, last: string }} + */ + this.strings = table.o.paging.strings; + + /* PUBLIC */ + /** + * The current page number to display. + * @instance + * @type {number} + */ + this.current = table.o.paging.current; + /** + * The number of rows to display per page. + * @instance + * @type {number} + */ + this.size = table.o.paging.size; + /** + * The maximum number of page links to display at once. + * @instance + * @type {number} + */ + this.limit = table.o.paging.limit; + /** + * The position of the pagination control within the paging rows cell. + * @instance + * @type {string} + */ + this.position = table.o.paging.position; + /** + * The format string used to generate the text displayed under the pagination control. + * @instance + * @type {string} + */ + this.countFormat = table.o.paging.countFormat; + /** + * The total number of pages. + * @instance + * @type {number} + */ + this.total = -1; + /** + * The number of rows in the {@link FooTable.Rows#array} before paging is applied. + * @instance + * @type {number} + */ + this.totalRows = 0; + /** + * A number indicating the previous page displayed. + * @instance + * @type {number} + */ + this.previous = -1; + /** + * The count string generated using the {@link FooTable.Filtering#countFormat} option. This value is only set after the first call to the {@link FooTable.Filtering#predraw} method. + * @instance + * @type {string} + */ + this.formattedCount = null; + /** + * The jQuery row object that contains all the paging specific elements. + * @instance + * @type {jQuery} + */ + this.$row = null; + /** + * The jQuery cell object that contains the pagination control and total count. + * @instance + * @type {jQuery} + */ + this.$cell = null; + /** + * The jQuery object that contains the links for the pagination control. + * @instance + * @type {jQuery} + */ + this.$pagination = null; + /** + * The jQuery object that contains the row count. + * @instance + * @type {jQuery} + */ + this.$count = null; + /** + * Whether or not the pagination row is detached from the table. + * @instance + * @type {boolean} + */ + this.detached = true; + + /* PRIVATE */ + /** + * Used to hold the number of page links created. + * @instance + * @type {number} + * @private + */ + this._createdLinks = 0; + }, + + /* PROTECTED */ + /** + * Checks the supplied data and options for the paging component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.Paging#"preinit.ft.paging" + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.paging event is raised before the UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Paging#"preinit.ft.paging" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + this.ft.raise('preinit.ft.paging', [data]).then(function(){ + if (self.ft.$el.hasClass('footable-paging')) + self.enabled = true; + self.enabled = F.is.boolean(data.paging) + ? data.paging + : self.enabled; + + if (!self.enabled) return; + + self.size = F.is.number(data.pagingSize) + ? data.pagingSize + : self.size; + + self.current = F.is.number(data.pagingCurrent) + ? data.pagingCurrent + : self.current; + + self.limit = F.is.number(data.pagingLimit) + ? data.pagingLimit + : self.limit; + + if (self.ft.$el.hasClass('footable-paging-left')) + self.position = 'left'; + if (self.ft.$el.hasClass('footable-paging-center')) + self.position = 'center'; + if (self.ft.$el.hasClass('footable-paging-right')) + self.position = 'right'; + + self.position = F.is.string(data.pagingPosition) + ? data.pagingPosition + : self.position; + + self.countFormat = F.is.string(data.pagingCountFormat) + ? data.pagingCountFormat + : self.countFormat; + + self.total = Math.ceil(self.ft.rows.all.length / self.size); + }, function(){ + self.enabled = false; + }); + }, + /** + * Initializes the paging component for the plugin using the supplied table and options. + * @instance + * @protected + * @fires FooTable.Paging#"init.ft.paging" + */ + init: function(){ + /** + * The init.ft.paging event is raised before its UI is generated. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Paging#"init.ft.paging" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('init.ft.paging').then(function(){ + self.$create(); + }, function(){ + self.enabled = false; + }); + }, + /** + * Destroys the paging component removing any UI generated from the table. + * @instance + * @protected + * @fires FooTable.Paging#"destroy.ft.paging" + */ + destroy: function () { + /** + * The destroy.ft.paging event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Paging#"destroy.ft.paging" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('destroy.ft.paging').then(function(){ + self.ft.$el.removeClass('footable-paging') + .find('tfoot > tr.footable-paging').remove(); + self.detached = true; + self._createdLinks = 0; + }); + }, + /** + * Performs the actual paging against the {@link FooTable.Rows#current} array removing all rows that are not on the current visible page. + * @instance + * @protected + */ + predraw: function(){ + this.total = Math.ceil(this.ft.rows.array.length / this.size); + this.current = this.current > this.total ? this.total : (this.current < 1 ? 1 : this.current); + this.totalRows = this.ft.rows.array.length; + if (this.totalRows > this.size){ + this.ft.rows.array = this.ft.rows.array.splice((this.current - 1) * this.size, this.size); + } + this.formattedCount = this.format(this.countFormat); + }, + /** + * Updates the paging UI setting the state of the pagination control. + * @instance + * @protected + */ + draw: function(){ + if (this.total <= 1){ + if (!this.detached){ + this.$row.detach(); + this.detached = true; + } + } else { + if (this.detached){ + var $tfoot = this.ft.$el.children('tfoot'); + if ($tfoot.length == 0){ + $tfoot = $('
    '); + this.ft.$el.append($tfoot); + } + this.$row.appendTo($tfoot); + this.detached = false; + } + this.$cell.attr('colspan', this.ft.columns.visibleColspan); + this._createLinks(); + this._setVisible(this.current, this.current > this.previous); + this._setNavigation(true); + this.$count.text(this.formattedCount); + } + }, + /** + * Creates the paging UI from the current options setting the various jQuery properties of this component. + * @instance + * @protected + */ + $create: function(){ + this._createdLinks = 0; + var position = 'footable-paging-center'; + switch (this.position){ + case 'left': position = 'footable-paging-left'; break; + case 'right': position = 'footable-paging-right'; break; + } + this.ft.$el.addClass('footable-paging').addClass(position); + this.$cell = $(''); + this.ft.$el.append($tfoot); + } + this.$row = $('', { 'class': 'footable-paging' }).append(this.$cell).appendTo($tfoot); + this.$pagination = $('"),this.ft.$el.append(b)),this.$row.appendTo(b),this.detached=!1}this.$cell.attr("colspan",this.ft.columns.visibleColspan),this._createLinks(),this._setVisible(this.current,this.current>this.previous),this._setNavigation(!0),this.$count.text(this.formattedCount)}},$create:function(){this._createdLinks=0;var b="footable-paging-center";switch(this.position){case"left":b="footable-paging-left";break;case"right":b="footable-paging-right"}this.ft.$el.addClass("footable-paging").addClass(b),this.$cell=a(""),this.ft.$el.append(c)),this.$row=a("",{"class":"footable-paging"}).append(this.$cell).appendTo(c),this.$pagination=a(" + * "sorter": function(a, b){ + * if (a is less than b by some ordering criterion) { + * return -1; + * } + * if (a is greater than b by the ordering criterion) { + * return 1; + * } + * // a must be equal to b + * return 0; + * } + */ + F.Column.prototype.sorter = function(a, b){ + if (typeof a === 'string') a = a.toLowerCase(); + if (typeof b === 'string') b = b.toLowerCase(); + if (a === b) return 0; + if (a < b) return -1; + return 1; + }; + + /** + * This is supplied either the cell value or jQuery object to parse. A value must be returned from this method and will be used during sorting operations. + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {*} + * @this FooTable.Column + */ + F.Column.prototype.sortValue = function(valueOrElement){ + // if we have an element or a jQuery object use jQuery to get the value + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('sortValue'); + return F.is.defined(data) ? data : this.parser(valueOrElement); + } + // if options are supplied with the value + if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){ + if (F.is.string(valueOrElement.options.sortValue)) return valueOrElement.options.sortValue; + if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value; + } + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement; + return null; + }; + + // this is used to define the sorting specific properties on column creation + F.Column.prototype.__sorting_define__ = function(definition){ + this.sorter = F.checkFnValue(this, definition.sorter, this.sorter); + this.direction = F.is.type(definition.direction, 'string') ? F.Sorting.dir(definition.direction) : null; + this.sortable = F.is.boolean(definition.sortable) ? definition.sortable : true; + this.sorted = F.is.boolean(definition.sorted) ? definition.sorted : false; + this.sortValue = F.checkFnValue(this, definition.sortValue, this.sortValue); + }; + + // overrides the public define method and replaces it with our own + F.Column.extend('define', function(definition){ + this._super(definition); + this.__sorting_define__(definition); + }); + +})(jQuery, FooTable); +(function(F){ + /** + * An object containing the sorting options for the plugin. Added by the {@link FooTable.Sorting} component. + * @type {object} + * @prop {boolean} enabled=false - Whether or not to allow sorting on the table. + */ + F.Defaults.prototype.sorting = { + enabled: false + }; +})(FooTable); +(function($, F){ + + F.HTMLColumn.extend('__sorting_define__', function(definition){ + this._super(definition); + this.sortUse = F.is.string(definition.sortUse) && $.inArray(definition.sortUse, ['html','text']) !== -1 ? definition.sortUse : 'html'; + }); + + /** + * This is supplied either the cell value or jQuery object to parse. A value must be returned from this method and will be used during sorting operations. + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {*} + * @this FooTable.HTMLColumn + */ + F.HTMLColumn.prototype.sortValue = function(valueOrElement){ + // if we have an element or a jQuery object use jQuery to get the data value or pass it off to the parser + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('sortValue'); + return F.is.defined(data) ? data : $.trim($(valueOrElement)[this.sortUse]()); + } + // if options are supplied with the value + if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){ + if (F.is.string(valueOrElement.options.sortValue)) return valueOrElement.options.sortValue; + if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value; + } + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement; + return null; + }; + +})(jQuery, FooTable); +(function(F){ + /** + * Sort the table using the specified column and direction. Added by the {@link FooTable.Sorting} component. + * @instance + * @param {(string|number|FooTable.Column)} column - The column name, index or the actual {@link FooTable.Column} object to sort by. + * @param {string} [direction="ASC"] - The direction to sort by, either ASC or DESC. + * @returns {jQuery.Promise} + * @fires FooTable.Sorting#"change.ft.sorting" + * @fires FooTable.Sorting#"changed.ft.sorting" + * @see FooTable.Sorting#sort + */ + F.Table.prototype.sort = function(column, direction){ + return this.use(F.Sorting).sort(column, direction); + }; +})(FooTable); \ No newline at end of file diff --git a/media/footable-v3/js/footable.sorting.min.js b/media/footable-v3/js/footable.sorting.min.js new file mode 100644 index 0000000..c73f118 --- /dev/null +++ b/media/footable-v3/js/footable.sorting.min.js @@ -0,0 +1,8 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +!function(a,b){b.Sorter=b.Class.extend({construct:function(a,b){this.column=a,this.direction=b}})}(jQuery,FooTable),function(a,b){b.Sorting=b.Component.extend({construct:function(a){this._super(a,a.o.sorting.enabled),this.o=a.o.sorting,this.column=null,this.allowed=!0,this.initial=null},preinit:function(a){var c=this;this.ft.raise("preinit.ft.sorting",[a]).then(function(){c.ft.$el.hasClass("footable-sorting")&&(c.enabled=!0),c.enabled=b.is["boolean"](a.sorting)?a.sorting:c.enabled,c.enabled&&(c.column=b.arr.first(c.ft.columns.array,function(a){return a.sorted}))},function(){c.enabled=!1})},init:function(){var c=this;this.ft.raise("init.ft.sorting").then(function(){if(!c.initial){var d=!!c.column;c.initial={isset:d,rows:c.ft.rows.all.slice(0),column:d?c.column.name:null,direction:d?c.column.direction:null}}b.arr.each(c.ft.columns.array,function(b){b.sortable&&b.$el.addClass("footable-sortable").append(a("",{"class":"fooicon fooicon-sort"}))}),c.ft.$el.on("click.footable",".footable-sortable",{self:c},c._onSortClicked)},function(){c.enabled=!1})},destroy:function(){var a=this;this.ft.raise("destroy.ft.paging").then(function(){a.ft.$el.off("click.footable",".footable-sortable",a._onSortClicked),a.ft.$el.children("thead").children("tr.footable-header").children(".footable-sortable").removeClass("footable-sortable footable-asc footable-desc").find("span.fooicon").remove()})},predraw:function(){if(this.column){var a=this,b=a.column;a.ft.rows.array.sort(function(a,c){return"DESC"==b.direction?b.sorter(c.cells[b.index].sortValue,a.cells[b.index].sortValue):b.sorter(a.cells[b.index].sortValue,c.cells[b.index].sortValue)})}},draw:function(){if(this.column){var a=this,b=a.ft.$el.find("thead > tr > .footable-sortable"),c=a.column.$el;b.removeClass("footable-asc footable-desc").children(".fooicon").removeClass("fooicon-sort fooicon-sort-asc fooicon-sort-desc"),b.not(c).children(".fooicon").addClass("fooicon-sort"),c.addClass("DESC"==a.column.direction?"footable-desc":"footable-asc").children(".fooicon").addClass("DESC"==a.column.direction?"fooicon-sort-desc":"fooicon-sort-asc")}},sort:function(a,b){return this._sort(a,b)},toggleAllowed:function(a){a=b.is["boolean"](a)?a:!this.allowed,this.allowed=a,this.ft.$el.toggleClass("footable-sorting-disabled",!this.allowed)},hasChanged:function(){return!(!this.initial||!this.column||this.column.name===this.initial.column&&(this.column.direction===this.initial.direction||null===this.initial.direction&&"ASC"===this.column.direction))},reset:function(){this.initial&&(this.initial.isset?this.sort(this.initial.column,this.initial.direction):(this.column&&(this.column.$el.removeClass("footable-asc footable-desc"),this.column=null),this.ft.rows.all=this.initial.rows,this.ft.draw()))},_sort:function(c,d){if(!this.allowed)return a.Deferred().reject("sorting disabled");var e=this,f=new b.Sorter(e.ft.columns.get(c),b.Sorting.dir(d));return e.ft.raise("before.ft.sorting",[f]).then(function(){return b.arr.each(e.ft.columns.array,function(a){a!=e.column&&(a.direction=null)}),e.column=e.ft.columns.get(f.column),e.column&&(e.column.direction=b.Sorting.dir(f.direction)),e.ft.draw().then(function(){e.ft.raise("after.ft.sorting",[f])})})},_onSortClicked:function(b){var c=b.data.self,d=a(this).closest("th,td"),e=d.is(".footable-asc, .footable-desc")?d.hasClass("footable-desc")?"ASC":"DESC":"ASC";c._sort(d.index(),e)}}),b.Sorting.dir=function(a){return!b.is.string(a)||"ASC"!=a&&"DESC"!=a?"ASC":a},b.components.register("sorting",b.Sorting,600)}(jQuery,FooTable),function(a){a.Cell.prototype.sortValue=null,a.Cell.prototype.__sorting_define__=function(a){this.sortValue=this.column.sortValue.call(this.column,a)},a.Cell.prototype.__sorting_val__=function(b){a.is.defined(b)&&(this.sortValue=this.column.sortValue.call(this.column,b))},a.Cell.extend("define",function(a){this._super(a),this.__sorting_define__(a)}),a.Cell.extend("val",function(a){var b=this._super(a);return this.__sorting_val__(a),b})}(FooTable),function(a,b){b.Column.prototype.direction=null,b.Column.prototype.sortable=!0,b.Column.prototype.sorted=!1,b.Column.prototype.sorter=function(a,b){return"string"==typeof a&&(a=a.toLowerCase()),"string"==typeof b&&(b=b.toLowerCase()),a===b?0:b>a?-1:1},b.Column.prototype.sortValue=function(c){if(b.is.element(c)||b.is.jq(c)){var d=a(c).data("sortValue");return b.is.defined(d)?d:this.parser(c)}if(b.is.hash(c)&&b.is.hash(c.options)){if(b.is.string(c.options.sortValue))return c.options.sortValue;b.is.defined(c.value)&&(c=c.value)}return b.is.defined(c)&&null!=c?c:null},b.Column.prototype.__sorting_define__=function(a){this.sorter=b.checkFnValue(this,a.sorter,this.sorter),this.direction=b.is.type(a.direction,"string")?b.Sorting.dir(a.direction):null,this.sortable=b.is["boolean"](a.sortable)?a.sortable:!0,this.sorted=b.is["boolean"](a.sorted)?a.sorted:!1,this.sortValue=b.checkFnValue(this,a.sortValue,this.sortValue)},b.Column.extend("define",function(a){this._super(a),this.__sorting_define__(a)})}(jQuery,FooTable),function(a){a.Defaults.prototype.sorting={enabled:!1}}(FooTable),function(a,b){b.HTMLColumn.extend("__sorting_define__",function(c){this._super(c),this.sortUse=b.is.string(c.sortUse)&&-1!==a.inArray(c.sortUse,["html","text"])?c.sortUse:"html"}),b.HTMLColumn.prototype.sortValue=function(c){if(b.is.element(c)||b.is.jq(c)){var d=a(c).data("sortValue");return b.is.defined(d)?d:a.trim(a(c)[this.sortUse]())}if(b.is.hash(c)&&b.is.hash(c.options)){if(b.is.string(c.options.sortValue))return c.options.sortValue;b.is.defined(c.value)&&(c=c.value)}return b.is.defined(c)&&null!=c?c:null}}(jQuery,FooTable),function(a){a.Table.prototype.sort=function(b,c){return this.use(a.Sorting).sort(b,c)}}(FooTable); \ No newline at end of file diff --git a/media/footable-v3/js/footable.state.js b/media/footable-v3/js/footable.state.js new file mode 100644 index 0000000..20584ea --- /dev/null +++ b/media/footable-v3/js/footable.state.js @@ -0,0 +1,362 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +(function($, F){ + + // global int to use if the table has no ID + var _uid = 0, + // a hash value for the current url + _url_hash = (function(str){ + var i, l, hval = 0x811c9dc5; + for (i = 0, l = str.length; i < l; i++) { + hval ^= str.charCodeAt(i); + hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); + } + return hval >>> 0; + })(location.origin + location.pathname); + + F.State = F.Component.extend(/** @lends FooTable.State */{ + /** + * The state component adds the ability for the table to remember its basic state for filtering, paging and sorting. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component. + * @returns {FooTable.State} + */ + construct: function(table){ + // call the constructor of the base class + this._super(table, table.o.state.enabled); + // Change this value if an update to this component requires any stored data to be reset + this._key = '1'; + /** + * The key to use to store the state for this table. + * @type {(null|string)} + */ + this.key = this._key + (F.is.string(table.o.state.key) ? table.o.state.key : this._uid()); + /** + * Whether or not to allow the filtering component to store it's state. + * @type {boolean} + */ + this.filtering = F.is.boolean(table.o.state.filtering) ? table.o.state.filtering : true; + /** + * Whether or not to allow the paging component to store it's state. + * @type {boolean} + */ + this.paging = F.is.boolean(table.o.state.paging) ? table.o.state.paging : true; + /** + * Whether or not to allow the sorting component to store it's state. + * @type {boolean} + */ + this.sorting = F.is.boolean(table.o.state.sorting) ? table.o.state.sorting : true; + }, + /* PROTECTED */ + /** + * Checks the supplied data and options for the state component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.State#"preinit.ft.state" + * @this FooTable.State + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.state event is raised before the UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the component. + * @event FooTable.State#"preinit.ft.state" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + this.ft.raise('preinit.ft.state', [data]).then(function(){ + + self.enabled = F.is.boolean(data.state) + ? data.state + : self.enabled; + + if (!self.enabled) return; + + self.key = self._key + (F.is.string(data.stateKey) ? data.stateKey : self.key); + + self.filtering = F.is.boolean(data.stateFiltering) ? data.stateFiltering : self.filtering; + + self.paging = F.is.boolean(data.statePaging) ? data.statePaging : self.paging; + + self.sorting = F.is.boolean(data.stateSorting) ? data.stateSorting : self.sorting; + + }, function(){ + self.enabled = false; + }); + }, + /** + * Gets the state value for the specified key for this table. + * @instance + * @param {string} key - The key to get the value for. + * @returns {(*|null)} + */ + get: function(key){ + return JSON.parse(localStorage.getItem(this.key + ':' + key)); + }, + /** + * Sets the state value for the specified key for this table. + * @instance + * @param {string} key - The key to set the value for. + * @param {*} data - The value to store for the key. This value must be JSON.stringify friendly. + */ + set: function(key, data){ + localStorage.setItem(this.key + ':' + key, JSON.stringify(data)); + }, + /** + * Clears the state value for the specified key for this table. + * @instance + * @param {string} key - The key to clear the value for. + */ + remove: function(key){ + localStorage.removeItem(this.key + ':' + key); + }, + /** + * Executes the {@link FooTable.Component#readState} function on all components. + * @instance + */ + read: function(){ + this.ft.execute(false, true, 'readState'); + }, + /** + * Executes the {@link FooTable.Component#writeState} function on all components. + * @instance + */ + write: function(){ + this.ft.execute(false, true, 'writeState'); + }, + /** + * Executes the {@link FooTable.Component#clearState} function on all components. + * @instance + */ + clear: function(){ + this.ft.execute(false, true, 'clearState'); + }, + /** + * Generates a unique identifier for the current {@link FooTable.Table} if one is not supplied through the options. + * This value is a combination of the url hash and either the element ID or an incremented global int value. + * @instance + * @returns {*} + * @private + */ + _uid: function(){ + var id = this.ft.$el.attr('id'); + return _url_hash + '_' + (F.is.string(id) ? id : ++_uid); + } + }); + + F.components.register('state', F.State, 700); + +})(jQuery, FooTable); +(function(F){ + + /** + * This method is called from the {@link FooTable.State#read} method and allows a component to retrieve its' stored state. + * @instance + * @protected + * @function + */ + F.Component.prototype.readState = function(){}; + + /** + * This method is called from the {@link FooTable.State#write} method and allows a component to write its' current state to the store. + * @instance + * @protected + * @function + */ + F.Component.prototype.writeState = function(){}; + + /** + * This method is called from the {@link FooTable.State#clear} method and allows a component to clear any stored state. + * @instance + * @protected + * @function + */ + F.Component.prototype.clearState = function(){}; + +})(FooTable); +(function(F){ + + /** + * An object containing the state options for the plugin. Added by the {@link FooTable.State} component. + * @type {object} + * @prop {boolean} enabled=false - Whether or not to allow state to be stored for the table. This overrides the individual component enable options. + * @prop {boolean} filtering=true - Whether or not to allow the filtering state to be stored. + * @prop {boolean} paging=true - Whether or not to allow the filtering state to be stored. + * @prop {boolean} sorting=true - Whether or not to allow the filtering state to be stored. + * @prop {string} key=null - The unique key to use to store the table's data. + */ + F.Defaults.prototype.state = { + enabled: false, + filtering: true, + paging: true, + sorting: true, + key: null + }; + +})(FooTable); +(function(F){ + + if (!F.Filtering) return; + + /** + * Allows the filtering component to retrieve its' stored state. + */ + F.Filtering.prototype.readState = function(){ + if (this.ft.state.filtering){ + var state = this.ft.state.get('filtering'); + if (F.is.hash(state) && !F.is.emptyArray(state.filters)){ + this.filters = this.ensure(state.filters); + } + } + }; + + /** + * Allows the filtering component to write its' current state to the store. + */ + F.Filtering.prototype.writeState = function(){ + if (this.ft.state.filtering) { + var filters = F.arr.map(this.filters, function (f) { + return { + name: f.name, + query: f.query instanceof F.Query ? f.query.val() : f.query, + columns: F.arr.map(f.columns, function (c) { + return c.name; + }), + hidden: f.hidden, + space: f.space, + connectors: f.connectors, + ignoreCase: f.ignoreCase + }; + }); + this.ft.state.set('filtering', {filters: filters}); + } + }; + + /** + * Allows the filtering component to clear any stored state. + */ + F.Filtering.prototype.clearState = function(){ + if (this.ft.state.filtering) { + this.ft.state.remove('filtering'); + } + }; + +})(FooTable); +(function(F){ + + if (!F.Paging) return; + + /** + * Allows the paging component to retrieve its' stored state. + */ + F.Paging.prototype.readState = function(){ + if (this.ft.state.paging) { + var state = this.ft.state.get('paging'); + if (F.is.hash(state)) { + this.current = state.current; + this.size = state.size; + } + } + }; + + /** + * Allows the paging component to write its' current state to the store. + */ + F.Paging.prototype.writeState = function(){ + if (this.ft.state.paging) { + this.ft.state.set('paging', { + current: this.current, + size: this.size + }); + } + }; + + /** + * Allows the paging component to clear any stored state. + */ + F.Paging.prototype.clearState = function(){ + if (this.ft.state.paging) { + this.ft.state.remove('paging'); + } + }; + +})(FooTable); +(function(F){ + + if (!F.Sorting) return; + + /** + * Allows the sorting component to retrieve its' stored state. + */ + F.Sorting.prototype.readState = function(){ + if (this.ft.state.sorting) { + var state = this.ft.state.get('sorting'); + if (F.is.hash(state)) { + var column = this.ft.columns.get(state.column); + if (column instanceof F.Column) { + this.column = column; + this.column.direction = state.direction; + } + } + } + }; + + /** + * Allows the sorting component to write its' current state to the store. + */ + F.Sorting.prototype.writeState = function(){ + if (this.ft.state.sorting && this.column instanceof F.Column){ + this.ft.state.set('sorting', { + column: this.column.name, + direction: this.column.direction + }); + } + }; + + /** + * Allows the sorting component to clear any stored state. + */ + F.Sorting.prototype.clearState = function(){ + if (this.ft.state.sorting) { + this.ft.state.remove('sorting'); + } + }; + +})(FooTable); +(function(F){ + + // hook into the _construct method so we can add the state property to the table. + F.Table.extend('_construct', function(ready){ + this.state = this.use(FooTable.State); + this._super(ready); + }); + + // hook into the _preinit method so we can trigger a plugin wide read state operation. + F.Table.extend('_preinit', function(){ + var self = this; + return self._super().then(function(){ + if (self.state.enabled){ + self.state.read(); + } + }); + }); + + // hook into the draw method so we can trigger a plugin wide write state operation. + F.Table.extend('draw', function(){ + var self = this; + return self._super().then(function(){ + if (self.state.enabled){ + self.state.write(); + } + }); + }); + +})(FooTable); \ No newline at end of file diff --git a/media/footable-v3/js/footable.state.min.js b/media/footable-v3/js/footable.state.min.js new file mode 100644 index 0000000..d542d96 --- /dev/null +++ b/media/footable-v3/js/footable.state.min.js @@ -0,0 +1,8 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.4 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +!function(a,b){var c=0,d=function(a){var b,c,d=2166136261;for(b=0,c=a.length;c>b;b++)d^=a.charCodeAt(b),d+=(d<<1)+(d<<4)+(d<<7)+(d<<8)+(d<<24);return d>>>0}(location.origin+location.pathname);b.State=b.Component.extend({construct:function(a){this._super(a,a.o.state.enabled),this._key="1",this.key=this._key+(b.is.string(a.o.state.key)?a.o.state.key:this._uid()),this.filtering=b.is["boolean"](a.o.state.filtering)?a.o.state.filtering:!0,this.paging=b.is["boolean"](a.o.state.paging)?a.o.state.paging:!0,this.sorting=b.is["boolean"](a.o.state.sorting)?a.o.state.sorting:!0},preinit:function(a){var c=this;this.ft.raise("preinit.ft.state",[a]).then(function(){c.enabled=b.is["boolean"](a.state)?a.state:c.enabled,c.enabled&&(c.key=c._key+(b.is.string(a.stateKey)?a.stateKey:c.key),c.filtering=b.is["boolean"](a.stateFiltering)?a.stateFiltering:c.filtering,c.paging=b.is["boolean"](a.statePaging)?a.statePaging:c.paging,c.sorting=b.is["boolean"](a.stateSorting)?a.stateSorting:c.sorting)},function(){c.enabled=!1})},get:function(a){return JSON.parse(localStorage.getItem(this.key+":"+a))},set:function(a,b){localStorage.setItem(this.key+":"+a,JSON.stringify(b))},remove:function(a){localStorage.removeItem(this.key+":"+a)},read:function(){this.ft.execute(!1,!0,"readState")},write:function(){this.ft.execute(!1,!0,"writeState")},clear:function(){this.ft.execute(!1,!0,"clearState")},_uid:function(){var a=this.ft.$el.attr("id");return d+"_"+(b.is.string(a)?a:++c)}}),b.components.register("state",b.State,700)}(jQuery,FooTable),function(a){a.Component.prototype.readState=function(){},a.Component.prototype.writeState=function(){},a.Component.prototype.clearState=function(){}}(FooTable),function(a){a.Defaults.prototype.state={enabled:!1,filtering:!0,paging:!0,sorting:!0,key:null}}(FooTable),function(a){a.Filtering&&(a.Filtering.prototype.readState=function(){if(this.ft.state.filtering){var b=this.ft.state.get("filtering");a.is.hash(b)&&!a.is.emptyArray(b.filters)&&(this.filters=this.ensure(b.filters))}},a.Filtering.prototype.writeState=function(){if(this.ft.state.filtering){var b=a.arr.map(this.filters,function(b){return{name:b.name,query:b.query instanceof a.Query?b.query.val():b.query,columns:a.arr.map(b.columns,function(a){return a.name}),hidden:b.hidden,space:b.space,connectors:b.connectors,ignoreCase:b.ignoreCase}});this.ft.state.set("filtering",{filters:b})}},a.Filtering.prototype.clearState=function(){this.ft.state.filtering&&this.ft.state.remove("filtering")})}(FooTable),function(a){a.Paging&&(a.Paging.prototype.readState=function(){if(this.ft.state.paging){var b=this.ft.state.get("paging");a.is.hash(b)&&(this.current=b.current,this.size=b.size)}},a.Paging.prototype.writeState=function(){this.ft.state.paging&&this.ft.state.set("paging",{current:this.current,size:this.size})},a.Paging.prototype.clearState=function(){this.ft.state.paging&&this.ft.state.remove("paging")})}(FooTable),function(a){a.Sorting&&(a.Sorting.prototype.readState=function(){if(this.ft.state.sorting){var b=this.ft.state.get("sorting");if(a.is.hash(b)){var c=this.ft.columns.get(b.column);c instanceof a.Column&&(this.column=c,this.column.direction=b.direction)}}},a.Sorting.prototype.writeState=function(){this.ft.state.sorting&&this.column instanceof a.Column&&this.ft.state.set("sorting",{column:this.column.name,direction:this.column.direction})},a.Sorting.prototype.clearState=function(){this.ft.state.sorting&&this.ft.state.remove("sorting")})}(FooTable),function(a){a.Table.extend("_construct",function(a){this.state=this.use(FooTable.State),this._super(a)}),a.Table.extend("_preinit",function(){var a=this;return a._super().then(function(){a.state.enabled&&a.state.read()})}),a.Table.extend("draw",function(){var a=this;return a._super().then(function(){a.state.enabled&&a.state.write()})})}(FooTable); \ No newline at end of file diff --git a/media/footable-v3/js/index.html b/media/footable-v3/js/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/footable-v3/js/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/images/index.html b/media/images/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/images/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/images/spinner.svg b/media/images/spinner.svg new file mode 100644 index 0000000..e88002a --- /dev/null +++ b/media/images/spinner.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/index.html b/media/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/js/download.js b/media/js/download.js new file mode 100644 index 0000000..6aed031 --- /dev/null +++ b/media/js/download.js @@ -0,0 +1,20 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage download.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + + diff --git a/media/js/extension.js b/media/js/extension.js new file mode 100644 index 0000000..0b01ce1 --- /dev/null +++ b/media/js/extension.js @@ -0,0 +1,113 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage extension.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// Initial Script +jQuery(document).ready(function() +{ + var type_vvvvvwb = jQuery("#jform_type").val(); + vvvvvwb(type_vvvvvwb); + + var type_vvvvvwc = jQuery("#jform_type").val(); + vvvvvwc(type_vvvvvwc); +}); + +// the vvvvvwb function +function vvvvvwb(type_vvvvvwb) +{ + if (isSet(type_vvvvvwb) && type_vvvvvwb.constructor !== Array) + { + var temp_vvvvvwb = type_vvvvvwb; + var type_vvvvvwb = []; + type_vvvvvwb.push(temp_vvvvvwb); + } + else if (!isSet(type_vvvvvwb)) + { + var type_vvvvvwb = []; + } + var type = type_vvvvvwb.some(type_vvvvvwb_SomeFunc); + + + // set this function logic + if (type) + { + jQuery('#jform_group').closest('.control-group').show(); + } + else + { + jQuery('#jform_group').closest('.control-group').hide(); + } +} + +// the vvvvvwb Some function +function type_vvvvvwb_SomeFunc(type_vvvvvwb) +{ + // set the function logic + if (type_vvvvvwb == 'plugin') + { + return true; + } + return false; +} + +// the vvvvvwc function +function vvvvvwc(type_vvvvvwc) +{ + if (isSet(type_vvvvvwc) && type_vvvvvwc.constructor !== Array) + { + var temp_vvvvvwc = type_vvvvvwc; + var type_vvvvvwc = []; + type_vvvvvwc.push(temp_vvvvvwc); + } + else if (!isSet(type_vvvvvwc)) + { + var type_vvvvvwc = []; + } + var type = type_vvvvvwc.some(type_vvvvvwc_SomeFunc); + + + // set this function logic + if (type) + { + jQuery('#jform_client').closest('.control-group').show(); + } + else + { + jQuery('#jform_client').closest('.control-group').hide(); + } +} + +// the vvvvvwc Some function +function type_vvvvvwc_SomeFunc(type_vvvvvwc) +{ + // set the function logic + if (type_vvvvvwc == 'module' || type_vvvvvwc == 'template' || type_vvvvvwc == 'language') + { + return true; + } + return false; +} + +// the isSet function +function isSet(val) +{ + if ((val != undefined) && (val != null) && 0 !== val.length){ + return true; + } + return false; +} diff --git a/media/js/index.html b/media/js/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/js/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/js/key.js b/media/js/key.js new file mode 100644 index 0000000..3ada12b --- /dev/null +++ b/media/js/key.js @@ -0,0 +1,20 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage key.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + + diff --git a/media/js/package.js b/media/js/package.js new file mode 100644 index 0000000..da0eaf7 --- /dev/null +++ b/media/js/package.js @@ -0,0 +1,133 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage package.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// Initial Script +jQuery(document).ready(function() +{ + var add_php_method_uninstall_vvvvvvv = jQuery("#jform_add_php_method_uninstall input[type='radio']:checked").val(); + vvvvvvv(add_php_method_uninstall_vvvvvvv); + + var add_php_postflight_install_vvvvvvw = jQuery("#jform_add_php_postflight_install input[type='radio']:checked").val(); + vvvvvvw(add_php_postflight_install_vvvvvvw); + + var add_php_postflight_update_vvvvvvx = jQuery("#jform_add_php_postflight_update input[type='radio']:checked").val(); + vvvvvvx(add_php_postflight_update_vvvvvvx); + + var add_php_preflight_install_vvvvvvy = jQuery("#jform_add_php_preflight_install input[type='radio']:checked").val(); + vvvvvvy(add_php_preflight_install_vvvvvvy); + + var add_php_preflight_uninstall_vvvvvvz = jQuery("#jform_add_php_preflight_uninstall input[type='radio']:checked").val(); + vvvvvvz(add_php_preflight_uninstall_vvvvvvz); + + var add_php_preflight_update_vvvvvwa = jQuery("#jform_add_php_preflight_update input[type='radio']:checked").val(); + vvvvvwa(add_php_preflight_update_vvvvvwa); +}); + +// the vvvvvvv function +function vvvvvvv(add_php_method_uninstall_vvvvvvv) +{ + // set the function logic + if (add_php_method_uninstall_vvvvvvv == 1) + { + jQuery('#jform_php_method_uninstall-lbl').closest('.control-group').show(); + } + else + { + jQuery('#jform_php_method_uninstall-lbl').closest('.control-group').hide(); + } +} + +// the vvvvvvw function +function vvvvvvw(add_php_postflight_install_vvvvvvw) +{ + // set the function logic + if (add_php_postflight_install_vvvvvvw == 1) + { + jQuery('#jform_php_postflight_install-lbl').closest('.control-group').show(); + } + else + { + jQuery('#jform_php_postflight_install-lbl').closest('.control-group').hide(); + } +} + +// the vvvvvvx function +function vvvvvvx(add_php_postflight_update_vvvvvvx) +{ + // set the function logic + if (add_php_postflight_update_vvvvvvx == 1) + { + jQuery('#jform_php_postflight_update-lbl').closest('.control-group').show(); + } + else + { + jQuery('#jform_php_postflight_update-lbl').closest('.control-group').hide(); + } +} + +// the vvvvvvy function +function vvvvvvy(add_php_preflight_install_vvvvvvy) +{ + // set the function logic + if (add_php_preflight_install_vvvvvvy == 1) + { + jQuery('#jform_php_preflight_install-lbl').closest('.control-group').show(); + } + else + { + jQuery('#jform_php_preflight_install-lbl').closest('.control-group').hide(); + } +} + +// the vvvvvvz function +function vvvvvvz(add_php_preflight_uninstall_vvvvvvz) +{ + // set the function logic + if (add_php_preflight_uninstall_vvvvvvz == 1) + { + jQuery('#jform_php_preflight_uninstall-lbl').closest('.control-group').show(); + } + else + { + jQuery('#jform_php_preflight_uninstall-lbl').closest('.control-group').hide(); + } +} + +// the vvvvvwa function +function vvvvvwa(add_php_preflight_update_vvvvvwa) +{ + // set the function logic + if (add_php_preflight_update_vvvvvwa == 1) + { + jQuery('#jform_php_preflight_update-lbl').closest('.control-group').show(); + } + else + { + jQuery('#jform_php_preflight_update-lbl').closest('.control-group').hide(); + } +} + +// the isSet function +function isSet(val) +{ + if ((val != undefined) && (val != null) && 0 !== val.length){ + return true; + } + return false; +} diff --git a/media/js/release.js b/media/js/release.js new file mode 100644 index 0000000..0b7c7cb --- /dev/null +++ b/media/js/release.js @@ -0,0 +1,20 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage release.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + + diff --git a/media/js/subscription.js b/media/js/subscription.js new file mode 100644 index 0000000..3259dce --- /dev/null +++ b/media/js/subscription.js @@ -0,0 +1,20 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage subscription.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + + diff --git a/media/uikit-v3/css/index.html b/media/uikit-v3/css/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/uikit-v3/css/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/uikit-v3/css/uikit.min.css b/media/uikit-v3/css/uikit.min.css new file mode 100644 index 0000000..4b0a87c --- /dev/null +++ b/media/uikit-v3/css/uikit.min.css @@ -0,0 +1 @@ +/*! UIkit 3.6.22 | https://www.getuikit.com | (c) 2014 - 2021 YOOtheme | MIT License */html{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:16px;font-weight:400;line-height:1.5;-webkit-text-size-adjust:100%;background:#fff;color:#666}body{margin:0}a:active,a:hover{outline:0}.uk-link,a{color:#1e87f0;text-decoration:none;cursor:pointer}.uk-link-toggle:focus .uk-link,.uk-link-toggle:hover .uk-link,.uk-link:hover,a:hover{color:#0f6ecd;text-decoration:underline}abbr[title]{text-decoration:underline dotted;-webkit-text-decoration-style:dotted}b,strong{font-weight:bolder}:not(pre)>code,:not(pre)>kbd,:not(pre)>samp{font-family:Consolas,monaco,monospace;font-size:.875rem;color:#f0506e;white-space:nowrap;padding:2px 6px;background:#f8f8f8}em{color:#f0506e}ins{background:#ffd;color:#666;text-decoration:none}mark{background:#ffd;color:#666}q{font-style:italic}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}audio,canvas,iframe,img,svg,video{vertical-align:middle}canvas,img,video{max-width:100%;height:auto;box-sizing:border-box}@supports (display:block){svg{max-width:100%;height:auto;box-sizing:border-box}}svg:not(:root){overflow:hidden}img:not([src]){min-width:1px;visibility:hidden}iframe{border:0}address,dl,fieldset,figure,ol,p,pre,ul{margin:0 0 20px 0}*+address,*+dl,*+fieldset,*+figure,*+ol,*+p,*+pre,*+ul{margin-top:20px}.uk-h1,.uk-h2,.uk-h3,.uk-h4,.uk-h5,.uk-h6,.uk-heading-2xlarge,.uk-heading-large,.uk-heading-medium,.uk-heading-small,.uk-heading-xlarge,h1,h2,h3,h4,h5,h6{margin:0 0 20px 0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-weight:400;color:#333;text-transform:none}*+.uk-h1,*+.uk-h2,*+.uk-h3,*+.uk-h4,*+.uk-h5,*+.uk-h6,*+.uk-heading-2xlarge,*+.uk-heading-large,*+.uk-heading-medium,*+.uk-heading-small,*+.uk-heading-xlarge,*+h1,*+h2,*+h3,*+h4,*+h5,*+h6{margin-top:40px}.uk-h1,h1{font-size:2.23125rem;line-height:1.2}.uk-h2,h2{font-size:1.7rem;line-height:1.3}.uk-h3,h3{font-size:1.5rem;line-height:1.4}.uk-h4,h4{font-size:1.25rem;line-height:1.4}.uk-h5,h5{font-size:16px;line-height:1.4}.uk-h6,h6{font-size:.875rem;line-height:1.4}@media (min-width:960px){.uk-h1,h1{font-size:2.625rem}.uk-h2,h2{font-size:2rem}}ol,ul{padding-left:30px}ol>li>ol,ol>li>ul,ul>li>ol,ul>li>ul{margin:0}dt{font-weight:700}dd{margin-left:0}.uk-hr,hr{overflow:visible;text-align:inherit;margin:0 0 20px 0;border:0;border-top:1px solid #e5e5e5}*+.uk-hr,*+hr{margin-top:20px}address{font-style:normal}blockquote{margin:0 0 20px 0;font-size:1.25rem;line-height:1.5;font-style:italic;color:#333}*+blockquote{margin-top:20px}blockquote p:last-of-type{margin-bottom:0}blockquote footer{margin-top:10px;font-size:.875rem;line-height:1.5;color:#666}blockquote footer::before{content:"— "}pre{font:0.875rem/1.5 Consolas,monaco,monospace;color:#666;-moz-tab-size:4;tab-size:4;overflow:auto;padding:10px;border:1px solid #e5e5e5;border-radius:3px;background:#fff}pre code{font-family:Consolas,monaco,monospace}::selection{background:#39f;color:#fff;text-shadow:none}details,main{display:block}summary{display:list-item}template{display:none}.uk-breakpoint-s::before{content:'640px'}.uk-breakpoint-m::before{content:'960px'}.uk-breakpoint-l::before{content:'1200px'}.uk-breakpoint-xl::before{content:'1600px'}:root{--uk-breakpoint-s:640px;--uk-breakpoint-m:960px;--uk-breakpoint-l:1200px;--uk-breakpoint-xl:1600px}.uk-link-muted a,a.uk-link-muted{color:#999}.uk-link-muted a:hover,.uk-link-toggle:focus .uk-link-muted,.uk-link-toggle:hover .uk-link-muted,a.uk-link-muted:hover{color:#666}.uk-link-text a,a.uk-link-text{color:inherit}.uk-link-text a:hover,.uk-link-toggle:focus .uk-link-text,.uk-link-toggle:hover .uk-link-text,a.uk-link-text:hover{color:#999}.uk-link-heading a,a.uk-link-heading{color:inherit}.uk-link-heading a:hover,.uk-link-toggle:focus .uk-link-heading,.uk-link-toggle:hover .uk-link-heading,a.uk-link-heading:hover{color:#1e87f0;text-decoration:none}.uk-link-reset a,a.uk-link-reset{color:inherit!important;text-decoration:none!important}.uk-link-toggle{color:inherit!important;text-decoration:none!important}.uk-link-toggle:focus{outline:0}.uk-heading-small{font-size:2.6rem;line-height:1.2}.uk-heading-medium{font-size:2.8875rem;line-height:1.1}.uk-heading-large{font-size:3.4rem;line-height:1.1}.uk-heading-xlarge{font-size:4rem;line-height:1}.uk-heading-2xlarge{font-size:6rem;line-height:1}@media (min-width:960px){.uk-heading-small{font-size:3.25rem}.uk-heading-medium{font-size:3.5rem}.uk-heading-large{font-size:4rem}.uk-heading-xlarge{font-size:6rem}.uk-heading-2xlarge{font-size:8rem}}@media (min-width:1200px){.uk-heading-medium{font-size:4rem}.uk-heading-large{font-size:6rem}.uk-heading-xlarge{font-size:8rem}.uk-heading-2xlarge{font-size:11rem}}.uk-heading-divider{padding-bottom:calc(5px + .1em);border-bottom:calc(.2px + .05em) solid #e5e5e5}.uk-heading-bullet{position:relative}.uk-heading-bullet::before{content:"";display:inline-block;position:relative;top:calc(-.1 * 1em);vertical-align:middle;height:calc(4px + .7em);margin-right:calc(5px + .2em);border-left:calc(5px + .1em) solid #e5e5e5}.uk-heading-line{overflow:hidden}.uk-heading-line>*{display:inline-block;position:relative}.uk-heading-line>::after,.uk-heading-line>::before{content:"";position:absolute;top:calc(50% - ((.2px + .05em)/ 2));width:2000px;border-bottom:calc(.2px + .05em) solid #e5e5e5}.uk-heading-line>::before{right:100%;margin-right:calc(5px + .3em)}.uk-heading-line>::after{left:100%;margin-left:calc(5px + .3em)}[class*=uk-divider]{border:none;margin-bottom:20px}*+[class*=uk-divider]{margin-top:20px}.uk-divider-icon{position:relative;height:20px;background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22none%22%20stroke%3D%22%23e5e5e5%22%20stroke-width%3D%222%22%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%227%22%20%2F%3E%0A%3C%2Fsvg%3E%0A");background-repeat:no-repeat;background-position:50% 50%}.uk-divider-icon::after,.uk-divider-icon::before{content:"";position:absolute;top:50%;max-width:calc(50% - (50px / 2));border-bottom:1px solid #e5e5e5}.uk-divider-icon::before{right:calc(50% + (50px / 2));width:100%}.uk-divider-icon::after{left:calc(50% + (50px / 2));width:100%}.uk-divider-small{line-height:0}.uk-divider-small::after{content:"";display:inline-block;width:100px;max-width:100%;border-top:1px solid #e5e5e5;vertical-align:top}.uk-divider-vertical{width:1px;height:100px;margin-left:auto;margin-right:auto;border-left:1px solid #e5e5e5}.uk-list{padding:0;list-style:none}.uk-list>*>:last-child{margin-bottom:0}.uk-list>*>ul,.uk-list>:nth-child(n+2){margin-top:10px}.uk-list-circle>*,.uk-list-decimal>*,.uk-list-disc>*,.uk-list-hyphen>*,.uk-list-square>*{padding-left:30px}.uk-list-decimal{counter-reset:decimal}.uk-list-decimal>*{counter-increment:decimal}[class*=uk-list]>::before{content:'';position:relative;left:-30px;width:30px;height:1.5em;margin-bottom:-1.5em;display:list-item;list-style-position:inside;text-align:right}.uk-list-disc>::before{list-style-type:disc}.uk-list-circle>::before{list-style-type:circle}.uk-list-square>::before{list-style-type:square}.uk-list-decimal>::before{content:counter(decimal,decimal) '\200A.\00A0'}.uk-list-hyphen>::before{content:'–\00A0\00A0'}.uk-list-muted>::before{color:#999!important}.uk-list-emphasis>::before{color:#333!important}.uk-list-primary>::before{color:#1e87f0!important}.uk-list-secondary>::before{color:#222!important}.uk-list-bullet>*{padding-left:30px}.uk-list-bullet>::before{content:"";position:relative;left:-30px;width:30px;height:1.5em;margin-bottom:-1.5em;background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22%23666%22%20cx%3D%223%22%20cy%3D%223%22%20r%3D%223%22%20%2F%3E%0A%3C%2Fsvg%3E");background-repeat:no-repeat;background-position:50% 50%}.uk-list-divider>:nth-child(n+2){margin-top:10px;padding-top:10px;border-top:1px solid #e5e5e5}.uk-list-striped>*{padding:10px 10px}.uk-list-striped>:nth-of-type(odd){border-top:1px solid #e5e5e5;border-bottom:1px solid #e5e5e5}.uk-list-striped>:nth-of-type(odd){background:#f8f8f8}.uk-list-striped>:nth-child(n+2){margin-top:0}.uk-list-large>*>ul,.uk-list-large>:nth-child(n+2){margin-top:20px}.uk-list-collapse>*>ul,.uk-list-collapse>:nth-child(n+2){margin-top:0}.uk-list-large.uk-list-divider>:nth-child(n+2){margin-top:20px;padding-top:20px}.uk-list-collapse.uk-list-divider>:nth-child(n+2){margin-top:0;padding-top:0}.uk-list-large.uk-list-striped>*{padding:20px 10px}.uk-list-collapse.uk-list-striped>*{padding-top:0;padding-bottom:0}.uk-list-collapse.uk-list-striped>:nth-child(n+2),.uk-list-large.uk-list-striped>:nth-child(n+2){margin-top:0}.uk-description-list>dt{color:#333;font-size:.875rem;font-weight:400;text-transform:uppercase}.uk-description-list>dt:nth-child(n+2){margin-top:20px}.uk-description-list-divider>dt:nth-child(n+2){margin-top:20px;padding-top:20px;border-top:1px solid #e5e5e5}.uk-table{border-collapse:collapse;border-spacing:0;width:100%;margin-bottom:20px}*+.uk-table{margin-top:20px}.uk-table th{padding:16px 12px;text-align:left;vertical-align:bottom;font-size:.875rem;font-weight:400;color:#999;text-transform:uppercase}.uk-table td{padding:16px 12px;vertical-align:top}.uk-table td>:last-child{margin-bottom:0}.uk-table tfoot{font-size:.875rem}.uk-table caption{font-size:.875rem;text-align:left;color:#999}.uk-table-middle,.uk-table-middle td{vertical-align:middle!important}.uk-table-divider>:first-child>tr:not(:first-child),.uk-table-divider>:not(:first-child)>tr,.uk-table-divider>tr:not(:first-child){border-top:1px solid #e5e5e5}.uk-table-striped tbody tr:nth-of-type(odd),.uk-table-striped>tr:nth-of-type(odd){background:#f8f8f8;border-top:1px solid #e5e5e5;border-bottom:1px solid #e5e5e5}.uk-table-hover tbody tr:hover,.uk-table-hover>tr:hover{background:#ffd}.uk-table tbody tr.uk-active,.uk-table>tr.uk-active{background:#ffd}.uk-table-small td,.uk-table-small th{padding:10px 12px}.uk-table-large td,.uk-table-large th{padding:22px 12px}.uk-table-justify td:first-child,.uk-table-justify th:first-child{padding-left:0}.uk-table-justify td:last-child,.uk-table-justify th:last-child{padding-right:0}.uk-table-shrink{width:1px}.uk-table-expand{min-width:150px}.uk-table-link{padding:0!important}.uk-table-link>a{display:block;padding:16px 12px}.uk-table-small .uk-table-link>a{padding:10px 12px}@media (max-width:959px){.uk-table-responsive,.uk-table-responsive tbody,.uk-table-responsive td,.uk-table-responsive th,.uk-table-responsive tr{display:block}.uk-table-responsive thead{display:none}.uk-table-responsive td,.uk-table-responsive th{width:auto!important;max-width:none!important;min-width:0!important;overflow:visible!important;white-space:normal!important}.uk-table-responsive .uk-table-link:not(:first-child)>a,.uk-table-responsive td:not(:first-child):not(.uk-table-link),.uk-table-responsive th:not(:first-child):not(.uk-table-link){padding-top:5px!important}.uk-table-responsive .uk-table-link:not(:last-child)>a,.uk-table-responsive td:not(:last-child):not(.uk-table-link),.uk-table-responsive th:not(:last-child):not(.uk-table-link){padding-bottom:5px!important}.uk-table-justify.uk-table-responsive td,.uk-table-justify.uk-table-responsive th{padding-left:0;padding-right:0}}.uk-table tbody tr{transition:background-color .1s linear}.uk-icon{margin:0;border:none;border-radius:0;overflow:visible;font:inherit;color:inherit;text-transform:none;padding:0;background-color:transparent;display:inline-block;fill:currentcolor;line-height:0}button.uk-icon:not(:disabled){cursor:pointer}.uk-icon::-moz-focus-inner{border:0;padding:0}.uk-icon:not(.uk-preserve) [fill*='#']:not(.uk-preserve){fill:currentcolor}.uk-icon:not(.uk-preserve) [stroke*='#']:not(.uk-preserve){stroke:currentcolor}.uk-icon>*{transform:translate(0,0)}.uk-icon-image{width:20px;height:20px;background-position:50% 50%;background-repeat:no-repeat;background-size:contain;vertical-align:middle}.uk-icon-link{color:#999}.uk-icon-link:focus,.uk-icon-link:hover{color:#666;outline:0}.uk-active>.uk-icon-link,.uk-icon-link:active{color:#595959}.uk-icon-button{box-sizing:border-box;width:36px;height:36px;border-radius:500px;background:#f8f8f8;color:#999;vertical-align:middle;display:inline-flex;justify-content:center;align-items:center;transition:.1s ease-in-out;transition-property:color,background-color}.uk-icon-button:focus,.uk-icon-button:hover{background-color:#ebebeb;color:#666;outline:0}.uk-active>.uk-icon-button,.uk-icon-button:active{background-color:#dfdfdf;color:#666}.uk-range{box-sizing:border-box;margin:0;vertical-align:middle;max-width:100%;width:100%;-webkit-appearance:none;background:0 0;padding:0}.uk-range:focus{outline:0}.uk-range::-moz-focus-outer{border:none}.uk-range::-ms-track{height:15px;background:0 0;border-color:transparent;color:transparent}.uk-range:not(:disabled)::-webkit-slider-thumb{cursor:pointer}.uk-range:not(:disabled)::-moz-range-thumb{cursor:pointer}.uk-range:not(:disabled)::-ms-thumb{cursor:pointer}.uk-range::-webkit-slider-thumb{-webkit-appearance:none;margin-top:-7px;height:15px;width:15px;border-radius:500px;background:#fff;border:1px solid #ccc}.uk-range::-moz-range-thumb{border:none;height:15px;width:15px;border-radius:500px;background:#fff;border:1px solid #ccc}.uk-range::-ms-thumb{margin-top:0}.uk-range::-ms-thumb{border:none;height:15px;width:15px;border-radius:500px;background:#fff;border:1px solid #ccc}.uk-range::-ms-tooltip{display:none}.uk-range::-webkit-slider-runnable-track{height:3px;background:#ebebeb;border-radius:500px}.uk-range:active::-webkit-slider-runnable-track,.uk-range:focus::-webkit-slider-runnable-track{background:#d2d2d2}.uk-range::-moz-range-track{height:3px;background:#ebebeb;border-radius:500px}.uk-range:focus::-moz-range-track{background:#d2d2d2}.uk-range::-ms-fill-lower,.uk-range::-ms-fill-upper{height:3px;background:#ebebeb;border-radius:500px}.uk-range:focus::-ms-fill-lower,.uk-range:focus::-ms-fill-upper{background:#d2d2d2}.uk-checkbox,.uk-input,.uk-radio,.uk-select,.uk-textarea{box-sizing:border-box;margin:0;border-radius:0;font:inherit}.uk-input{overflow:visible}.uk-select{text-transform:none}.uk-select optgroup{font:inherit;font-weight:700}.uk-textarea{overflow:auto}.uk-input[type=search]::-webkit-search-cancel-button,.uk-input[type=search]::-webkit-search-decoration{-webkit-appearance:none}.uk-input[type=number]::-webkit-inner-spin-button,.uk-input[type=number]::-webkit-outer-spin-button{height:auto}.uk-input::-moz-placeholder,.uk-textarea::-moz-placeholder{opacity:1}.uk-checkbox:not(:disabled),.uk-radio:not(:disabled){cursor:pointer}.uk-fieldset{border:none;margin:0;padding:0}.uk-input,.uk-textarea{-webkit-appearance:none}.uk-input,.uk-select,.uk-textarea{max-width:100%;width:100%;border:0 none;padding:0 10px;background:#fff;color:#666;border:1px solid #e5e5e5;transition:.2s ease-in-out;transition-property:color,background-color,border}.uk-input,.uk-select:not([multiple]):not([size]){height:40px;vertical-align:middle;display:inline-block}.uk-input:not(input),.uk-select:not(select){line-height:38px}.uk-select[multiple],.uk-select[size],.uk-textarea{padding-top:4px;padding-bottom:4px;vertical-align:top}.uk-select[multiple],.uk-select[size]{resize:vertical}.uk-input:focus,.uk-select:focus,.uk-textarea:focus{outline:0;background-color:#fff;color:#666;border-color:#1e87f0}.uk-input:disabled,.uk-select:disabled,.uk-textarea:disabled{background-color:#f8f8f8;color:#999;border-color:#e5e5e5}.uk-input::-ms-input-placeholder{color:#999!important}.uk-input::placeholder{color:#999}.uk-textarea::-ms-input-placeholder{color:#999!important}.uk-textarea::placeholder{color:#999}.uk-form-small{font-size:.875rem}.uk-form-small:not(textarea):not([multiple]):not([size]){height:30px;padding-left:8px;padding-right:8px}.uk-form-small:not(select):not(input):not(textarea){line-height:28px}.uk-form-large{font-size:1.25rem}.uk-form-large:not(textarea):not([multiple]):not([size]){height:55px;padding-left:12px;padding-right:12px}.uk-form-large:not(select):not(input):not(textarea){line-height:53px}.uk-form-danger,.uk-form-danger:focus{color:#f0506e;border-color:#f0506e}.uk-form-success,.uk-form-success:focus{color:#32d296;border-color:#32d296}.uk-form-blank{background:0 0;border-color:transparent}.uk-form-blank:focus{border-color:#e5e5e5;border-style:dashed}input.uk-form-width-xsmall{width:50px}select.uk-form-width-xsmall{width:75px}.uk-form-width-small{width:130px}.uk-form-width-medium{width:200px}.uk-form-width-large{width:500px}.uk-select:not([multiple]):not([size]){-webkit-appearance:none;-moz-appearance:none;padding-right:20px;background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23666%22%20points%3D%2212%201%209%206%2015%206%22%20%2F%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23666%22%20points%3D%2212%2013%209%208%2015%208%22%20%2F%3E%0A%3C%2Fsvg%3E%0A");background-repeat:no-repeat;background-position:100% 50%}.uk-select:not([multiple]):not([size])::-ms-expand{display:none}.uk-select:not([multiple]):not([size]) option{color:#444}.uk-select:not([multiple]):not([size]):disabled{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23999%22%20points%3D%2212%201%209%206%2015%206%22%20%2F%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23999%22%20points%3D%2212%2013%209%208%2015%208%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-input[list]{padding-right:20px;background-repeat:no-repeat;background-position:100% 50%}.uk-input[list]:focus,.uk-input[list]:hover{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23666%22%20points%3D%2212%2012%208%206%2016%206%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-input[list]::-webkit-calendar-picker-indicator{display:none}.uk-checkbox,.uk-radio{display:inline-block;height:16px;width:16px;overflow:hidden;margin-top:-4px;vertical-align:middle;-webkit-appearance:none;-moz-appearance:none;background-color:transparent;background-repeat:no-repeat;background-position:50% 50%;border:1px solid #ccc;transition:.2s ease-in-out;transition-property:background-color,border}.uk-radio{border-radius:50%}.uk-checkbox:focus,.uk-radio:focus{outline:0;border-color:#1e87f0}.uk-checkbox:checked,.uk-checkbox:indeterminate,.uk-radio:checked{background-color:#1e87f0;border-color:transparent}.uk-checkbox:checked:focus,.uk-checkbox:indeterminate:focus,.uk-radio:checked:focus{background-color:#0e6dcd}.uk-radio:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22%23fff%22%20cx%3D%228%22%20cy%3D%228%22%20r%3D%222%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-checkbox:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2211%22%20viewBox%3D%220%200%2014%2011%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23fff%22%20points%3D%2212%201%205%207.5%202%205%201%205.5%205%2010%2013%201.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-checkbox:indeterminate{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22%23fff%22%20x%3D%223%22%20y%3D%228%22%20width%3D%2210%22%20height%3D%221%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-checkbox:disabled,.uk-radio:disabled{background-color:#f8f8f8;border-color:#e5e5e5}.uk-radio:disabled:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22%23999%22%20cx%3D%228%22%20cy%3D%228%22%20r%3D%222%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-checkbox:disabled:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2211%22%20viewBox%3D%220%200%2014%2011%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23999%22%20points%3D%2212%201%205%207.5%202%205%201%205.5%205%2010%2013%201.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-checkbox:disabled:indeterminate{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22%23999%22%20x%3D%223%22%20y%3D%228%22%20width%3D%2210%22%20height%3D%221%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-legend{width:100%;color:inherit;padding:0;font-size:1.5rem;line-height:1.4}.uk-form-custom{display:inline-block;position:relative;max-width:100%;vertical-align:middle}.uk-form-custom input[type=file],.uk-form-custom select{position:absolute;top:0;z-index:1;width:100%;height:100%;left:0;-webkit-appearance:none;opacity:0;cursor:pointer}.uk-form-custom input[type=file]{font-size:500px;overflow:hidden}.uk-form-label{color:#333;font-size:.875rem}.uk-form-stacked .uk-form-label{display:block;margin-bottom:5px}@media (max-width:959px){.uk-form-horizontal .uk-form-label{display:block;margin-bottom:5px}}@media (min-width:960px){.uk-form-horizontal .uk-form-label{width:200px;margin-top:7px;float:left}.uk-form-horizontal .uk-form-controls{margin-left:215px}.uk-form-horizontal .uk-form-controls-text{padding-top:7px}}.uk-form-icon{position:absolute;top:0;bottom:0;left:0;width:40px;display:inline-flex;justify-content:center;align-items:center;color:#999}.uk-form-icon:hover{color:#666}.uk-form-icon:not(a):not(button):not(input){pointer-events:none}.uk-form-icon:not(.uk-form-icon-flip)~.uk-input{padding-left:40px!important}.uk-form-icon-flip{right:0;left:auto}.uk-form-icon-flip~.uk-input{padding-right:40px!important}.uk-button{margin:0;border:none;overflow:visible;font:inherit;color:inherit;text-transform:none;-webkit-appearance:none;border-radius:0;display:inline-block;box-sizing:border-box;padding:0 30px;vertical-align:middle;font-size:.875rem;line-height:38px;text-align:center;text-decoration:none;text-transform:uppercase;transition:.1s ease-in-out;transition-property:color,background-color,border-color}.uk-button:not(:disabled){cursor:pointer}.uk-button::-moz-focus-inner{border:0;padding:0}.uk-button:hover{text-decoration:none}.uk-button:focus{outline:0}.uk-button-default{background-color:transparent;color:#333;border:1px solid #e5e5e5}.uk-button-default:focus,.uk-button-default:hover{background-color:transparent;color:#333;border-color:#b2b2b2}.uk-button-default.uk-active,.uk-button-default:active{background-color:transparent;color:#333;border-color:#999}.uk-button-primary{background-color:#1e87f0;color:#fff;border:1px solid transparent}.uk-button-primary:focus,.uk-button-primary:hover{background-color:#0f7ae5;color:#fff}.uk-button-primary.uk-active,.uk-button-primary:active{background-color:#0e6dcd;color:#fff}.uk-button-secondary{background-color:#222;color:#fff;border:1px solid transparent}.uk-button-secondary:focus,.uk-button-secondary:hover{background-color:#151515;color:#fff}.uk-button-secondary.uk-active,.uk-button-secondary:active{background-color:#080808;color:#fff}.uk-button-danger{background-color:#f0506e;color:#fff;border:1px solid transparent}.uk-button-danger:focus,.uk-button-danger:hover{background-color:#ee395b;color:#fff}.uk-button-danger.uk-active,.uk-button-danger:active{background-color:#ec2147;color:#fff}.uk-button-danger:disabled,.uk-button-default:disabled,.uk-button-primary:disabled,.uk-button-secondary:disabled{background-color:transparent;color:#999;border-color:#e5e5e5}.uk-button-small{padding:0 15px;line-height:28px;font-size:.875rem}.uk-button-large{padding:0 40px;line-height:53px;font-size:.875rem}.uk-button-text{padding:0;line-height:1.5;background:0 0;color:#333;position:relative}.uk-button-text::before{content:"";position:absolute;bottom:0;left:0;right:100%;border-bottom:1px solid #333;transition:right .3s ease-out}.uk-button-text:focus,.uk-button-text:hover{color:#333}.uk-button-text:focus::before,.uk-button-text:hover::before{right:0}.uk-button-text:disabled{color:#999}.uk-button-text:disabled::before{display:none}.uk-button-link{padding:0;line-height:1.5;background:0 0;color:#1e87f0}.uk-button-link:focus,.uk-button-link:hover{color:#0f6ecd;text-decoration:underline}.uk-button-link:disabled{color:#999;text-decoration:none}.uk-button-group{display:inline-flex;vertical-align:middle;position:relative}.uk-button-group>.uk-button:nth-child(n+2),.uk-button-group>div:nth-child(n+2) .uk-button{margin-left:-1px}.uk-button-group .uk-button.uk-active,.uk-button-group .uk-button:active,.uk-button-group .uk-button:focus,.uk-button-group .uk-button:hover{position:relative;z-index:1}.uk-progress{vertical-align:baseline;-webkit-appearance:none;-moz-appearance:none;display:block;width:100%;border:0;background-color:#f8f8f8;margin-bottom:20px;height:15px;border-radius:500px;overflow:hidden}*+.uk-progress{margin-top:20px}.uk-progress:indeterminate{color:transparent}.uk-progress::-webkit-progress-bar{background-color:#f8f8f8;border-radius:500px;overflow:hidden}.uk-progress:indeterminate::-moz-progress-bar{width:0}.uk-progress::-webkit-progress-value{background-color:#1e87f0;transition:width .6s ease}.uk-progress::-moz-progress-bar{background-color:#1e87f0}.uk-progress::-ms-fill{background-color:#1e87f0;transition:width .6s ease;border:0}.uk-section{display:flow-root;box-sizing:border-box;padding-top:40px;padding-bottom:40px}@media (min-width:960px){.uk-section{padding-top:70px;padding-bottom:70px}}.uk-section>:last-child{margin-bottom:0}.uk-section-xsmall{padding-top:20px;padding-bottom:20px}.uk-section-small{padding-top:40px;padding-bottom:40px}.uk-section-large{padding-top:70px;padding-bottom:70px}@media (min-width:960px){.uk-section-large{padding-top:140px;padding-bottom:140px}}.uk-section-xlarge{padding-top:140px;padding-bottom:140px}@media (min-width:960px){.uk-section-xlarge{padding-top:210px;padding-bottom:210px}}.uk-section-default{background:#fff}.uk-section-muted{background:#f8f8f8}.uk-section-primary{background:#1e87f0}.uk-section-secondary{background:#222}.uk-container{display:flow-root;box-sizing:content-box;max-width:1200px;margin-left:auto;margin-right:auto;padding-left:15px;padding-right:15px}@media (min-width:640px){.uk-container{padding-left:30px;padding-right:30px}}@media (min-width:960px){.uk-container{padding-left:40px;padding-right:40px}}.uk-container>:last-child{margin-bottom:0}.uk-container .uk-container{padding-left:0;padding-right:0}.uk-container-xsmall{max-width:750px}.uk-container-small{max-width:900px}.uk-container-large{max-width:1400px}.uk-container-xlarge{max-width:1600px}.uk-container-expand{max-width:none}.uk-container-expand-left{margin-left:0}.uk-container-expand-right{margin-right:0}@media (min-width:640px){.uk-container-expand-left.uk-container-xsmall,.uk-container-expand-right.uk-container-xsmall{max-width:calc(50% + (750px / 2) - 30px)}.uk-container-expand-left.uk-container-small,.uk-container-expand-right.uk-container-small{max-width:calc(50% + (900px / 2) - 30px)}}@media (min-width:960px){.uk-container-expand-left,.uk-container-expand-right{max-width:calc(50% + (1200px / 2) - 40px)}.uk-container-expand-left.uk-container-xsmall,.uk-container-expand-right.uk-container-xsmall{max-width:calc(50% + (750px / 2) - 40px)}.uk-container-expand-left.uk-container-small,.uk-container-expand-right.uk-container-small{max-width:calc(50% + (900px / 2) - 40px)}.uk-container-expand-left.uk-container-large,.uk-container-expand-right.uk-container-large{max-width:calc(50% + (1400px / 2) - 40px)}.uk-container-expand-left.uk-container-xlarge,.uk-container-expand-right.uk-container-xlarge{max-width:calc(50% + (1600px / 2) - 40px)}}.uk-container-item-padding-remove-left,.uk-container-item-padding-remove-right{width:calc(100% + 15px)}.uk-container-item-padding-remove-left{margin-left:-15px}.uk-container-item-padding-remove-right{margin-right:-15px}@media (min-width:640px){.uk-container-item-padding-remove-left,.uk-container-item-padding-remove-right{width:calc(100% + 30px)}.uk-container-item-padding-remove-left{margin-left:-30px}.uk-container-item-padding-remove-right{margin-right:-30px}}@media (min-width:960px){.uk-container-item-padding-remove-left,.uk-container-item-padding-remove-right{width:calc(100% + 40px)}.uk-container-item-padding-remove-left{margin-left:-40px}.uk-container-item-padding-remove-right{margin-right:-40px}}.uk-tile{display:flow-root;position:relative;box-sizing:border-box;padding-left:15px;padding-right:15px;padding-top:40px;padding-bottom:40px}@media (min-width:640px){.uk-tile{padding-left:30px;padding-right:30px}}@media (min-width:960px){.uk-tile{padding-left:40px;padding-right:40px;padding-top:70px;padding-bottom:70px}}.uk-tile>:last-child{margin-bottom:0}.uk-tile-xsmall{padding-top:20px;padding-bottom:20px}.uk-tile-small{padding-top:40px;padding-bottom:40px}.uk-tile-large{padding-top:70px;padding-bottom:70px}@media (min-width:960px){.uk-tile-large{padding-top:140px;padding-bottom:140px}}.uk-tile-xlarge{padding-top:140px;padding-bottom:140px}@media (min-width:960px){.uk-tile-xlarge{padding-top:210px;padding-bottom:210px}}.uk-tile-default{background:#fff}.uk-tile-muted{background:#f8f8f8}.uk-tile-primary{background:#1e87f0}.uk-tile-secondary{background:#222}.uk-card{position:relative;box-sizing:border-box;transition:box-shadow .1s ease-in-out}.uk-card-body{display:flow-root;padding:30px 30px}.uk-card-header{display:flow-root;padding:15px 30px}.uk-card-footer{display:flow-root;padding:15px 30px}@media (min-width:1200px){.uk-card-body{padding:40px 40px}.uk-card-header{padding:20px 40px}.uk-card-footer{padding:20px 40px}}.uk-card-body>:last-child,.uk-card-footer>:last-child,.uk-card-header>:last-child{margin-bottom:0}.uk-card-title{font-size:1.5rem;line-height:1.4}.uk-card-badge{position:absolute;top:15px;right:15px;z-index:1;height:30px;padding-left:10px;padding-right:10px;background:#1e87f0;color:#fff;font-size:.875rem;display:flex;justify-content:center;align-items:center}.uk-card-badge:first-child+*{margin-top:0}.uk-card-hover:not(.uk-card-default):not(.uk-card-primary):not(.uk-card-secondary):hover{background:#fff;box-shadow:0 14px 25px rgba(0,0,0,.16)}.uk-card-default{background:#fff;color:#666;box-shadow:0 5px 15px rgba(0,0,0,.08)}.uk-card-default .uk-card-title{color:#333}.uk-card-default.uk-card-hover:hover{background-color:#fff;box-shadow:0 14px 25px rgba(0,0,0,.16)}.uk-card-default .uk-card-header{border-bottom:1px solid #e5e5e5}.uk-card-default .uk-card-footer{border-top:1px solid #e5e5e5}.uk-card-primary{background:#1e87f0;color:#fff;box-shadow:0 5px 15px rgba(0,0,0,.08)}.uk-card-primary .uk-card-title{color:#fff}.uk-card-primary.uk-card-hover:hover{background-color:#1e87f0;box-shadow:0 14px 25px rgba(0,0,0,.16)}.uk-card-secondary{background:#222;color:#fff;box-shadow:0 5px 15px rgba(0,0,0,.08)}.uk-card-secondary .uk-card-title{color:#fff}.uk-card-secondary.uk-card-hover:hover{background-color:#222;box-shadow:0 14px 25px rgba(0,0,0,.16)}.uk-card-small .uk-card-body,.uk-card-small.uk-card-body{padding:20px 20px}.uk-card-small .uk-card-header{padding:13px 20px}.uk-card-small .uk-card-footer{padding:13px 20px}@media (min-width:1200px){.uk-card-large .uk-card-body,.uk-card-large.uk-card-body{padding:70px 70px}.uk-card-large .uk-card-header{padding:35px 70px}.uk-card-large .uk-card-footer{padding:35px 70px}}.uk-card-body>.uk-nav-default{margin-left:-30px;margin-right:-30px}.uk-card-body>.uk-nav-default:only-child{margin-top:-15px;margin-bottom:-15px}.uk-card-body>.uk-nav-default .uk-nav-divider,.uk-card-body>.uk-nav-default .uk-nav-header,.uk-card-body>.uk-nav-default>li>a{padding-left:30px;padding-right:30px}.uk-card-body>.uk-nav-default .uk-nav-sub{padding-left:45px}@media (min-width:1200px){.uk-card-body>.uk-nav-default{margin-left:-40px;margin-right:-40px}.uk-card-body>.uk-nav-default:only-child{margin-top:-25px;margin-bottom:-25px}.uk-card-body>.uk-nav-default .uk-nav-divider,.uk-card-body>.uk-nav-default .uk-nav-header,.uk-card-body>.uk-nav-default>li>a{padding-left:40px;padding-right:40px}.uk-card-body>.uk-nav-default .uk-nav-sub{padding-left:55px}}.uk-card-small>.uk-nav-default{margin-left:-20px;margin-right:-20px}.uk-card-small>.uk-nav-default:only-child{margin-top:-5px;margin-bottom:-5px}.uk-card-small>.uk-nav-default .uk-nav-divider,.uk-card-small>.uk-nav-default .uk-nav-header,.uk-card-small>.uk-nav-default>li>a{padding-left:20px;padding-right:20px}.uk-card-small>.uk-nav-default .uk-nav-sub{padding-left:35px}@media (min-width:1200px){.uk-card-large>.uk-nav-default{margin:0}.uk-card-large>.uk-nav-default:only-child{margin:0}.uk-card-large>.uk-nav-default .uk-nav-divider,.uk-card-large>.uk-nav-default .uk-nav-header,.uk-card-large>.uk-nav-default>li>a{padding-left:0;padding-right:0}.uk-card-large>.uk-nav-default .uk-nav-sub{padding-left:15px}}.uk-close{color:#999;transition:.1s ease-in-out;transition-property:color,opacity}.uk-close:focus,.uk-close:hover{color:#666;outline:0}.uk-spinner>*{animation:uk-spinner-rotate 1.4s linear infinite}@keyframes uk-spinner-rotate{0%{transform:rotate(0)}100%{transform:rotate(270deg)}}.uk-spinner>*>*{stroke-dasharray:88px;stroke-dashoffset:0;transform-origin:center;animation:uk-spinner-dash 1.4s ease-in-out infinite;stroke-width:1;stroke-linecap:round}@keyframes uk-spinner-dash{0%{stroke-dashoffset:88px}50%{stroke-dashoffset:22px;transform:rotate(135deg)}100%{stroke-dashoffset:88px;transform:rotate(450deg)}}.uk-totop{padding:5px;color:#999;transition:color .1s ease-in-out}.uk-totop:focus,.uk-totop:hover{color:#666;outline:0}.uk-totop:active{color:#333}.uk-marker{padding:5px;background:#222;color:#fff;border-radius:500px}.uk-marker:focus,.uk-marker:hover{color:#fff;outline:0}.uk-alert{position:relative;margin-bottom:20px;padding:15px 29px 15px 15px;background:#f8f8f8;color:#666}*+.uk-alert{margin-top:20px}.uk-alert>:last-child{margin-bottom:0}.uk-alert-close{position:absolute;top:20px;right:15px;color:inherit;opacity:.4}.uk-alert-close:first-child+*{margin-top:0}.uk-alert-close:focus,.uk-alert-close:hover{color:inherit;opacity:.8}.uk-alert-primary{background:#d8eafc;color:#1e87f0}.uk-alert-success{background:#edfbf6;color:#32d296}.uk-alert-warning{background:#fff6ee;color:#faa05a}.uk-alert-danger{background:#fef4f6;color:#f0506e}.uk-alert h1,.uk-alert h2,.uk-alert h3,.uk-alert h4,.uk-alert h5,.uk-alert h6{color:inherit}.uk-alert a:not([class]){color:inherit;text-decoration:underline}.uk-alert a:not([class]):hover{color:inherit;text-decoration:underline}.uk-placeholder{margin-bottom:20px;padding:30px 30px;background:0 0;border:1px dashed #e5e5e5}*+.uk-placeholder{margin-top:20px}.uk-placeholder>:last-child{margin-bottom:0}.uk-badge{box-sizing:border-box;min-width:22px;height:22px;padding:0 5px;border-radius:500px;vertical-align:middle;background:#1e87f0;color:#fff;font-size:.875rem;display:inline-flex;justify-content:center;align-items:center}.uk-badge:focus,.uk-badge:hover{color:#fff;text-decoration:none;outline:0}.uk-label{display:inline-block;padding:0 10px;background:#1e87f0;line-height:1.5;font-size:.875rem;color:#fff;vertical-align:middle;white-space:nowrap;border-radius:2px;text-transform:uppercase}.uk-label-success{background-color:#32d296;color:#fff}.uk-label-warning{background-color:#faa05a;color:#fff}.uk-label-danger{background-color:#f0506e;color:#fff}.uk-overlay{padding:30px 30px}.uk-overlay>:last-child{margin-bottom:0}.uk-overlay-default{background:rgba(255,255,255,.8)}.uk-overlay-primary{background:rgba(34,34,34,.8)}.uk-article{display:flow-root}.uk-article>:last-child{margin-bottom:0}.uk-article+.uk-article{margin-top:70px}.uk-article-title{font-size:2.23125rem;line-height:1.2}@media (min-width:960px){.uk-article-title{font-size:2.625rem}}.uk-article-meta{font-size:.875rem;line-height:1.4;color:#999}.uk-article-meta a{color:#999}.uk-article-meta a:hover{color:#666;text-decoration:none}.uk-comment-body{display:flow-root;overflow-wrap:break-word;word-wrap:break-word}.uk-comment-header{display:flow-root;margin-bottom:20px}.uk-comment-body>:last-child,.uk-comment-header>:last-child{margin-bottom:0}.uk-comment-title{font-size:1.25rem;line-height:1.4}.uk-comment-meta{font-size:.875rem;line-height:1.4;color:#999}.uk-comment-list{padding:0;list-style:none}.uk-comment-list>:nth-child(n+2){margin-top:70px}.uk-comment-list .uk-comment~ul{margin:70px 0 0 0;padding-left:30px;list-style:none}@media (min-width:960px){.uk-comment-list .uk-comment~ul{padding-left:100px}}.uk-comment-list .uk-comment~ul>:nth-child(n+2){margin-top:70px}.uk-comment-primary{padding:30px;background-color:#f8f8f8}.uk-search{display:inline-block;position:relative;max-width:100%;margin:0}.uk-search-input::-webkit-search-cancel-button,.uk-search-input::-webkit-search-decoration{-webkit-appearance:none}.uk-search-input::-moz-placeholder{opacity:1}.uk-search-input{box-sizing:border-box;margin:0;border-radius:0;font:inherit;overflow:visible;-webkit-appearance:none;vertical-align:middle;width:100%;border:none;color:#666}.uk-search-input:focus{outline:0}.uk-search-input:-ms-input-placeholder{color:#999!important}.uk-search-input::placeholder{color:#999}.uk-search-icon:focus{outline:0}.uk-search .uk-search-icon{position:absolute;top:0;bottom:0;left:0;display:inline-flex;justify-content:center;align-items:center;color:#999}.uk-search .uk-search-icon:hover{color:#999}.uk-search .uk-search-icon:not(a):not(button):not(input){pointer-events:none}.uk-search .uk-search-icon-flip{right:0;left:auto}.uk-search-default{width:180px}.uk-search-default .uk-search-input{height:40px;padding-left:6px;padding-right:6px;background:0 0;border:1px solid #e5e5e5}.uk-search-default .uk-search-input:focus{background-color:transparent}.uk-search-default .uk-search-icon{width:40px}.uk-search-default .uk-search-icon:not(.uk-search-icon-flip)~.uk-search-input{padding-left:40px}.uk-search-default .uk-search-icon-flip~.uk-search-input{padding-right:40px}.uk-search-navbar{width:400px}.uk-search-navbar .uk-search-input{height:40px;background:0 0;font-size:1.5rem}.uk-search-navbar .uk-search-icon{width:40px}.uk-search-navbar .uk-search-icon:not(.uk-search-icon-flip)~.uk-search-input{padding-left:40px}.uk-search-navbar .uk-search-icon-flip~.uk-search-input{padding-right:40px}.uk-search-large{width:500px}.uk-search-large .uk-search-input{height:80px;background:0 0;font-size:2.625rem}.uk-search-large .uk-search-icon{width:80px}.uk-search-large .uk-search-icon:not(.uk-search-icon-flip)~.uk-search-input{padding-left:80px}.uk-search-large .uk-search-icon-flip~.uk-search-input{padding-right:80px}.uk-search-toggle{color:#999}.uk-search-toggle:focus,.uk-search-toggle:hover{color:#666}.uk-accordion{padding:0;list-style:none}.uk-accordion>:nth-child(n+2){margin-top:20px}.uk-accordion-title{display:block;font-size:1.25rem;line-height:1.4;color:#333;overflow:hidden}.uk-accordion-title::before{content:"";width:1.4em;height:1.4em;margin-left:10px;float:right;background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22%23666%22%20width%3D%2213%22%20height%3D%221%22%20x%3D%220%22%20y%3D%226%22%20%2F%3E%0A%20%20%20%20%3Crect%20fill%3D%22%23666%22%20width%3D%221%22%20height%3D%2213%22%20x%3D%226%22%20y%3D%220%22%20%2F%3E%0A%3C%2Fsvg%3E");background-repeat:no-repeat;background-position:50% 50%}.uk-open>.uk-accordion-title::before{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22%23666%22%20width%3D%2213%22%20height%3D%221%22%20x%3D%220%22%20y%3D%226%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-accordion-title:focus,.uk-accordion-title:hover{color:#666;text-decoration:none;outline:0}.uk-accordion-content{display:flow-root;margin-top:20px}.uk-accordion-content>:last-child{margin-bottom:0}.uk-drop{display:none;position:absolute;z-index:1020;box-sizing:border-box;width:300px}.uk-drop.uk-open{display:block}[class*=uk-drop-top]{margin-top:-20px}[class*=uk-drop-bottom]{margin-top:20px}[class*=uk-drop-left]{margin-left:-20px}[class*=uk-drop-right]{margin-left:20px}.uk-drop-stack .uk-drop-grid>*{width:100%!important}.uk-dropdown{display:none;position:absolute;z-index:1020;box-sizing:border-box;min-width:200px;padding:25px;background:#fff;color:#666;box-shadow:0 5px 12px rgba(0,0,0,.15)}.uk-dropdown.uk-open{display:block}.uk-dropdown-nav{white-space:nowrap;font-size:.875rem}.uk-dropdown-nav>li>a{color:#999}.uk-dropdown-nav>li.uk-active>a,.uk-dropdown-nav>li>a:focus,.uk-dropdown-nav>li>a:hover{color:#666}.uk-dropdown-nav .uk-nav-header{color:#333}.uk-dropdown-nav .uk-nav-divider{border-top:1px solid #e5e5e5}.uk-dropdown-nav .uk-nav-sub a{color:#999}.uk-dropdown-nav .uk-nav-sub a:focus,.uk-dropdown-nav .uk-nav-sub a:hover,.uk-dropdown-nav .uk-nav-sub li.uk-active>a{color:#666}[class*=uk-dropdown-top]{margin-top:-10px}[class*=uk-dropdown-bottom]{margin-top:10px}[class*=uk-dropdown-left]{margin-left:-10px}[class*=uk-dropdown-right]{margin-left:10px}.uk-dropdown-stack .uk-dropdown-grid>*{width:100%!important}.uk-modal{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1010;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:15px 15px;background:rgba(0,0,0,.6);opacity:0;transition:opacity .15s linear}@media (min-width:640px){.uk-modal{padding:50px 30px}}@media (min-width:960px){.uk-modal{padding-left:40px;padding-right:40px}}.uk-modal.uk-open{opacity:1}.uk-modal-page{overflow:hidden}.uk-modal-dialog{position:relative;box-sizing:border-box;margin:0 auto;width:600px;max-width:calc(100% - .01px)!important;background:#fff;opacity:0;transform:translateY(-100px);transition:.3s linear;transition-property:opacity,transform}.uk-open>.uk-modal-dialog{opacity:1;transform:translateY(0)}.uk-modal-container .uk-modal-dialog{width:1200px}.uk-modal-full{padding:0;background:0 0}.uk-modal-full .uk-modal-dialog{margin:0;width:100%;max-width:100%;transform:translateY(0)}.uk-modal-body{display:flow-root;padding:30px 30px}.uk-modal-header{display:flow-root;padding:15px 30px;background:#fff;border-bottom:1px solid #e5e5e5}.uk-modal-footer{display:flow-root;padding:15px 30px;background:#fff;border-top:1px solid #e5e5e5}.uk-modal-body>:last-child,.uk-modal-footer>:last-child,.uk-modal-header>:last-child{margin-bottom:0}.uk-modal-title{font-size:2rem;line-height:1.3}[class*=uk-modal-close-]{position:absolute;z-index:1010;top:10px;right:10px;padding:5px}[class*=uk-modal-close-]:first-child+*{margin-top:0}.uk-modal-close-outside{top:0;right:-5px;transform:translate(0,-100%);color:#fff}.uk-modal-close-outside:hover{color:#fff}@media (min-width:960px){.uk-modal-close-outside{right:0;transform:translate(100%,-100%)}}.uk-modal-close-full{top:0;right:0;padding:20px;background:#fff}.uk-slideshow{-webkit-tap-highlight-color:transparent}.uk-slideshow-items{position:relative;z-index:0;margin:0;padding:0;list-style:none;overflow:hidden;-webkit-touch-callout:none}.uk-slideshow-items>*{position:absolute;top:0;left:0;right:0;bottom:0;overflow:hidden;will-change:transform,opacity;touch-action:pan-y}.uk-slideshow-items>:focus{outline:0}.uk-slideshow-items>:not(.uk-active){display:none}.uk-slider{-webkit-tap-highlight-color:transparent}.uk-slider-container{overflow:hidden}.uk-slider-container-offset{margin:-11px -25px -39px -25px;padding:11px 25px 39px 25px}.uk-slider-items{will-change:transform;position:relative}.uk-slider-items:not(.uk-grid){display:flex;margin:0;padding:0;list-style:none;-webkit-touch-callout:none}.uk-slider-items.uk-grid{flex-wrap:nowrap}.uk-slider-items>*{flex:none;max-width:100%;position:relative;touch-action:pan-y}.uk-slider-items>:focus{outline:0}.uk-sticky-fixed{z-index:980;box-sizing:border-box;margin:0!important;-webkit-backface-visibility:hidden;backface-visibility:hidden}.uk-sticky[class*=uk-animation-]{animation-duration:.2s}.uk-sticky.uk-animation-reverse{animation-duration:.2s}.uk-offcanvas{display:none;position:fixed;top:0;bottom:0;left:0;z-index:1000}.uk-offcanvas-flip .uk-offcanvas{right:0;left:auto}.uk-offcanvas-bar{position:absolute;top:0;bottom:0;left:-270px;box-sizing:border-box;width:270px;padding:20px 20px;background:#222;overflow-y:auto;-webkit-overflow-scrolling:touch}@media (min-width:960px){.uk-offcanvas-bar{left:-350px;width:350px;padding:40px 40px}}.uk-offcanvas-flip .uk-offcanvas-bar{left:auto;right:-270px}@media (min-width:960px){.uk-offcanvas-flip .uk-offcanvas-bar{right:-350px}}.uk-open>.uk-offcanvas-bar{left:0}.uk-offcanvas-flip .uk-open>.uk-offcanvas-bar{left:auto;right:0}.uk-offcanvas-bar-animation{transition:left .3s ease-out}.uk-offcanvas-flip .uk-offcanvas-bar-animation{transition-property:right}.uk-offcanvas-reveal{position:absolute;top:0;bottom:0;left:0;width:0;overflow:hidden;transition:width .3s ease-out}.uk-offcanvas-reveal .uk-offcanvas-bar{left:0}.uk-offcanvas-flip .uk-offcanvas-reveal .uk-offcanvas-bar{left:auto;right:0}.uk-open>.uk-offcanvas-reveal{width:270px}@media (min-width:960px){.uk-open>.uk-offcanvas-reveal{width:350px}}.uk-offcanvas-flip .uk-offcanvas-reveal{right:0;left:auto}.uk-offcanvas-close{position:absolute;z-index:1000;top:20px;right:20px;padding:5px}.uk-offcanvas-overlay{width:100vw;touch-action:none}.uk-offcanvas-overlay::before{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background:rgba(0,0,0,.1);opacity:0;transition:opacity .15s linear}.uk-offcanvas-overlay.uk-open::before{opacity:1}.uk-offcanvas-container,.uk-offcanvas-page{overflow-x:hidden}.uk-offcanvas-container{position:relative;left:0;transition:left .3s ease-out;box-sizing:border-box;width:100%}:not(.uk-offcanvas-flip).uk-offcanvas-container-animation{left:270px}.uk-offcanvas-flip.uk-offcanvas-container-animation{left:-270px}@media (min-width:960px){:not(.uk-offcanvas-flip).uk-offcanvas-container-animation{left:350px}.uk-offcanvas-flip.uk-offcanvas-container-animation{left:-350px}}.uk-switcher{margin:0;padding:0;list-style:none}.uk-switcher>:not(.uk-active){display:none}.uk-switcher>*>:last-child{margin-bottom:0}.uk-leader{overflow:hidden}.uk-leader-fill::after{display:inline-block;margin-left:15px;width:0;content:attr(data-fill);white-space:nowrap}.uk-leader-fill.uk-leader-hide::after{display:none}.uk-leader-fill-content::before{content:'.'}:root{--uk-leader-fill-content:.}.uk-notification{position:fixed;top:10px;left:10px;z-index:1040;box-sizing:border-box;width:350px}.uk-notification-bottom-right,.uk-notification-top-right{left:auto;right:10px}.uk-notification-bottom-center,.uk-notification-top-center{left:50%;margin-left:-175px}.uk-notification-bottom-center,.uk-notification-bottom-left,.uk-notification-bottom-right{top:auto;bottom:10px}@media (max-width:639px){.uk-notification{left:10px;right:10px;width:auto;margin:0}}.uk-notification-message{position:relative;padding:15px;background:#f8f8f8;color:#666;font-size:1.25rem;line-height:1.4;cursor:pointer}*+.uk-notification-message{margin-top:10px}.uk-notification-close{display:none;position:absolute;top:20px;right:15px}.uk-notification-message:hover .uk-notification-close{display:block}.uk-notification-message-primary{color:#1e87f0}.uk-notification-message-success{color:#32d296}.uk-notification-message-warning{color:#faa05a}.uk-notification-message-danger{color:#f0506e}.uk-tooltip{display:none;position:absolute;z-index:1030;top:0;box-sizing:border-box;max-width:200px;padding:3px 6px;background:#666;border-radius:2px;color:#fff;font-size:12px}.uk-tooltip.uk-active{display:block}[class*=uk-tooltip-top]{margin-top:-10px}[class*=uk-tooltip-bottom]{margin-top:10px}[class*=uk-tooltip-left]{margin-left:-10px}[class*=uk-tooltip-right]{margin-left:10px}.uk-sortable{position:relative}.uk-sortable>:last-child{margin-bottom:0}.uk-sortable-drag{position:fixed!important;z-index:1050!important;pointer-events:none}.uk-sortable-placeholder{opacity:0;pointer-events:none}.uk-sortable-empty{min-height:50px}.uk-sortable-handle:hover{cursor:move}.uk-countdown-number{font-variant-numeric:tabular-nums;font-size:2rem;line-height:.8}@media (min-width:640px){.uk-countdown-number{font-size:4rem}}@media (min-width:960px){.uk-countdown-number{font-size:6rem}}.uk-countdown-separator{font-size:1rem;line-height:1.6}@media (min-width:640px){.uk-countdown-separator{font-size:2rem}}@media (min-width:960px){.uk-countdown-separator{font-size:3rem}}.uk-grid{display:flex;flex-wrap:wrap;margin:0;padding:0;list-style:none}.uk-grid>*{margin:0}.uk-grid>*>:last-child{margin-bottom:0}.uk-grid{margin-left:-30px}.uk-grid>*{padding-left:30px}*+.uk-grid-margin,.uk-grid+.uk-grid,.uk-grid>.uk-grid-margin{margin-top:30px}@media (min-width:1200px){.uk-grid{margin-left:-40px}.uk-grid>*{padding-left:40px}*+.uk-grid-margin,.uk-grid+.uk-grid,.uk-grid>.uk-grid-margin{margin-top:40px}}.uk-grid-column-small,.uk-grid-small{margin-left:-15px}.uk-grid-column-small>*,.uk-grid-small>*{padding-left:15px}*+.uk-grid-margin-small,.uk-grid+.uk-grid-row-small,.uk-grid+.uk-grid-small,.uk-grid-row-small>.uk-grid-margin,.uk-grid-small>.uk-grid-margin{margin-top:15px}.uk-grid-column-medium,.uk-grid-medium{margin-left:-30px}.uk-grid-column-medium>*,.uk-grid-medium>*{padding-left:30px}*+.uk-grid-margin-medium,.uk-grid+.uk-grid-medium,.uk-grid+.uk-grid-row-medium,.uk-grid-medium>.uk-grid-margin,.uk-grid-row-medium>.uk-grid-margin{margin-top:30px}.uk-grid-column-large,.uk-grid-large{margin-left:-40px}.uk-grid-column-large>*,.uk-grid-large>*{padding-left:40px}*+.uk-grid-margin-large,.uk-grid+.uk-grid-large,.uk-grid+.uk-grid-row-large,.uk-grid-large>.uk-grid-margin,.uk-grid-row-large>.uk-grid-margin{margin-top:40px}@media (min-width:1200px){.uk-grid-column-large,.uk-grid-large{margin-left:-70px}.uk-grid-column-large>*,.uk-grid-large>*{padding-left:70px}*+.uk-grid-margin-large,.uk-grid+.uk-grid-large,.uk-grid+.uk-grid-row-large,.uk-grid-large>.uk-grid-margin,.uk-grid-row-large>.uk-grid-margin{margin-top:70px}}.uk-grid-collapse,.uk-grid-column-collapse{margin-left:0}.uk-grid-collapse>*,.uk-grid-column-collapse>*{padding-left:0}.uk-grid+.uk-grid-collapse,.uk-grid+.uk-grid-row-collapse,.uk-grid-collapse>.uk-grid-margin,.uk-grid-row-collapse>.uk-grid-margin{margin-top:0}.uk-grid-divider>*{position:relative}.uk-grid-divider>:not(.uk-first-column)::before{content:"";position:absolute;top:0;bottom:0;border-left:1px solid #e5e5e5}.uk-grid-divider.uk-grid-stack>.uk-grid-margin::before{content:"";position:absolute;left:0;right:0;border-top:1px solid #e5e5e5}.uk-grid-divider{margin-left:-60px}.uk-grid-divider>*{padding-left:60px}.uk-grid-divider>:not(.uk-first-column)::before{left:30px}.uk-grid-divider.uk-grid-stack>.uk-grid-margin{margin-top:60px}.uk-grid-divider.uk-grid-stack>.uk-grid-margin::before{top:-30px;left:60px}@media (min-width:1200px){.uk-grid-divider{margin-left:-80px}.uk-grid-divider>*{padding-left:80px}.uk-grid-divider>:not(.uk-first-column)::before{left:40px}.uk-grid-divider.uk-grid-stack>.uk-grid-margin{margin-top:80px}.uk-grid-divider.uk-grid-stack>.uk-grid-margin::before{top:-40px;left:80px}}.uk-grid-divider.uk-grid-column-small,.uk-grid-divider.uk-grid-small{margin-left:-30px}.uk-grid-divider.uk-grid-column-small>*,.uk-grid-divider.uk-grid-small>*{padding-left:30px}.uk-grid-divider.uk-grid-column-small>:not(.uk-first-column)::before,.uk-grid-divider.uk-grid-small>:not(.uk-first-column)::before{left:15px}.uk-grid-divider.uk-grid-row-small.uk-grid-stack>.uk-grid-margin,.uk-grid-divider.uk-grid-small.uk-grid-stack>.uk-grid-margin{margin-top:30px}.uk-grid-divider.uk-grid-small.uk-grid-stack>.uk-grid-margin::before{top:-15px;left:30px}.uk-grid-divider.uk-grid-row-small.uk-grid-stack>.uk-grid-margin::before{top:-15px}.uk-grid-divider.uk-grid-column-small.uk-grid-stack>.uk-grid-margin::before{left:30px}.uk-grid-divider.uk-grid-column-medium,.uk-grid-divider.uk-grid-medium{margin-left:-60px}.uk-grid-divider.uk-grid-column-medium>*,.uk-grid-divider.uk-grid-medium>*{padding-left:60px}.uk-grid-divider.uk-grid-column-medium>:not(.uk-first-column)::before,.uk-grid-divider.uk-grid-medium>:not(.uk-first-column)::before{left:30px}.uk-grid-divider.uk-grid-medium.uk-grid-stack>.uk-grid-margin,.uk-grid-divider.uk-grid-row-medium.uk-grid-stack>.uk-grid-margin{margin-top:60px}.uk-grid-divider.uk-grid-medium.uk-grid-stack>.uk-grid-margin::before{top:-30px;left:60px}.uk-grid-divider.uk-grid-row-medium.uk-grid-stack>.uk-grid-margin::before{top:-30px}.uk-grid-divider.uk-grid-column-medium.uk-grid-stack>.uk-grid-margin::before{left:60px}.uk-grid-divider.uk-grid-column-large,.uk-grid-divider.uk-grid-large{margin-left:-80px}.uk-grid-divider.uk-grid-column-large>*,.uk-grid-divider.uk-grid-large>*{padding-left:80px}.uk-grid-divider.uk-grid-column-large>:not(.uk-first-column)::before,.uk-grid-divider.uk-grid-large>:not(.uk-first-column)::before{left:40px}.uk-grid-divider.uk-grid-large.uk-grid-stack>.uk-grid-margin,.uk-grid-divider.uk-grid-row-large.uk-grid-stack>.uk-grid-margin{margin-top:80px}.uk-grid-divider.uk-grid-large.uk-grid-stack>.uk-grid-margin::before{top:-40px;left:80px}.uk-grid-divider.uk-grid-row-large.uk-grid-stack>.uk-grid-margin::before{top:-40px}.uk-grid-divider.uk-grid-column-large.uk-grid-stack>.uk-grid-margin::before{left:80px}@media (min-width:1200px){.uk-grid-divider.uk-grid-column-large,.uk-grid-divider.uk-grid-large{margin-left:-140px}.uk-grid-divider.uk-grid-column-large>*,.uk-grid-divider.uk-grid-large>*{padding-left:140px}.uk-grid-divider.uk-grid-column-large>:not(.uk-first-column)::before,.uk-grid-divider.uk-grid-large>:not(.uk-first-column)::before{left:70px}.uk-grid-divider.uk-grid-large.uk-grid-stack>.uk-grid-margin,.uk-grid-divider.uk-grid-row-large.uk-grid-stack>.uk-grid-margin{margin-top:140px}.uk-grid-divider.uk-grid-large.uk-grid-stack>.uk-grid-margin::before{top:-70px;left:140px}.uk-grid-divider.uk-grid-row-large.uk-grid-stack>.uk-grid-margin::before{top:-70px}.uk-grid-divider.uk-grid-column-large.uk-grid-stack>.uk-grid-margin::before{left:140px}}.uk-grid-item-match,.uk-grid-match>*{display:flex;flex-wrap:wrap}.uk-grid-item-match>:not([class*=uk-width]),.uk-grid-match>*>:not([class*=uk-width]){box-sizing:border-box;width:100%;flex:auto}.uk-nav,.uk-nav ul{margin:0;padding:0;list-style:none}.uk-nav li>a{display:block;text-decoration:none}.uk-nav li>a:focus{outline:0}.uk-nav>li>a{padding:5px 0}ul.uk-nav-sub{padding:5px 0 5px 15px}.uk-nav-sub ul{padding-left:15px}.uk-nav-sub a{padding:2px 0}.uk-nav-parent-icon>.uk-parent>a::after{content:"";width:1.5em;height:1.5em;float:right;background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22%23666%22%20stroke-width%3D%221.1%22%20points%3D%2210%201%204%207%2010%2013%22%20%2F%3E%0A%3C%2Fsvg%3E");background-repeat:no-repeat;background-position:50% 50%}.uk-nav-parent-icon>.uk-parent.uk-open>a::after{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22%23666%22%20stroke-width%3D%221.1%22%20points%3D%221%204%207%2010%2013%204%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-nav-header{padding:5px 0;text-transform:uppercase;font-size:.875rem}.uk-nav-header:not(:first-child){margin-top:20px}.uk-nav>.uk-nav-divider{margin:5px 0}.uk-nav-default{font-size:.875rem}.uk-nav-default>li>a{color:#999}.uk-nav-default>li>a:focus,.uk-nav-default>li>a:hover{color:#666}.uk-nav-default>li.uk-active>a{color:#333}.uk-nav-default .uk-nav-header{color:#333}.uk-nav-default .uk-nav-divider{border-top:1px solid #e5e5e5}.uk-nav-default .uk-nav-sub a{color:#999}.uk-nav-default .uk-nav-sub a:focus,.uk-nav-default .uk-nav-sub a:hover{color:#666}.uk-nav-default .uk-nav-sub li.uk-active>a{color:#333}.uk-nav-primary>li>a{font-size:1.5rem;line-height:1.5;color:#999}.uk-nav-primary>li>a:focus,.uk-nav-primary>li>a:hover{color:#666}.uk-nav-primary>li.uk-active>a{color:#333}.uk-nav-primary .uk-nav-header{color:#333}.uk-nav-primary .uk-nav-divider{border-top:1px solid #e5e5e5}.uk-nav-primary .uk-nav-sub a{color:#999}.uk-nav-primary .uk-nav-sub a:focus,.uk-nav-primary .uk-nav-sub a:hover{color:#666}.uk-nav-primary .uk-nav-sub li.uk-active>a{color:#333}.uk-nav-center{text-align:center}.uk-nav-center .uk-nav-sub,.uk-nav-center .uk-nav-sub ul{padding-left:0}.uk-nav-center.uk-nav-parent-icon>.uk-parent>a::after{position:absolute}.uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider){margin-top:0;padding-top:0;border-top:1px solid #e5e5e5}.uk-navbar{display:flex;position:relative}.uk-navbar-container:not(.uk-navbar-transparent){background:#f8f8f8}.uk-navbar-container>::after,.uk-navbar-container>::before{display:none!important}.uk-navbar-center,.uk-navbar-center-left>*,.uk-navbar-center-right>*,.uk-navbar-left,.uk-navbar-right{display:flex;align-items:center}.uk-navbar-right{margin-left:auto}.uk-navbar-center:only-child{margin-left:auto;margin-right:auto;position:relative}.uk-navbar-center:not(:only-child){position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:max-content;box-sizing:border-box;z-index:990}.uk-navbar-center-left,.uk-navbar-center-right{position:absolute;top:0}.uk-navbar-center-left{right:100%}.uk-navbar-center-right{left:100%}[class*=uk-navbar-center-]{width:max-content;box-sizing:border-box}.uk-navbar-nav{display:flex;margin:0;padding:0;list-style:none}.uk-navbar-center:only-child,.uk-navbar-left,.uk-navbar-right{flex-wrap:wrap}.uk-navbar-item,.uk-navbar-nav>li>a,.uk-navbar-toggle{display:flex;justify-content:center;align-items:center;box-sizing:border-box;min-height:80px;padding:0 15px;font-size:.875rem;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";text-decoration:none}.uk-navbar-nav>li>a{color:#999;text-transform:uppercase;transition:.1s ease-in-out;transition-property:color,background-color}.uk-navbar-nav>li:hover>a,.uk-navbar-nav>li>a.uk-open,.uk-navbar-nav>li>a:focus{color:#666;outline:0}.uk-navbar-nav>li>a:active{color:#333}.uk-navbar-nav>li.uk-active>a{color:#333}.uk-navbar-item{color:#666}.uk-navbar-toggle{color:#999}.uk-navbar-toggle.uk-open,.uk-navbar-toggle:focus,.uk-navbar-toggle:hover{color:#666;outline:0;text-decoration:none}.uk-navbar-subtitle{font-size:.875rem}.uk-navbar-dropdown{display:none;position:absolute;z-index:1020;box-sizing:border-box;width:200px;padding:25px;background:#fff;color:#666;box-shadow:0 5px 12px rgba(0,0,0,.15)}.uk-navbar-dropdown.uk-open{display:block}[class*=uk-navbar-dropdown-top]{margin-top:-15px}[class*=uk-navbar-dropdown-bottom]{margin-top:15px}[class*=uk-navbar-dropdown-left]{margin-left:-15px}[class*=uk-navbar-dropdown-right]{margin-left:15px}.uk-navbar-dropdown-grid{margin-left:-50px}.uk-navbar-dropdown-grid>*{padding-left:50px}.uk-navbar-dropdown-grid>.uk-grid-margin{margin-top:50px}.uk-navbar-dropdown-stack .uk-navbar-dropdown-grid>*{width:100%!important}.uk-navbar-dropdown-width-2:not(.uk-navbar-dropdown-stack){width:400px}.uk-navbar-dropdown-width-3:not(.uk-navbar-dropdown-stack){width:600px}.uk-navbar-dropdown-width-4:not(.uk-navbar-dropdown-stack){width:800px}.uk-navbar-dropdown-width-5:not(.uk-navbar-dropdown-stack){width:1000px}.uk-navbar-dropdown-dropbar{margin-top:0;margin-bottom:0;box-shadow:none}.uk-navbar-dropdown-nav{font-size:.875rem}.uk-navbar-dropdown-nav>li>a{color:#999}.uk-navbar-dropdown-nav>li>a:focus,.uk-navbar-dropdown-nav>li>a:hover{color:#666}.uk-navbar-dropdown-nav>li.uk-active>a{color:#333}.uk-navbar-dropdown-nav .uk-nav-header{color:#333}.uk-navbar-dropdown-nav .uk-nav-divider{border-top:1px solid #e5e5e5}.uk-navbar-dropdown-nav .uk-nav-sub a{color:#999}.uk-navbar-dropdown-nav .uk-nav-sub a:focus,.uk-navbar-dropdown-nav .uk-nav-sub a:hover{color:#666}.uk-navbar-dropdown-nav .uk-nav-sub li.uk-active>a{color:#333}.uk-navbar-dropbar{background:#fff}.uk-navbar-dropbar-slide{position:absolute;z-index:980;left:0;right:0;box-shadow:0 5px 7px rgba(0,0,0,.05)}.uk-navbar-container>.uk-container .uk-navbar-left{margin-left:-15px;margin-right:-15px}.uk-navbar-container>.uk-container .uk-navbar-right{margin-right:-15px}.uk-navbar-dropdown-grid>*{position:relative}.uk-navbar-dropdown-grid>:not(.uk-first-column)::before{content:"";position:absolute;top:0;bottom:0;left:25px;border-left:1px solid #e5e5e5}.uk-navbar-dropdown-grid.uk-grid-stack>.uk-grid-margin::before{content:"";position:absolute;top:-25px;left:50px;right:0;border-top:1px solid #e5e5e5}.uk-subnav{display:flex;flex-wrap:wrap;margin-left:-20px;padding:0;list-style:none}.uk-subnav>*{flex:none;padding-left:20px;position:relative}.uk-subnav>*>:first-child{display:block;color:#999;font-size:.875rem;text-transform:uppercase;transition:.1s ease-in-out;transition-property:color,background-color}.uk-subnav>*>a:focus,.uk-subnav>*>a:hover{color:#666;text-decoration:none;outline:0}.uk-subnav>.uk-active>a{color:#333}.uk-subnav-divider{margin-left:-41px}.uk-subnav-divider>*{display:flex;align-items:center}.uk-subnav-divider>::before{content:"";height:1.5em;margin-left:0;margin-right:20px;border-left:1px solid transparent}.uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before{border-left-color:#e5e5e5}.uk-subnav-pill>*>:first-child{padding:5px 10px;background:0 0;color:#999}.uk-subnav-pill>*>a:focus,.uk-subnav-pill>*>a:hover{background-color:#f8f8f8;color:#666}.uk-subnav-pill>*>a:active{background-color:#f8f8f8;color:#666}.uk-subnav-pill>.uk-active>a{background-color:#1e87f0;color:#fff}.uk-subnav>.uk-disabled>a{color:#999}.uk-breadcrumb{padding:0;list-style:none}.uk-breadcrumb>*{display:contents}.uk-breadcrumb>*>*{font-size:.875rem;color:#999}.uk-breadcrumb>*>:focus,.uk-breadcrumb>*>:hover{color:#666;text-decoration:none}.uk-breadcrumb>:last-child>a:not([href]),.uk-breadcrumb>:last-child>span{color:#666}.uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before{content:"/";display:inline-block;margin:0 20px 0 calc(20px - 4px);font-size:.875rem;color:#999}.uk-pagination{display:flex;flex-wrap:wrap;margin-left:0;padding:0;list-style:none}.uk-pagination>*{flex:none;padding-left:0;position:relative}.uk-pagination>*>*{display:block;padding:5px 10px;color:#999;transition:color .1s ease-in-out}.uk-pagination>*>:focus,.uk-pagination>*>:hover{color:#666;text-decoration:none}.uk-pagination>.uk-active>*{color:#666}.uk-pagination>.uk-disabled>*{color:#999}.uk-tab{display:flex;flex-wrap:wrap;margin-left:-20px;padding:0;list-style:none;position:relative}.uk-tab::before{content:"";position:absolute;bottom:0;left:20px;right:0;border-bottom:1px solid #e5e5e5}.uk-tab>*{flex:none;padding-left:20px;position:relative}.uk-tab>*>a{display:block;text-align:center;padding:5px 10px;color:#999;border-bottom:1px solid transparent;font-size:.875rem;text-transform:uppercase;transition:color .1s ease-in-out}.uk-tab>*>a:focus,.uk-tab>*>a:hover{color:#666;text-decoration:none}.uk-tab>.uk-active>a{color:#333;border-color:#1e87f0}.uk-tab>.uk-disabled>a{color:#999}.uk-tab-bottom::before{top:0;bottom:auto}.uk-tab-bottom>*>a{border-top:1px solid transparent;border-bottom:none}.uk-tab-left,.uk-tab-right{flex-direction:column;margin-left:0}.uk-tab-left>*,.uk-tab-right>*{padding-left:0}.uk-tab-left::before{top:0;bottom:0;left:auto;right:0;border-left:1px solid #e5e5e5;border-bottom:none}.uk-tab-right::before{top:0;bottom:0;left:0;right:auto;border-left:1px solid #e5e5e5;border-bottom:none}.uk-tab-left>*>a{text-align:left;border-right:1px solid transparent;border-bottom:none}.uk-tab-right>*>a{text-align:left;border-left:1px solid transparent;border-bottom:none}.uk-tab .uk-dropdown{margin-left:30px}.uk-slidenav{padding:5px 10px;color:rgba(102,102,102,.5);transition:color .1s ease-in-out}.uk-slidenav:focus,.uk-slidenav:hover{color:rgba(102,102,102,.9);outline:0}.uk-slidenav:active{color:rgba(102,102,102,.5)}.uk-slidenav-large{padding:10px 10px}.uk-slidenav-container{display:flex}.uk-dotnav{display:flex;flex-wrap:wrap;margin:0;padding:0;list-style:none;margin-left:-12px}.uk-dotnav>*{flex:none;padding-left:12px}.uk-dotnav>*>*{display:block;box-sizing:border-box;width:10px;height:10px;border-radius:50%;background:0 0;text-indent:100%;overflow:hidden;white-space:nowrap;border:1px solid rgba(102,102,102,.4);transition:.2s ease-in-out;transition-property:background-color,border-color}.uk-dotnav>*>:focus,.uk-dotnav>*>:hover{background-color:rgba(102,102,102,.6);outline:0;border-color:transparent}.uk-dotnav>*>:active{background-color:rgba(102,102,102,.2);border-color:transparent}.uk-dotnav>.uk-active>*{background-color:rgba(102,102,102,.6);border-color:transparent}.uk-dotnav-vertical{flex-direction:column;margin-left:0;margin-top:-12px}.uk-dotnav-vertical>*{padding-left:0;padding-top:12px}.uk-thumbnav{display:flex;flex-wrap:wrap;margin:0;padding:0;list-style:none;margin-left:-15px}.uk-thumbnav>*{padding-left:15px}.uk-thumbnav>*>*{display:inline-block;position:relative}.uk-thumbnav>*>::after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background:rgba(255,255,255,.4);transition:background-color .1s ease-in-out}.uk-thumbnav>*>:focus,.uk-thumbnav>*>:hover{outline:0}.uk-thumbnav>*>:focus::after,.uk-thumbnav>*>:hover::after{background-color:transparent}.uk-thumbnav>.uk-active>::after{background-color:transparent}.uk-thumbnav-vertical{flex-direction:column;margin-left:0;margin-top:-15px}.uk-thumbnav-vertical>*{padding-left:0;padding-top:15px}.uk-iconnav{display:flex;flex-wrap:wrap;margin:0;padding:0;list-style:none;margin-left:-10px}.uk-iconnav>*{padding-left:10px}.uk-iconnav>*>a{display:block;color:#999}.uk-iconnav>*>a:focus,.uk-iconnav>*>a:hover{color:#666;outline:0}.uk-iconnav>.uk-active>a{color:#666}.uk-iconnav-vertical{flex-direction:column;margin-left:0;margin-top:-10px}.uk-iconnav-vertical>*{padding-left:0;padding-top:10px}.uk-lightbox{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1010;background:#000;opacity:0;transition:opacity .15s linear;touch-action:pinch-zoom}.uk-lightbox.uk-open{display:block;opacity:1}.uk-lightbox-page{overflow:hidden}.uk-lightbox-items>*{position:absolute;top:0;right:0;bottom:0;left:0;display:none;justify-content:center;align-items:center;color:rgba(255,255,255,.7);will-change:transform,opacity}.uk-lightbox-items>*>*{max-width:100vw;max-height:100vh}.uk-lightbox-items>:focus{outline:0}.uk-lightbox-items>*>:not(iframe){width:auto;height:auto}.uk-lightbox-items>.uk-active{display:flex}.uk-lightbox-toolbar{padding:10px 10px;background:rgba(0,0,0,.3);color:rgba(255,255,255,.7)}.uk-lightbox-toolbar>*{color:rgba(255,255,255,.7)}.uk-lightbox-toolbar-icon{padding:5px;color:rgba(255,255,255,.7)}.uk-lightbox-toolbar-icon:hover{color:#fff}.uk-lightbox-button{box-sizing:border-box;width:50px;height:50px;background:rgba(0,0,0,.3);color:rgba(255,255,255,.7);display:inline-flex;justify-content:center;align-items:center}.uk-lightbox-button:focus,.uk-lightbox-button:hover{color:#fff}.uk-lightbox-caption:empty{display:none}.uk-lightbox-iframe{width:80%;height:80%}[class*=uk-animation-]{animation-duration:.5s;animation-timing-function:ease-out;animation-fill-mode:both}.uk-animation-fade{animation-name:uk-fade;animation-duration:.8s;animation-timing-function:linear}.uk-animation-scale-up{animation-name:uk-fade-scale-02}.uk-animation-scale-down{animation-name:uk-fade-scale-18}.uk-animation-slide-top{animation-name:uk-fade-top}.uk-animation-slide-bottom{animation-name:uk-fade-bottom}.uk-animation-slide-left{animation-name:uk-fade-left}.uk-animation-slide-right{animation-name:uk-fade-right}.uk-animation-slide-top-small{animation-name:uk-fade-top-small}.uk-animation-slide-bottom-small{animation-name:uk-fade-bottom-small}.uk-animation-slide-left-small{animation-name:uk-fade-left-small}.uk-animation-slide-right-small{animation-name:uk-fade-right-small}.uk-animation-slide-top-medium{animation-name:uk-fade-top-medium}.uk-animation-slide-bottom-medium{animation-name:uk-fade-bottom-medium}.uk-animation-slide-left-medium{animation-name:uk-fade-left-medium}.uk-animation-slide-right-medium{animation-name:uk-fade-right-medium}.uk-animation-kenburns{animation-name:uk-scale-kenburns;animation-duration:15s}.uk-animation-shake{animation-name:uk-shake}.uk-animation-stroke{animation-name:uk-stroke;stroke-dasharray:var(--uk-animation-stroke);animation-duration:2s}.uk-animation-reverse{animation-direction:reverse;animation-timing-function:ease-in}.uk-animation-fast{animation-duration:.1s}.uk-animation-toggle:not(:hover):not(:focus) [class*=uk-animation-]{animation-name:none}.uk-animation-toggle{-webkit-tap-highlight-color:transparent}.uk-animation-toggle:focus{outline:0}@keyframes uk-fade{0%{opacity:0}100%{opacity:1}}@keyframes uk-fade-top{0%{opacity:0;transform:translateY(-100%)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-bottom{0%{opacity:0;transform:translateY(100%)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-left{0%{opacity:0;transform:translateX(-100%)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-right{0%{opacity:0;transform:translateX(100%)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-top-small{0%{opacity:0;transform:translateY(-10px)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-bottom-small{0%{opacity:0;transform:translateY(10px)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-left-small{0%{opacity:0;transform:translateX(-10px)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-right-small{0%{opacity:0;transform:translateX(10px)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-top-medium{0%{opacity:0;transform:translateY(-50px)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-bottom-medium{0%{opacity:0;transform:translateY(50px)}100%{opacity:1;transform:translateY(0)}}@keyframes uk-fade-left-medium{0%{opacity:0;transform:translateX(-50px)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-right-medium{0%{opacity:0;transform:translateX(50px)}100%{opacity:1;transform:translateX(0)}}@keyframes uk-fade-scale-02{0%{opacity:0;transform:scale(.2)}100%{opacity:1;transform:scale(1)}}@keyframes uk-fade-scale-18{0%{opacity:0;transform:scale(1.8)}100%{opacity:1;transform:scale(1)}}@keyframes uk-scale-kenburns{0%{transform:scale(1)}100%{transform:scale(1.2)}}@keyframes uk-shake{0%,100%{transform:translateX(0)}10%{transform:translateX(-9px)}20%{transform:translateX(8px)}30%{transform:translateX(-7px)}40%{transform:translateX(6px)}50%{transform:translateX(-5px)}60%{transform:translateX(4px)}70%{transform:translateX(-3px)}80%{transform:translateX(2px)}90%{transform:translateX(-1px)}}@keyframes uk-stroke{0%{stroke-dashoffset:var(--uk-animation-stroke)}100%{stroke-dashoffset:0}}[class*=uk-child-width]>*{box-sizing:border-box;width:100%}.uk-child-width-1-2>*{width:50%}.uk-child-width-1-3>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4>*{width:25%}.uk-child-width-1-5>*{width:20%}.uk-child-width-1-6>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto>*{width:auto}.uk-child-width-expand>:not([class*=uk-width]){flex:1;min-width:1px}@media (min-width:640px){.uk-child-width-1-1\@s>*{width:100%}.uk-child-width-1-2\@s>*{width:50%}.uk-child-width-1-3\@s>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4\@s>*{width:25%}.uk-child-width-1-5\@s>*{width:20%}.uk-child-width-1-6\@s>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto\@s>*{width:auto}.uk-child-width-expand\@s>:not([class*=uk-width]){flex:1;min-width:1px}}@media (min-width:960px){.uk-child-width-1-1\@m>*{width:100%}.uk-child-width-1-2\@m>*{width:50%}.uk-child-width-1-3\@m>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4\@m>*{width:25%}.uk-child-width-1-5\@m>*{width:20%}.uk-child-width-1-6\@m>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto\@m>*{width:auto}.uk-child-width-expand\@m>:not([class*=uk-width]){flex:1;min-width:1px}}@media (min-width:1200px){.uk-child-width-1-1\@l>*{width:100%}.uk-child-width-1-2\@l>*{width:50%}.uk-child-width-1-3\@l>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4\@l>*{width:25%}.uk-child-width-1-5\@l>*{width:20%}.uk-child-width-1-6\@l>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto\@l>*{width:auto}.uk-child-width-expand\@l>:not([class*=uk-width]){flex:1;min-width:1px}}@media (min-width:1600px){.uk-child-width-1-1\@xl>*{width:100%}.uk-child-width-1-2\@xl>*{width:50%}.uk-child-width-1-3\@xl>*{width:calc(100% * 1 / 3.001)}.uk-child-width-1-4\@xl>*{width:25%}.uk-child-width-1-5\@xl>*{width:20%}.uk-child-width-1-6\@xl>*{width:calc(100% * 1 / 6.001)}.uk-child-width-auto\@xl>*{width:auto}.uk-child-width-expand\@xl>:not([class*=uk-width]){flex:1;min-width:1px}}[class*=uk-width]{box-sizing:border-box;width:100%;max-width:100%}.uk-width-1-2{width:50%}.uk-width-1-3{width:calc(100% * 1 / 3.001)}.uk-width-2-3{width:calc(100% * 2 / 3.001)}.uk-width-1-4{width:25%}.uk-width-3-4{width:75%}.uk-width-1-5{width:20%}.uk-width-2-5{width:40%}.uk-width-3-5{width:60%}.uk-width-4-5{width:80%}.uk-width-1-6{width:calc(100% * 1 / 6.001)}.uk-width-5-6{width:calc(100% * 5 / 6.001)}.uk-width-small{width:150px}.uk-width-medium{width:300px}.uk-width-large{width:450px}.uk-width-xlarge{width:600px}.uk-width-2xlarge{width:750px}.uk-width-auto{width:auto}.uk-width-expand{flex:1;min-width:1px}@media (min-width:640px){.uk-width-1-1\@s{width:100%}.uk-width-1-2\@s{width:50%}.uk-width-1-3\@s{width:calc(100% * 1 / 3.001)}.uk-width-2-3\@s{width:calc(100% * 2 / 3.001)}.uk-width-1-4\@s{width:25%}.uk-width-3-4\@s{width:75%}.uk-width-1-5\@s{width:20%}.uk-width-2-5\@s{width:40%}.uk-width-3-5\@s{width:60%}.uk-width-4-5\@s{width:80%}.uk-width-1-6\@s{width:calc(100% * 1 / 6.001)}.uk-width-5-6\@s{width:calc(100% * 5 / 6.001)}.uk-width-small\@s{width:150px}.uk-width-medium\@s{width:300px}.uk-width-large\@s{width:450px}.uk-width-xlarge\@s{width:600px}.uk-width-2xlarge\@s{width:750px}.uk-width-auto\@s{width:auto}.uk-width-expand\@s{flex:1;min-width:1px}}@media (min-width:960px){.uk-width-1-1\@m{width:100%}.uk-width-1-2\@m{width:50%}.uk-width-1-3\@m{width:calc(100% * 1 / 3.001)}.uk-width-2-3\@m{width:calc(100% * 2 / 3.001)}.uk-width-1-4\@m{width:25%}.uk-width-3-4\@m{width:75%}.uk-width-1-5\@m{width:20%}.uk-width-2-5\@m{width:40%}.uk-width-3-5\@m{width:60%}.uk-width-4-5\@m{width:80%}.uk-width-1-6\@m{width:calc(100% * 1 / 6.001)}.uk-width-5-6\@m{width:calc(100% * 5 / 6.001)}.uk-width-small\@m{width:150px}.uk-width-medium\@m{width:300px}.uk-width-large\@m{width:450px}.uk-width-xlarge\@m{width:600px}.uk-width-2xlarge\@m{width:750px}.uk-width-auto\@m{width:auto}.uk-width-expand\@m{flex:1;min-width:1px}}@media (min-width:1200px){.uk-width-1-1\@l{width:100%}.uk-width-1-2\@l{width:50%}.uk-width-1-3\@l{width:calc(100% * 1 / 3.001)}.uk-width-2-3\@l{width:calc(100% * 2 / 3.001)}.uk-width-1-4\@l{width:25%}.uk-width-3-4\@l{width:75%}.uk-width-1-5\@l{width:20%}.uk-width-2-5\@l{width:40%}.uk-width-3-5\@l{width:60%}.uk-width-4-5\@l{width:80%}.uk-width-1-6\@l{width:calc(100% * 1 / 6.001)}.uk-width-5-6\@l{width:calc(100% * 5 / 6.001)}.uk-width-small\@l{width:150px}.uk-width-medium\@l{width:300px}.uk-width-large\@l{width:450px}.uk-width-xlarge\@l{width:600px}.uk-width-2xlarge\@l{width:750px}.uk-width-auto\@l{width:auto}.uk-width-expand\@l{flex:1;min-width:1px}}@media (min-width:1600px){.uk-width-1-1\@xl{width:100%}.uk-width-1-2\@xl{width:50%}.uk-width-1-3\@xl{width:calc(100% * 1 / 3.001)}.uk-width-2-3\@xl{width:calc(100% * 2 / 3.001)}.uk-width-1-4\@xl{width:25%}.uk-width-3-4\@xl{width:75%}.uk-width-1-5\@xl{width:20%}.uk-width-2-5\@xl{width:40%}.uk-width-3-5\@xl{width:60%}.uk-width-4-5\@xl{width:80%}.uk-width-1-6\@xl{width:calc(100% * 1 / 6.001)}.uk-width-5-6\@xl{width:calc(100% * 5 / 6.001)}.uk-width-small\@xl{width:150px}.uk-width-medium\@xl{width:300px}.uk-width-large\@xl{width:450px}.uk-width-xlarge\@xl{width:600px}.uk-width-2xlarge\@xl{width:750px}.uk-width-auto\@xl{width:auto}.uk-width-expand\@xl{flex:1;min-width:1px}}[class*=uk-height]{box-sizing:border-box}.uk-height-1-1{height:100%}.uk-height-viewport{min-height:100vh}.uk-height-small{height:150px}.uk-height-medium{height:300px}.uk-height-large{height:450px}.uk-height-max-small{max-height:150px}.uk-height-max-medium{max-height:300px}.uk-height-max-large{max-height:450px}.uk-text-lead{font-size:1.5rem;line-height:1.5;color:#333}.uk-text-meta{font-size:.875rem;line-height:1.4;color:#999}.uk-text-meta a{color:#999}.uk-text-meta a:hover{color:#666;text-decoration:none}.uk-text-small{font-size:.875rem;line-height:1.5}.uk-text-large{font-size:1.5rem;line-height:1.5}.uk-text-default{font-size:16px;line-height:1.5}.uk-text-light{font-weight:300}.uk-text-normal{font-weight:400}.uk-text-bold{font-weight:700}.uk-text-lighter{font-weight:lighter}.uk-text-bolder{font-weight:bolder}.uk-text-italic{font-style:italic}.uk-text-capitalize{text-transform:capitalize!important}.uk-text-uppercase{text-transform:uppercase!important}.uk-text-lowercase{text-transform:lowercase!important}.uk-text-muted{color:#999!important}.uk-text-emphasis{color:#333!important}.uk-text-primary{color:#1e87f0!important}.uk-text-secondary{color:#222!important}.uk-text-success{color:#32d296!important}.uk-text-warning{color:#faa05a!important}.uk-text-danger{color:#f0506e!important}.uk-text-background{-webkit-background-clip:text;display:inline-block;color:#1e87f0!important}@supports (-webkit-background-clip:text){.uk-text-background{background-color:#1e87f0;color:transparent!important}}.uk-text-left{text-align:left!important}.uk-text-right{text-align:right!important}.uk-text-center{text-align:center!important}.uk-text-justify{text-align:justify!important}@media (min-width:640px){.uk-text-left\@s{text-align:left!important}.uk-text-right\@s{text-align:right!important}.uk-text-center\@s{text-align:center!important}}@media (min-width:960px){.uk-text-left\@m{text-align:left!important}.uk-text-right\@m{text-align:right!important}.uk-text-center\@m{text-align:center!important}}@media (min-width:1200px){.uk-text-left\@l{text-align:left!important}.uk-text-right\@l{text-align:right!important}.uk-text-center\@l{text-align:center!important}}@media (min-width:1600px){.uk-text-left\@xl{text-align:left!important}.uk-text-right\@xl{text-align:right!important}.uk-text-center\@xl{text-align:center!important}}.uk-text-top{vertical-align:top!important}.uk-text-middle{vertical-align:middle!important}.uk-text-bottom{vertical-align:bottom!important}.uk-text-baseline{vertical-align:baseline!important}.uk-text-nowrap{white-space:nowrap}.uk-text-truncate{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}td.uk-text-truncate,th.uk-text-truncate{max-width:0}.uk-text-break{overflow-wrap:break-word;word-wrap:break-word}td.uk-text-break,th.uk-text-break{word-break:break-all}[class*=uk-column-]{column-gap:30px}@media (min-width:1200px){[class*=uk-column-]{column-gap:40px}}[class*=uk-column-] img{transform:translate3d(0,0,0)}.uk-column-divider{column-rule:1px solid #e5e5e5;column-gap:60px}@media (min-width:1200px){.uk-column-divider{column-gap:80px}}.uk-column-1-2{column-count:2}.uk-column-1-3{column-count:3}.uk-column-1-4{column-count:4}.uk-column-1-5{column-count:5}.uk-column-1-6{column-count:6}@media (min-width:640px){.uk-column-1-2\@s{column-count:2}.uk-column-1-3\@s{column-count:3}.uk-column-1-4\@s{column-count:4}.uk-column-1-5\@s{column-count:5}.uk-column-1-6\@s{column-count:6}}@media (min-width:960px){.uk-column-1-2\@m{column-count:2}.uk-column-1-3\@m{column-count:3}.uk-column-1-4\@m{column-count:4}.uk-column-1-5\@m{column-count:5}.uk-column-1-6\@m{column-count:6}}@media (min-width:1200px){.uk-column-1-2\@l{column-count:2}.uk-column-1-3\@l{column-count:3}.uk-column-1-4\@l{column-count:4}.uk-column-1-5\@l{column-count:5}.uk-column-1-6\@l{column-count:6}}@media (min-width:1600px){.uk-column-1-2\@xl{column-count:2}.uk-column-1-3\@xl{column-count:3}.uk-column-1-4\@xl{column-count:4}.uk-column-1-5\@xl{column-count:5}.uk-column-1-6\@xl{column-count:6}}.uk-column-span{column-span:all}.uk-cover{max-width:none;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}iframe.uk-cover{pointer-events:none}.uk-cover-container{overflow:hidden;position:relative}.uk-background-default{background-color:#fff}.uk-background-muted{background-color:#f8f8f8}.uk-background-primary{background-color:#1e87f0}.uk-background-secondary{background-color:#222}.uk-background-contain,.uk-background-cover,.uk-background-height-1-1,.uk-background-width-1-1{background-position:50% 50%;background-repeat:no-repeat}.uk-background-cover{background-size:cover}.uk-background-contain{background-size:contain}.uk-background-width-1-1{background-size:100%}.uk-background-height-1-1{background-size:auto 100%}.uk-background-top-left{background-position:0 0}.uk-background-top-center{background-position:50% 0}.uk-background-top-right{background-position:100% 0}.uk-background-center-left{background-position:0 50%}.uk-background-center-center{background-position:50% 50%}.uk-background-center-right{background-position:100% 50%}.uk-background-bottom-left{background-position:0 100%}.uk-background-bottom-center{background-position:50% 100%}.uk-background-bottom-right{background-position:100% 100%}.uk-background-norepeat{background-repeat:no-repeat}.uk-background-fixed{background-attachment:fixed;backface-visibility:hidden}@media (pointer:coarse){.uk-background-fixed{background-attachment:scroll}}@media (max-width:639px){.uk-background-image\@s{background-image:none!important}}@media (max-width:959px){.uk-background-image\@m{background-image:none!important}}@media (max-width:1199px){.uk-background-image\@l{background-image:none!important}}@media (max-width:1599px){.uk-background-image\@xl{background-image:none!important}}.uk-background-blend-multiply{background-blend-mode:multiply}.uk-background-blend-screen{background-blend-mode:screen}.uk-background-blend-overlay{background-blend-mode:overlay}.uk-background-blend-darken{background-blend-mode:darken}.uk-background-blend-lighten{background-blend-mode:lighten}.uk-background-blend-color-dodge{background-blend-mode:color-dodge}.uk-background-blend-color-burn{background-blend-mode:color-burn}.uk-background-blend-hard-light{background-blend-mode:hard-light}.uk-background-blend-soft-light{background-blend-mode:soft-light}.uk-background-blend-difference{background-blend-mode:difference}.uk-background-blend-exclusion{background-blend-mode:exclusion}.uk-background-blend-hue{background-blend-mode:hue}.uk-background-blend-saturation{background-blend-mode:saturation}.uk-background-blend-color{background-blend-mode:color}.uk-background-blend-luminosity{background-blend-mode:luminosity}[class*=uk-align]{display:block;margin-bottom:30px}*+[class*=uk-align]{margin-top:30px}.uk-align-center{margin-left:auto;margin-right:auto}.uk-align-left{margin-top:0;margin-right:30px;float:left}.uk-align-right{margin-top:0;margin-left:30px;float:right}@media (min-width:640px){.uk-align-left\@s{margin-top:0;margin-right:30px;float:left}.uk-align-right\@s{margin-top:0;margin-left:30px;float:right}}@media (min-width:960px){.uk-align-left\@m{margin-top:0;margin-right:30px;float:left}.uk-align-right\@m{margin-top:0;margin-left:30px;float:right}}@media (min-width:1200px){.uk-align-left\@l{margin-top:0;float:left}.uk-align-right\@l{margin-top:0;float:right}.uk-align-left,.uk-align-left\@l,.uk-align-left\@m,.uk-align-left\@s{margin-right:40px}.uk-align-right,.uk-align-right\@l,.uk-align-right\@m,.uk-align-right\@s{margin-left:40px}}@media (min-width:1600px){.uk-align-left\@xl{margin-top:0;margin-right:40px;float:left}.uk-align-right\@xl{margin-top:0;margin-left:40px;float:right}}.uk-svg,.uk-svg:not(.uk-preserve) [fill*='#']:not(.uk-preserve){fill:currentcolor}.uk-svg:not(.uk-preserve) [stroke*='#']:not(.uk-preserve){stroke:currentcolor}.uk-svg{transform:translate(0,0)}.uk-panel{display:flow-root;position:relative;box-sizing:border-box}.uk-panel>:last-child{margin-bottom:0}.uk-panel-scrollable{height:170px;padding:10px;border:1px solid #e5e5e5;overflow:auto;-webkit-overflow-scrolling:touch;resize:both}.uk-clearfix::before{content:"";display:table-cell}.uk-clearfix::after{content:"";display:table;clear:both}.uk-float-left{float:left}.uk-float-right{float:right}[class*=uk-float-]{max-width:100%}.uk-overflow-hidden{overflow:hidden}.uk-overflow-auto{overflow:auto;-webkit-overflow-scrolling:touch}.uk-overflow-auto>:last-child{margin-bottom:0}.uk-resize{resize:both}.uk-resize-vertical{resize:vertical}.uk-display-block{display:block!important}.uk-display-inline{display:inline!important}.uk-display-inline-block{display:inline-block!important}[class*=uk-inline]{display:inline-block;position:relative;max-width:100%;vertical-align:middle;-webkit-backface-visibility:hidden}.uk-inline-clip{overflow:hidden}.uk-preserve-width,.uk-preserve-width canvas,.uk-preserve-width img,.uk-preserve-width svg,.uk-preserve-width video{max-width:none}.uk-responsive-height,.uk-responsive-width{box-sizing:border-box}.uk-responsive-width{max-width:100%!important;height:auto}.uk-responsive-height{max-height:100%;width:auto;max-width:none}.uk-border-circle{border-radius:50%}.uk-border-pill{border-radius:500px}.uk-border-rounded{border-radius:5px}.uk-inline-clip[class*=uk-border-]{-webkit-transform:translateZ(0)}.uk-box-shadow-small{box-shadow:0 2px 8px rgba(0,0,0,.08)}.uk-box-shadow-medium{box-shadow:0 5px 15px rgba(0,0,0,.08)}.uk-box-shadow-large{box-shadow:0 14px 25px rgba(0,0,0,.16)}.uk-box-shadow-xlarge{box-shadow:0 28px 50px rgba(0,0,0,.16)}[class*=uk-box-shadow-hover]{transition:box-shadow .1s ease-in-out}.uk-box-shadow-hover-small:hover{box-shadow:0 2px 8px rgba(0,0,0,.08)}.uk-box-shadow-hover-medium:hover{box-shadow:0 5px 15px rgba(0,0,0,.08)}.uk-box-shadow-hover-large:hover{box-shadow:0 14px 25px rgba(0,0,0,.16)}.uk-box-shadow-hover-xlarge:hover{box-shadow:0 28px 50px rgba(0,0,0,.16)}@supports (filter:blur(0)){.uk-box-shadow-bottom{display:inline-block;position:relative;z-index:0;max-width:100%;vertical-align:middle}.uk-box-shadow-bottom::after{content:'';position:absolute;bottom:-30px;left:0;right:0;z-index:-1;height:30px;border-radius:100%;background:#444;filter:blur(20px);will-change:filter}}.uk-dropcap::first-letter,.uk-dropcap>p:first-of-type::first-letter{display:block;margin-right:10px;float:left;font-size:4.5em;line-height:1;margin-bottom:-2px}@-moz-document url-prefix(){.uk-dropcap::first-letter,.uk-dropcap>p:first-of-type::first-letter{margin-top:1.1%}}@supports (-ms-ime-align:auto){.uk-dropcap>p:first-of-type::first-letter{font-size:1em}}.uk-logo{font-size:1.5rem;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";color:#666;text-decoration:none}.uk-logo:focus,.uk-logo:hover{color:#666;outline:0;text-decoration:none}.uk-logo-inverse{display:none}.uk-disabled{pointer-events:none}.uk-drag,.uk-drag *{cursor:move}.uk-drag iframe{pointer-events:none}.uk-dragover{box-shadow:0 0 20px rgba(100,100,100,.3)}.uk-blend-multiply{mix-blend-mode:multiply}.uk-blend-screen{mix-blend-mode:screen}.uk-blend-overlay{mix-blend-mode:overlay}.uk-blend-darken{mix-blend-mode:darken}.uk-blend-lighten{mix-blend-mode:lighten}.uk-blend-color-dodge{mix-blend-mode:color-dodge}.uk-blend-color-burn{mix-blend-mode:color-burn}.uk-blend-hard-light{mix-blend-mode:hard-light}.uk-blend-soft-light{mix-blend-mode:soft-light}.uk-blend-difference{mix-blend-mode:difference}.uk-blend-exclusion{mix-blend-mode:exclusion}.uk-blend-hue{mix-blend-mode:hue}.uk-blend-saturation{mix-blend-mode:saturation}.uk-blend-color{mix-blend-mode:color}.uk-blend-luminosity{mix-blend-mode:luminosity}.uk-transform-center{transform:translate(-50%,-50%)}.uk-transform-origin-top-left{transform-origin:0 0}.uk-transform-origin-top-center{transform-origin:50% 0}.uk-transform-origin-top-right{transform-origin:100% 0}.uk-transform-origin-center-left{transform-origin:0 50%}.uk-transform-origin-center-right{transform-origin:100% 50%}.uk-transform-origin-bottom-left{transform-origin:0 100%}.uk-transform-origin-bottom-center{transform-origin:50% 100%}.uk-transform-origin-bottom-right{transform-origin:100% 100%}.uk-flex{display:flex}.uk-flex-inline{display:inline-flex}.uk-flex-inline::after,.uk-flex-inline::before,.uk-flex::after,.uk-flex::before{display:none}.uk-flex-left{justify-content:flex-start}.uk-flex-center{justify-content:center}.uk-flex-right{justify-content:flex-end}.uk-flex-between{justify-content:space-between}.uk-flex-around{justify-content:space-around}@media (min-width:640px){.uk-flex-left\@s{justify-content:flex-start}.uk-flex-center\@s{justify-content:center}.uk-flex-right\@s{justify-content:flex-end}.uk-flex-between\@s{justify-content:space-between}.uk-flex-around\@s{justify-content:space-around}}@media (min-width:960px){.uk-flex-left\@m{justify-content:flex-start}.uk-flex-center\@m{justify-content:center}.uk-flex-right\@m{justify-content:flex-end}.uk-flex-between\@m{justify-content:space-between}.uk-flex-around\@m{justify-content:space-around}}@media (min-width:1200px){.uk-flex-left\@l{justify-content:flex-start}.uk-flex-center\@l{justify-content:center}.uk-flex-right\@l{justify-content:flex-end}.uk-flex-between\@l{justify-content:space-between}.uk-flex-around\@l{justify-content:space-around}}@media (min-width:1600px){.uk-flex-left\@xl{justify-content:flex-start}.uk-flex-center\@xl{justify-content:center}.uk-flex-right\@xl{justify-content:flex-end}.uk-flex-between\@xl{justify-content:space-between}.uk-flex-around\@xl{justify-content:space-around}}.uk-flex-stretch{align-items:stretch}.uk-flex-top{align-items:flex-start}.uk-flex-middle{align-items:center}.uk-flex-bottom{align-items:flex-end}.uk-flex-row{flex-direction:row}.uk-flex-row-reverse{flex-direction:row-reverse}.uk-flex-column{flex-direction:column}.uk-flex-column-reverse{flex-direction:column-reverse}.uk-flex-nowrap{flex-wrap:nowrap}.uk-flex-wrap{flex-wrap:wrap}.uk-flex-wrap-reverse{flex-wrap:wrap-reverse}.uk-flex-wrap-stretch{align-content:stretch}.uk-flex-wrap-top{align-content:flex-start}.uk-flex-wrap-middle{align-content:center}.uk-flex-wrap-bottom{align-content:flex-end}.uk-flex-wrap-between{align-content:space-between}.uk-flex-wrap-around{align-content:space-around}.uk-flex-first{order:-1}.uk-flex-last{order:99}@media (min-width:640px){.uk-flex-first\@s{order:-1}.uk-flex-last\@s{order:99}}@media (min-width:960px){.uk-flex-first\@m{order:-1}.uk-flex-last\@m{order:99}}@media (min-width:1200px){.uk-flex-first\@l{order:-1}.uk-flex-last\@l{order:99}}@media (min-width:1600px){.uk-flex-first\@xl{order:-1}.uk-flex-last\@xl{order:99}}.uk-flex-none{flex:none}.uk-flex-auto{flex:auto}.uk-flex-1{flex:1}.uk-margin{margin-bottom:20px}*+.uk-margin{margin-top:20px!important}.uk-margin-top{margin-top:20px!important}.uk-margin-bottom{margin-bottom:20px!important}.uk-margin-left{margin-left:20px!important}.uk-margin-right{margin-right:20px!important}.uk-margin-small{margin-bottom:10px}*+.uk-margin-small{margin-top:10px!important}.uk-margin-small-top{margin-top:10px!important}.uk-margin-small-bottom{margin-bottom:10px!important}.uk-margin-small-left{margin-left:10px!important}.uk-margin-small-right{margin-right:10px!important}.uk-margin-medium{margin-bottom:40px}*+.uk-margin-medium{margin-top:40px!important}.uk-margin-medium-top{margin-top:40px!important}.uk-margin-medium-bottom{margin-bottom:40px!important}.uk-margin-medium-left{margin-left:40px!important}.uk-margin-medium-right{margin-right:40px!important}.uk-margin-large{margin-bottom:40px}*+.uk-margin-large{margin-top:40px!important}.uk-margin-large-top{margin-top:40px!important}.uk-margin-large-bottom{margin-bottom:40px!important}.uk-margin-large-left{margin-left:40px!important}.uk-margin-large-right{margin-right:40px!important}@media (min-width:1200px){.uk-margin-large{margin-bottom:70px}*+.uk-margin-large{margin-top:70px!important}.uk-margin-large-top{margin-top:70px!important}.uk-margin-large-bottom{margin-bottom:70px!important}.uk-margin-large-left{margin-left:70px!important}.uk-margin-large-right{margin-right:70px!important}}.uk-margin-xlarge{margin-bottom:70px}*+.uk-margin-xlarge{margin-top:70px!important}.uk-margin-xlarge-top{margin-top:70px!important}.uk-margin-xlarge-bottom{margin-bottom:70px!important}.uk-margin-xlarge-left{margin-left:70px!important}.uk-margin-xlarge-right{margin-right:70px!important}@media (min-width:1200px){.uk-margin-xlarge{margin-bottom:140px}*+.uk-margin-xlarge{margin-top:140px!important}.uk-margin-xlarge-top{margin-top:140px!important}.uk-margin-xlarge-bottom{margin-bottom:140px!important}.uk-margin-xlarge-left{margin-left:140px!important}.uk-margin-xlarge-right{margin-right:140px!important}}.uk-margin-auto{margin-left:auto!important;margin-right:auto!important}.uk-margin-auto-top{margin-top:auto!important}.uk-margin-auto-bottom{margin-bottom:auto!important}.uk-margin-auto-left{margin-left:auto!important}.uk-margin-auto-right{margin-right:auto!important}.uk-margin-auto-vertical{margin-top:auto!important;margin-bottom:auto!important}@media (min-width:640px){.uk-margin-auto\@s{margin-left:auto!important;margin-right:auto!important}.uk-margin-auto-left\@s{margin-left:auto!important}.uk-margin-auto-right\@s{margin-right:auto!important}}@media (min-width:960px){.uk-margin-auto\@m{margin-left:auto!important;margin-right:auto!important}.uk-margin-auto-left\@m{margin-left:auto!important}.uk-margin-auto-right\@m{margin-right:auto!important}}@media (min-width:1200px){.uk-margin-auto\@l{margin-left:auto!important;margin-right:auto!important}.uk-margin-auto-left\@l{margin-left:auto!important}.uk-margin-auto-right\@l{margin-right:auto!important}}@media (min-width:1600px){.uk-margin-auto\@xl{margin-left:auto!important;margin-right:auto!important}.uk-margin-auto-left\@xl{margin-left:auto!important}.uk-margin-auto-right\@xl{margin-right:auto!important}}.uk-margin-remove{margin:0!important}.uk-margin-remove-top{margin-top:0!important}.uk-margin-remove-bottom{margin-bottom:0!important}.uk-margin-remove-left{margin-left:0!important}.uk-margin-remove-right{margin-right:0!important}.uk-margin-remove-vertical{margin-top:0!important;margin-bottom:0!important}.uk-margin-remove-adjacent+*,.uk-margin-remove-first-child>:first-child{margin-top:0!important}.uk-margin-remove-last-child>:last-child{margin-bottom:0!important}@media (min-width:640px){.uk-margin-remove-left\@s{margin-left:0!important}.uk-margin-remove-right\@s{margin-right:0!important}}@media (min-width:960px){.uk-margin-remove-left\@m{margin-left:0!important}.uk-margin-remove-right\@m{margin-right:0!important}}@media (min-width:1200px){.uk-margin-remove-left\@l{margin-left:0!important}.uk-margin-remove-right\@l{margin-right:0!important}}@media (min-width:1600px){.uk-margin-remove-left\@xl{margin-left:0!important}.uk-margin-remove-right\@xl{margin-right:0!important}}.uk-padding{padding:30px}@media (min-width:1200px){.uk-padding{padding:40px}}.uk-padding-small{padding:15px}.uk-padding-large{padding:30px}@media (min-width:1200px){.uk-padding-large{padding:70px}}.uk-padding-remove{padding:0!important}.uk-padding-remove-top{padding-top:0!important}.uk-padding-remove-bottom{padding-bottom:0!important}.uk-padding-remove-left{padding-left:0!important}.uk-padding-remove-right{padding-right:0!important}.uk-padding-remove-vertical{padding-top:0!important;padding-bottom:0!important}.uk-padding-remove-horizontal{padding-left:0!important;padding-right:0!important}[class*=uk-position-bottom],[class*=uk-position-center],[class*=uk-position-left],[class*=uk-position-right],[class*=uk-position-top]{position:absolute!important;max-width:100%}.uk-position-top{top:0;left:0;right:0}.uk-position-bottom{bottom:0;left:0;right:0}.uk-position-left{top:0;bottom:0;left:0}.uk-position-right{top:0;bottom:0;right:0}.uk-position-top-left{top:0;left:0}.uk-position-top-right{top:0;right:0}.uk-position-bottom-left{bottom:0;left:0}.uk-position-bottom-right{bottom:0;right:0}.uk-position-center{top:50%;left:50%;transform:translate(-50%,-50%);width:max-content;max-width:100%;box-sizing:border-box}[class*=uk-position-center-left],[class*=uk-position-center-right]{top:50%;transform:translateY(-50%)}.uk-position-center-left{left:0}.uk-position-center-right{right:0}.uk-position-center-left-out{right:100%;width:max-content}.uk-position-center-right-out{left:100%;width:max-content}.uk-position-bottom-center,.uk-position-top-center{left:50%;transform:translateX(-50%);width:max-content;max-width:100%;box-sizing:border-box}.uk-position-top-center{top:0}.uk-position-bottom-center{bottom:0}.uk-position-cover{position:absolute;top:0;bottom:0;left:0;right:0}.uk-position-relative{position:relative!important}.uk-position-absolute{position:absolute!important}.uk-position-fixed{position:fixed!important}.uk-position-z-index{z-index:1}.uk-position-small{max-width:calc(100% - (15px * 2));margin:15px}.uk-position-small.uk-position-center{transform:translate(-50%,-50%) translate(-15px,-15px)}.uk-position-small[class*=uk-position-center-left],.uk-position-small[class*=uk-position-center-right]{transform:translateY(-50%) translateY(-15px)}.uk-position-small.uk-position-bottom-center,.uk-position-small.uk-position-top-center{transform:translateX(-50%) translateX(-15px)}.uk-position-medium{max-width:calc(100% - (30px * 2));margin:30px}.uk-position-medium.uk-position-center{transform:translate(-50%,-50%) translate(-30px,-30px)}.uk-position-medium[class*=uk-position-center-left],.uk-position-medium[class*=uk-position-center-right]{transform:translateY(-50%) translateY(-30px)}.uk-position-medium.uk-position-bottom-center,.uk-position-medium.uk-position-top-center{transform:translateX(-50%) translateX(-30px)}.uk-position-large{max-width:calc(100% - (30px * 2));margin:30px}.uk-position-large.uk-position-center{transform:translate(-50%,-50%) translate(-30px,-30px)}.uk-position-large[class*=uk-position-center-left],.uk-position-large[class*=uk-position-center-right]{transform:translateY(-50%) translateY(-30px)}.uk-position-large.uk-position-bottom-center,.uk-position-large.uk-position-top-center{transform:translateX(-50%) translateX(-30px)}@media (min-width:1200px){.uk-position-large{max-width:calc(100% - (50px * 2));margin:50px}.uk-position-large.uk-position-center{transform:translate(-50%,-50%) translate(-50px,-50px)}.uk-position-large[class*=uk-position-center-left],.uk-position-large[class*=uk-position-center-right]{transform:translateY(-50%) translateY(-50px)}.uk-position-large.uk-position-bottom-center,.uk-position-large.uk-position-top-center{transform:translateX(-50%) translateX(-50px)}}.uk-transition-toggle{-webkit-tap-highlight-color:transparent}.uk-transition-toggle:focus{outline:0}.uk-transition-fade,[class*=uk-transition-scale],[class*=uk-transition-slide]{transition:.3s ease-out;transition-property:opacity,transform,filter;opacity:0}.uk-transition-active.uk-active .uk-transition-fade,.uk-transition-toggle:focus .uk-transition-fade,.uk-transition-toggle:hover .uk-transition-fade{opacity:1}.uk-transition-scale-up{transform:scale(1,1)}.uk-transition-scale-down{transform:scale(1.03,1.03)}.uk-transition-active.uk-active .uk-transition-scale-up,.uk-transition-toggle:focus .uk-transition-scale-up,.uk-transition-toggle:hover .uk-transition-scale-up{opacity:1;transform:scale(1.03,1.03)}.uk-transition-active.uk-active .uk-transition-scale-down,.uk-transition-toggle:focus .uk-transition-scale-down,.uk-transition-toggle:hover .uk-transition-scale-down{opacity:1;transform:scale(1,1)}.uk-transition-slide-top{transform:translateY(-100%)}.uk-transition-slide-bottom{transform:translateY(100%)}.uk-transition-slide-left{transform:translateX(-100%)}.uk-transition-slide-right{transform:translateX(100%)}.uk-transition-slide-top-small{transform:translateY(-10px)}.uk-transition-slide-bottom-small{transform:translateY(10px)}.uk-transition-slide-left-small{transform:translateX(-10px)}.uk-transition-slide-right-small{transform:translateX(10px)}.uk-transition-slide-top-medium{transform:translateY(-50px)}.uk-transition-slide-bottom-medium{transform:translateY(50px)}.uk-transition-slide-left-medium{transform:translateX(-50px)}.uk-transition-slide-right-medium{transform:translateX(50px)}.uk-transition-active.uk-active [class*=uk-transition-slide],.uk-transition-toggle:focus [class*=uk-transition-slide],.uk-transition-toggle:hover [class*=uk-transition-slide]{opacity:1;transform:translate(0,0)}.uk-transition-opaque{opacity:1}.uk-transition-slow{transition-duration:.7s}.uk-hidden,[hidden]{display:none!important}@media (min-width:640px){.uk-hidden\@s{display:none!important}}@media (min-width:960px){.uk-hidden\@m{display:none!important}}@media (min-width:1200px){.uk-hidden\@l{display:none!important}}@media (min-width:1600px){.uk-hidden\@xl{display:none!important}}@media (max-width:639px){.uk-visible\@s{display:none!important}}@media (max-width:959px){.uk-visible\@m{display:none!important}}@media (max-width:1199px){.uk-visible\@l{display:none!important}}@media (max-width:1599px){.uk-visible\@xl{display:none!important}}.uk-invisible{visibility:hidden!important}.uk-visible-toggle:not(:hover):not(:focus) .uk-hidden-hover:not(:focus-within){position:absolute!important;width:0!important;height:0!important;padding:0!important;margin:0!important;overflow:hidden!important}.uk-visible-toggle:not(:hover):not(:focus) .uk-invisible-hover:not(:focus-within){opacity:0!important}.uk-visible-toggle{-webkit-tap-highlight-color:transparent}.uk-visible-toggle:focus{outline:0}@media (pointer:coarse){.uk-hidden-touch{display:none!important}}.uk-hidden-notouch{display:none!important}@media (pointer:coarse){.uk-hidden-notouch{display:block!important}}.uk-card-primary.uk-card-body,.uk-card-primary>:not([class*=uk-card-media]),.uk-card-secondary.uk-card-body,.uk-card-secondary>:not([class*=uk-card-media]),.uk-light,.uk-offcanvas-bar,.uk-overlay-primary,.uk-section-primary:not(.uk-preserve-color),.uk-section-secondary:not(.uk-preserve-color),.uk-tile-primary:not(.uk-preserve-color),.uk-tile-secondary:not(.uk-preserve-color){color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-link,.uk-card-primary.uk-card-body a,.uk-card-primary>:not([class*=uk-card-media]) .uk-link,.uk-card-primary>:not([class*=uk-card-media]) a,.uk-card-secondary.uk-card-body .uk-link,.uk-card-secondary.uk-card-body a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link,.uk-card-secondary>:not([class*=uk-card-media]) a,.uk-light .uk-link,.uk-light a,.uk-offcanvas-bar .uk-link,.uk-offcanvas-bar a,.uk-overlay-primary .uk-link,.uk-overlay-primary a,.uk-section-primary:not(.uk-preserve-color) .uk-link,.uk-section-primary:not(.uk-preserve-color) a,.uk-section-secondary:not(.uk-preserve-color) .uk-link,.uk-section-secondary:not(.uk-preserve-color) a,.uk-tile-primary:not(.uk-preserve-color) .uk-link,.uk-tile-primary:not(.uk-preserve-color) a,.uk-tile-secondary:not(.uk-preserve-color) .uk-link,.uk-tile-secondary:not(.uk-preserve-color) a{color:#fff}.uk-card-primary.uk-card-body .uk-link-toggle:focus .uk-link,.uk-card-primary.uk-card-body .uk-link-toggle:hover .uk-link,.uk-card-primary.uk-card-body .uk-link:hover,.uk-card-primary.uk-card-body a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-toggle:focus .uk-link,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-toggle:hover .uk-link,.uk-card-primary>:not([class*=uk-card-media]) .uk-link:hover,.uk-card-primary>:not([class*=uk-card-media]) a:hover,.uk-card-secondary.uk-card-body .uk-link-toggle:focus .uk-link,.uk-card-secondary.uk-card-body .uk-link-toggle:hover .uk-link,.uk-card-secondary.uk-card-body .uk-link:hover,.uk-card-secondary.uk-card-body a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-toggle:focus .uk-link,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-toggle:hover .uk-link,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link:hover,.uk-card-secondary>:not([class*=uk-card-media]) a:hover,.uk-light .uk-link-toggle:focus .uk-link,.uk-light .uk-link-toggle:hover .uk-link,.uk-light .uk-link:hover,.uk-light a:hover,.uk-offcanvas-bar .uk-link-toggle:focus .uk-link,.uk-offcanvas-bar .uk-link-toggle:hover .uk-link,.uk-offcanvas-bar .uk-link:hover,.uk-offcanvas-bar a:hover,.uk-overlay-primary .uk-link-toggle:focus .uk-link,.uk-overlay-primary .uk-link-toggle:hover .uk-link,.uk-overlay-primary .uk-link:hover,.uk-overlay-primary a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link,.uk-section-primary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link,.uk-section-primary:not(.uk-preserve-color) .uk-link:hover,.uk-section-primary:not(.uk-preserve-color) a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link,.uk-section-secondary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link,.uk-section-secondary:not(.uk-preserve-color) .uk-link:hover,.uk-section-secondary:not(.uk-preserve-color) a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link,.uk-tile-primary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link,.uk-tile-primary:not(.uk-preserve-color) .uk-link:hover,.uk-tile-primary:not(.uk-preserve-color) a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link,.uk-tile-secondary:not(.uk-preserve-color) .uk-link:hover,.uk-tile-secondary:not(.uk-preserve-color) a:hover{color:#fff}.uk-card-primary.uk-card-body :not(pre)>code,.uk-card-primary.uk-card-body :not(pre)>kbd,.uk-card-primary.uk-card-body :not(pre)>samp,.uk-card-primary>:not([class*=uk-card-media]) :not(pre)>code,.uk-card-primary>:not([class*=uk-card-media]) :not(pre)>kbd,.uk-card-primary>:not([class*=uk-card-media]) :not(pre)>samp,.uk-card-secondary.uk-card-body :not(pre)>code,.uk-card-secondary.uk-card-body :not(pre)>kbd,.uk-card-secondary.uk-card-body :not(pre)>samp,.uk-card-secondary>:not([class*=uk-card-media]) :not(pre)>code,.uk-card-secondary>:not([class*=uk-card-media]) :not(pre)>kbd,.uk-card-secondary>:not([class*=uk-card-media]) :not(pre)>samp,.uk-light :not(pre)>code,.uk-light :not(pre)>kbd,.uk-light :not(pre)>samp,.uk-offcanvas-bar :not(pre)>code,.uk-offcanvas-bar :not(pre)>kbd,.uk-offcanvas-bar :not(pre)>samp,.uk-overlay-primary :not(pre)>code,.uk-overlay-primary :not(pre)>kbd,.uk-overlay-primary :not(pre)>samp,.uk-section-primary:not(.uk-preserve-color) :not(pre)>code,.uk-section-primary:not(.uk-preserve-color) :not(pre)>kbd,.uk-section-primary:not(.uk-preserve-color) :not(pre)>samp,.uk-section-secondary:not(.uk-preserve-color) :not(pre)>code,.uk-section-secondary:not(.uk-preserve-color) :not(pre)>kbd,.uk-section-secondary:not(.uk-preserve-color) :not(pre)>samp,.uk-tile-primary:not(.uk-preserve-color) :not(pre)>code,.uk-tile-primary:not(.uk-preserve-color) :not(pre)>kbd,.uk-tile-primary:not(.uk-preserve-color) :not(pre)>samp,.uk-tile-secondary:not(.uk-preserve-color) :not(pre)>code,.uk-tile-secondary:not(.uk-preserve-color) :not(pre)>kbd,.uk-tile-secondary:not(.uk-preserve-color) :not(pre)>samp{color:rgba(255,255,255,.7);background:rgba(255,255,255,.1)}.uk-card-primary.uk-card-body em,.uk-card-primary>:not([class*=uk-card-media]) em,.uk-card-secondary.uk-card-body em,.uk-card-secondary>:not([class*=uk-card-media]) em,.uk-light em,.uk-offcanvas-bar em,.uk-overlay-primary em,.uk-section-primary:not(.uk-preserve-color) em,.uk-section-secondary:not(.uk-preserve-color) em,.uk-tile-primary:not(.uk-preserve-color) em,.uk-tile-secondary:not(.uk-preserve-color) em{color:#fff}.uk-card-primary.uk-card-body .uk-h1,.uk-card-primary.uk-card-body .uk-h2,.uk-card-primary.uk-card-body .uk-h3,.uk-card-primary.uk-card-body .uk-h4,.uk-card-primary.uk-card-body .uk-h5,.uk-card-primary.uk-card-body .uk-h6,.uk-card-primary.uk-card-body .uk-heading-2xlarge,.uk-card-primary.uk-card-body .uk-heading-large,.uk-card-primary.uk-card-body .uk-heading-medium,.uk-card-primary.uk-card-body .uk-heading-small,.uk-card-primary.uk-card-body .uk-heading-xlarge,.uk-card-primary.uk-card-body h1,.uk-card-primary.uk-card-body h2,.uk-card-primary.uk-card-body h3,.uk-card-primary.uk-card-body h4,.uk-card-primary.uk-card-body h5,.uk-card-primary.uk-card-body h6,.uk-card-primary>:not([class*=uk-card-media]) .uk-h1,.uk-card-primary>:not([class*=uk-card-media]) .uk-h2,.uk-card-primary>:not([class*=uk-card-media]) .uk-h3,.uk-card-primary>:not([class*=uk-card-media]) .uk-h4,.uk-card-primary>:not([class*=uk-card-media]) .uk-h5,.uk-card-primary>:not([class*=uk-card-media]) .uk-h6,.uk-card-primary>:not([class*=uk-card-media]) .uk-heading-2xlarge,.uk-card-primary>:not([class*=uk-card-media]) .uk-heading-large,.uk-card-primary>:not([class*=uk-card-media]) .uk-heading-medium,.uk-card-primary>:not([class*=uk-card-media]) .uk-heading-small,.uk-card-primary>:not([class*=uk-card-media]) .uk-heading-xlarge,.uk-card-primary>:not([class*=uk-card-media]) h1,.uk-card-primary>:not([class*=uk-card-media]) h2,.uk-card-primary>:not([class*=uk-card-media]) h3,.uk-card-primary>:not([class*=uk-card-media]) h4,.uk-card-primary>:not([class*=uk-card-media]) h5,.uk-card-primary>:not([class*=uk-card-media]) h6,.uk-card-secondary.uk-card-body .uk-h1,.uk-card-secondary.uk-card-body .uk-h2,.uk-card-secondary.uk-card-body .uk-h3,.uk-card-secondary.uk-card-body .uk-h4,.uk-card-secondary.uk-card-body .uk-h5,.uk-card-secondary.uk-card-body .uk-h6,.uk-card-secondary.uk-card-body .uk-heading-2xlarge,.uk-card-secondary.uk-card-body .uk-heading-large,.uk-card-secondary.uk-card-body .uk-heading-medium,.uk-card-secondary.uk-card-body .uk-heading-small,.uk-card-secondary.uk-card-body .uk-heading-xlarge,.uk-card-secondary.uk-card-body h1,.uk-card-secondary.uk-card-body h2,.uk-card-secondary.uk-card-body h3,.uk-card-secondary.uk-card-body h4,.uk-card-secondary.uk-card-body h5,.uk-card-secondary.uk-card-body h6,.uk-card-secondary>:not([class*=uk-card-media]) .uk-h1,.uk-card-secondary>:not([class*=uk-card-media]) .uk-h2,.uk-card-secondary>:not([class*=uk-card-media]) .uk-h3,.uk-card-secondary>:not([class*=uk-card-media]) .uk-h4,.uk-card-secondary>:not([class*=uk-card-media]) .uk-h5,.uk-card-secondary>:not([class*=uk-card-media]) .uk-h6,.uk-card-secondary>:not([class*=uk-card-media]) .uk-heading-2xlarge,.uk-card-secondary>:not([class*=uk-card-media]) .uk-heading-large,.uk-card-secondary>:not([class*=uk-card-media]) .uk-heading-medium,.uk-card-secondary>:not([class*=uk-card-media]) .uk-heading-small,.uk-card-secondary>:not([class*=uk-card-media]) .uk-heading-xlarge,.uk-card-secondary>:not([class*=uk-card-media]) h1,.uk-card-secondary>:not([class*=uk-card-media]) h2,.uk-card-secondary>:not([class*=uk-card-media]) h3,.uk-card-secondary>:not([class*=uk-card-media]) h4,.uk-card-secondary>:not([class*=uk-card-media]) h5,.uk-card-secondary>:not([class*=uk-card-media]) h6,.uk-light .uk-h1,.uk-light .uk-h2,.uk-light .uk-h3,.uk-light .uk-h4,.uk-light .uk-h5,.uk-light .uk-h6,.uk-light .uk-heading-2xlarge,.uk-light .uk-heading-large,.uk-light .uk-heading-medium,.uk-light .uk-heading-small,.uk-light .uk-heading-xlarge,.uk-light h1,.uk-light h2,.uk-light h3,.uk-light h4,.uk-light h5,.uk-light h6,.uk-offcanvas-bar .uk-h1,.uk-offcanvas-bar .uk-h2,.uk-offcanvas-bar .uk-h3,.uk-offcanvas-bar .uk-h4,.uk-offcanvas-bar .uk-h5,.uk-offcanvas-bar .uk-h6,.uk-offcanvas-bar .uk-heading-2xlarge,.uk-offcanvas-bar .uk-heading-large,.uk-offcanvas-bar .uk-heading-medium,.uk-offcanvas-bar .uk-heading-small,.uk-offcanvas-bar .uk-heading-xlarge,.uk-offcanvas-bar h1,.uk-offcanvas-bar h2,.uk-offcanvas-bar h3,.uk-offcanvas-bar h4,.uk-offcanvas-bar h5,.uk-offcanvas-bar h6,.uk-overlay-primary .uk-h1,.uk-overlay-primary .uk-h2,.uk-overlay-primary .uk-h3,.uk-overlay-primary .uk-h4,.uk-overlay-primary .uk-h5,.uk-overlay-primary .uk-h6,.uk-overlay-primary .uk-heading-2xlarge,.uk-overlay-primary .uk-heading-large,.uk-overlay-primary .uk-heading-medium,.uk-overlay-primary .uk-heading-small,.uk-overlay-primary .uk-heading-xlarge,.uk-overlay-primary h1,.uk-overlay-primary h2,.uk-overlay-primary h3,.uk-overlay-primary h4,.uk-overlay-primary h5,.uk-overlay-primary h6,.uk-section-primary:not(.uk-preserve-color) .uk-h1,.uk-section-primary:not(.uk-preserve-color) .uk-h2,.uk-section-primary:not(.uk-preserve-color) .uk-h3,.uk-section-primary:not(.uk-preserve-color) .uk-h4,.uk-section-primary:not(.uk-preserve-color) .uk-h5,.uk-section-primary:not(.uk-preserve-color) .uk-h6,.uk-section-primary:not(.uk-preserve-color) .uk-heading-2xlarge,.uk-section-primary:not(.uk-preserve-color) .uk-heading-large,.uk-section-primary:not(.uk-preserve-color) .uk-heading-medium,.uk-section-primary:not(.uk-preserve-color) .uk-heading-small,.uk-section-primary:not(.uk-preserve-color) .uk-heading-xlarge,.uk-section-primary:not(.uk-preserve-color) h1,.uk-section-primary:not(.uk-preserve-color) h2,.uk-section-primary:not(.uk-preserve-color) h3,.uk-section-primary:not(.uk-preserve-color) h4,.uk-section-primary:not(.uk-preserve-color) h5,.uk-section-primary:not(.uk-preserve-color) h6,.uk-section-secondary:not(.uk-preserve-color) .uk-h1,.uk-section-secondary:not(.uk-preserve-color) .uk-h2,.uk-section-secondary:not(.uk-preserve-color) .uk-h3,.uk-section-secondary:not(.uk-preserve-color) .uk-h4,.uk-section-secondary:not(.uk-preserve-color) .uk-h5,.uk-section-secondary:not(.uk-preserve-color) .uk-h6,.uk-section-secondary:not(.uk-preserve-color) .uk-heading-2xlarge,.uk-section-secondary:not(.uk-preserve-color) .uk-heading-large,.uk-section-secondary:not(.uk-preserve-color) .uk-heading-medium,.uk-section-secondary:not(.uk-preserve-color) .uk-heading-small,.uk-section-secondary:not(.uk-preserve-color) .uk-heading-xlarge,.uk-section-secondary:not(.uk-preserve-color) h1,.uk-section-secondary:not(.uk-preserve-color) h2,.uk-section-secondary:not(.uk-preserve-color) h3,.uk-section-secondary:not(.uk-preserve-color) h4,.uk-section-secondary:not(.uk-preserve-color) h5,.uk-section-secondary:not(.uk-preserve-color) h6,.uk-tile-primary:not(.uk-preserve-color) .uk-h1,.uk-tile-primary:not(.uk-preserve-color) .uk-h2,.uk-tile-primary:not(.uk-preserve-color) .uk-h3,.uk-tile-primary:not(.uk-preserve-color) .uk-h4,.uk-tile-primary:not(.uk-preserve-color) .uk-h5,.uk-tile-primary:not(.uk-preserve-color) .uk-h6,.uk-tile-primary:not(.uk-preserve-color) .uk-heading-2xlarge,.uk-tile-primary:not(.uk-preserve-color) .uk-heading-large,.uk-tile-primary:not(.uk-preserve-color) .uk-heading-medium,.uk-tile-primary:not(.uk-preserve-color) .uk-heading-small,.uk-tile-primary:not(.uk-preserve-color) .uk-heading-xlarge,.uk-tile-primary:not(.uk-preserve-color) h1,.uk-tile-primary:not(.uk-preserve-color) h2,.uk-tile-primary:not(.uk-preserve-color) h3,.uk-tile-primary:not(.uk-preserve-color) h4,.uk-tile-primary:not(.uk-preserve-color) h5,.uk-tile-primary:not(.uk-preserve-color) h6,.uk-tile-secondary:not(.uk-preserve-color) .uk-h1,.uk-tile-secondary:not(.uk-preserve-color) .uk-h2,.uk-tile-secondary:not(.uk-preserve-color) .uk-h3,.uk-tile-secondary:not(.uk-preserve-color) .uk-h4,.uk-tile-secondary:not(.uk-preserve-color) .uk-h5,.uk-tile-secondary:not(.uk-preserve-color) .uk-h6,.uk-tile-secondary:not(.uk-preserve-color) .uk-heading-2xlarge,.uk-tile-secondary:not(.uk-preserve-color) .uk-heading-large,.uk-tile-secondary:not(.uk-preserve-color) .uk-heading-medium,.uk-tile-secondary:not(.uk-preserve-color) .uk-heading-small,.uk-tile-secondary:not(.uk-preserve-color) .uk-heading-xlarge,.uk-tile-secondary:not(.uk-preserve-color) h1,.uk-tile-secondary:not(.uk-preserve-color) h2,.uk-tile-secondary:not(.uk-preserve-color) h3,.uk-tile-secondary:not(.uk-preserve-color) h4,.uk-tile-secondary:not(.uk-preserve-color) h5,.uk-tile-secondary:not(.uk-preserve-color) h6{color:#fff}.uk-card-primary.uk-card-body blockquote,.uk-card-primary>:not([class*=uk-card-media]) blockquote,.uk-card-secondary.uk-card-body blockquote,.uk-card-secondary>:not([class*=uk-card-media]) blockquote,.uk-light blockquote,.uk-offcanvas-bar blockquote,.uk-overlay-primary blockquote,.uk-section-primary:not(.uk-preserve-color) blockquote,.uk-section-secondary:not(.uk-preserve-color) blockquote,.uk-tile-primary:not(.uk-preserve-color) blockquote,.uk-tile-secondary:not(.uk-preserve-color) blockquote{color:#fff}.uk-card-primary.uk-card-body blockquote footer,.uk-card-primary>:not([class*=uk-card-media]) blockquote footer,.uk-card-secondary.uk-card-body blockquote footer,.uk-card-secondary>:not([class*=uk-card-media]) blockquote footer,.uk-light blockquote footer,.uk-offcanvas-bar blockquote footer,.uk-overlay-primary blockquote footer,.uk-section-primary:not(.uk-preserve-color) blockquote footer,.uk-section-secondary:not(.uk-preserve-color) blockquote footer,.uk-tile-primary:not(.uk-preserve-color) blockquote footer,.uk-tile-secondary:not(.uk-preserve-color) blockquote footer{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-hr,.uk-card-primary.uk-card-body hr,.uk-card-primary>:not([class*=uk-card-media]) .uk-hr,.uk-card-primary>:not([class*=uk-card-media]) hr,.uk-card-secondary.uk-card-body .uk-hr,.uk-card-secondary.uk-card-body hr,.uk-card-secondary>:not([class*=uk-card-media]) .uk-hr,.uk-card-secondary>:not([class*=uk-card-media]) hr,.uk-light .uk-hr,.uk-light hr,.uk-offcanvas-bar .uk-hr,.uk-offcanvas-bar hr,.uk-overlay-primary .uk-hr,.uk-overlay-primary hr,.uk-section-primary:not(.uk-preserve-color) .uk-hr,.uk-section-primary:not(.uk-preserve-color) hr,.uk-section-secondary:not(.uk-preserve-color) .uk-hr,.uk-section-secondary:not(.uk-preserve-color) hr,.uk-tile-primary:not(.uk-preserve-color) .uk-hr,.uk-tile-primary:not(.uk-preserve-color) hr,.uk-tile-secondary:not(.uk-preserve-color) .uk-hr,.uk-tile-secondary:not(.uk-preserve-color) hr{border-top-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-link-muted a,.uk-card-primary.uk-card-body a.uk-link-muted,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-muted a,.uk-card-primary>:not([class*=uk-card-media]) a.uk-link-muted,.uk-card-secondary.uk-card-body .uk-link-muted a,.uk-card-secondary.uk-card-body a.uk-link-muted,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-muted a,.uk-card-secondary>:not([class*=uk-card-media]) a.uk-link-muted,.uk-light .uk-link-muted a,.uk-light a.uk-link-muted,.uk-offcanvas-bar .uk-link-muted a,.uk-offcanvas-bar a.uk-link-muted,.uk-overlay-primary .uk-link-muted a,.uk-overlay-primary a.uk-link-muted,.uk-section-primary:not(.uk-preserve-color) .uk-link-muted a,.uk-section-primary:not(.uk-preserve-color) a.uk-link-muted,.uk-section-secondary:not(.uk-preserve-color) .uk-link-muted a,.uk-section-secondary:not(.uk-preserve-color) a.uk-link-muted,.uk-tile-primary:not(.uk-preserve-color) .uk-link-muted a,.uk-tile-primary:not(.uk-preserve-color) a.uk-link-muted,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-muted a,.uk-tile-secondary:not(.uk-preserve-color) a.uk-link-muted{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-link-muted a:hover,.uk-card-primary.uk-card-body .uk-link-toggle:focus .uk-link-muted,.uk-card-primary.uk-card-body .uk-link-toggle:hover .uk-link-muted,.uk-card-primary.uk-card-body a.uk-link-muted:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-muted a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-toggle:focus .uk-link-muted,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-toggle:hover .uk-link-muted,.uk-card-primary>:not([class*=uk-card-media]) a.uk-link-muted:hover,.uk-card-secondary.uk-card-body .uk-link-muted a:hover,.uk-card-secondary.uk-card-body .uk-link-toggle:focus .uk-link-muted,.uk-card-secondary.uk-card-body .uk-link-toggle:hover .uk-link-muted,.uk-card-secondary.uk-card-body a.uk-link-muted:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-muted a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-toggle:focus .uk-link-muted,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-toggle:hover .uk-link-muted,.uk-card-secondary>:not([class*=uk-card-media]) a.uk-link-muted:hover,.uk-light .uk-link-muted a:hover,.uk-light .uk-link-toggle:focus .uk-link-muted,.uk-light .uk-link-toggle:hover .uk-link-muted,.uk-light a.uk-link-muted:hover,.uk-offcanvas-bar .uk-link-muted a:hover,.uk-offcanvas-bar .uk-link-toggle:focus .uk-link-muted,.uk-offcanvas-bar .uk-link-toggle:hover .uk-link-muted,.uk-offcanvas-bar a.uk-link-muted:hover,.uk-overlay-primary .uk-link-muted a:hover,.uk-overlay-primary .uk-link-toggle:focus .uk-link-muted,.uk-overlay-primary .uk-link-toggle:hover .uk-link-muted,.uk-overlay-primary a.uk-link-muted:hover,.uk-section-primary:not(.uk-preserve-color) .uk-link-muted a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-muted,.uk-section-primary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-muted,.uk-section-primary:not(.uk-preserve-color) a.uk-link-muted:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-link-muted a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-muted,.uk-section-secondary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-muted,.uk-section-secondary:not(.uk-preserve-color) a.uk-link-muted:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-link-muted a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-muted,.uk-tile-primary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-muted,.uk-tile-primary:not(.uk-preserve-color) a.uk-link-muted:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-muted a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-muted,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-muted,.uk-tile-secondary:not(.uk-preserve-color) a.uk-link-muted:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-link-text a:hover,.uk-card-primary.uk-card-body .uk-link-toggle:focus .uk-link-text,.uk-card-primary.uk-card-body .uk-link-toggle:hover .uk-link-text,.uk-card-primary.uk-card-body a.uk-link-text:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-text a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-toggle:focus .uk-link-text,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-toggle:hover .uk-link-text,.uk-card-primary>:not([class*=uk-card-media]) a.uk-link-text:hover,.uk-card-secondary.uk-card-body .uk-link-text a:hover,.uk-card-secondary.uk-card-body .uk-link-toggle:focus .uk-link-text,.uk-card-secondary.uk-card-body .uk-link-toggle:hover .uk-link-text,.uk-card-secondary.uk-card-body a.uk-link-text:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-text a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-toggle:focus .uk-link-text,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-toggle:hover .uk-link-text,.uk-card-secondary>:not([class*=uk-card-media]) a.uk-link-text:hover,.uk-light .uk-link-text a:hover,.uk-light .uk-link-toggle:focus .uk-link-text,.uk-light .uk-link-toggle:hover .uk-link-text,.uk-light a.uk-link-text:hover,.uk-offcanvas-bar .uk-link-text a:hover,.uk-offcanvas-bar .uk-link-toggle:focus .uk-link-text,.uk-offcanvas-bar .uk-link-toggle:hover .uk-link-text,.uk-offcanvas-bar a.uk-link-text:hover,.uk-overlay-primary .uk-link-text a:hover,.uk-overlay-primary .uk-link-toggle:focus .uk-link-text,.uk-overlay-primary .uk-link-toggle:hover .uk-link-text,.uk-overlay-primary a.uk-link-text:hover,.uk-section-primary:not(.uk-preserve-color) .uk-link-text a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-text,.uk-section-primary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-text,.uk-section-primary:not(.uk-preserve-color) a.uk-link-text:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-link-text a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-text,.uk-section-secondary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-text,.uk-section-secondary:not(.uk-preserve-color) a.uk-link-text:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-link-text a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-text,.uk-tile-primary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-text,.uk-tile-primary:not(.uk-preserve-color) a.uk-link-text:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-text a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-text,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-text,.uk-tile-secondary:not(.uk-preserve-color) a.uk-link-text:hover{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-link-heading a:hover,.uk-card-primary.uk-card-body .uk-link-toggle:focus .uk-link-heading,.uk-card-primary.uk-card-body .uk-link-toggle:hover .uk-link-heading,.uk-card-primary.uk-card-body a.uk-link-heading:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-heading a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-toggle:focus .uk-link-heading,.uk-card-primary>:not([class*=uk-card-media]) .uk-link-toggle:hover .uk-link-heading,.uk-card-primary>:not([class*=uk-card-media]) a.uk-link-heading:hover,.uk-card-secondary.uk-card-body .uk-link-heading a:hover,.uk-card-secondary.uk-card-body .uk-link-toggle:focus .uk-link-heading,.uk-card-secondary.uk-card-body .uk-link-toggle:hover .uk-link-heading,.uk-card-secondary.uk-card-body a.uk-link-heading:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-heading a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-toggle:focus .uk-link-heading,.uk-card-secondary>:not([class*=uk-card-media]) .uk-link-toggle:hover .uk-link-heading,.uk-card-secondary>:not([class*=uk-card-media]) a.uk-link-heading:hover,.uk-light .uk-link-heading a:hover,.uk-light .uk-link-toggle:focus .uk-link-heading,.uk-light .uk-link-toggle:hover .uk-link-heading,.uk-light a.uk-link-heading:hover,.uk-offcanvas-bar .uk-link-heading a:hover,.uk-offcanvas-bar .uk-link-toggle:focus .uk-link-heading,.uk-offcanvas-bar .uk-link-toggle:hover .uk-link-heading,.uk-offcanvas-bar a.uk-link-heading:hover,.uk-overlay-primary .uk-link-heading a:hover,.uk-overlay-primary .uk-link-toggle:focus .uk-link-heading,.uk-overlay-primary .uk-link-toggle:hover .uk-link-heading,.uk-overlay-primary a.uk-link-heading:hover,.uk-section-primary:not(.uk-preserve-color) .uk-link-heading a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-heading,.uk-section-primary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-heading,.uk-section-primary:not(.uk-preserve-color) a.uk-link-heading:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-link-heading a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-heading,.uk-section-secondary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-heading,.uk-section-secondary:not(.uk-preserve-color) a.uk-link-heading:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-link-heading a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-heading,.uk-tile-primary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-heading,.uk-tile-primary:not(.uk-preserve-color) a.uk-link-heading:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-heading a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-toggle:focus .uk-link-heading,.uk-tile-secondary:not(.uk-preserve-color) .uk-link-toggle:hover .uk-link-heading,.uk-tile-secondary:not(.uk-preserve-color) a.uk-link-heading:hover{color:#fff}.uk-card-primary.uk-card-body .uk-heading-divider,.uk-card-primary>:not([class*=uk-card-media]) .uk-heading-divider,.uk-card-secondary.uk-card-body .uk-heading-divider,.uk-card-secondary>:not([class*=uk-card-media]) .uk-heading-divider,.uk-light .uk-heading-divider,.uk-offcanvas-bar .uk-heading-divider,.uk-overlay-primary .uk-heading-divider,.uk-section-primary:not(.uk-preserve-color) .uk-heading-divider,.uk-section-secondary:not(.uk-preserve-color) .uk-heading-divider,.uk-tile-primary:not(.uk-preserve-color) .uk-heading-divider,.uk-tile-secondary:not(.uk-preserve-color) .uk-heading-divider{border-bottom-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-heading-bullet::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-heading-bullet::before,.uk-card-secondary.uk-card-body .uk-heading-bullet::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-heading-bullet::before,.uk-light .uk-heading-bullet::before,.uk-offcanvas-bar .uk-heading-bullet::before,.uk-overlay-primary .uk-heading-bullet::before,.uk-section-primary:not(.uk-preserve-color) .uk-heading-bullet::before,.uk-section-secondary:not(.uk-preserve-color) .uk-heading-bullet::before,.uk-tile-primary:not(.uk-preserve-color) .uk-heading-bullet::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-heading-bullet::before{border-left-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-heading-line>::after,.uk-card-primary.uk-card-body .uk-heading-line>::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-heading-line>::after,.uk-card-primary>:not([class*=uk-card-media]) .uk-heading-line>::before,.uk-card-secondary.uk-card-body .uk-heading-line>::after,.uk-card-secondary.uk-card-body .uk-heading-line>::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-heading-line>::after,.uk-card-secondary>:not([class*=uk-card-media]) .uk-heading-line>::before,.uk-light .uk-heading-line>::after,.uk-light .uk-heading-line>::before,.uk-offcanvas-bar .uk-heading-line>::after,.uk-offcanvas-bar .uk-heading-line>::before,.uk-overlay-primary .uk-heading-line>::after,.uk-overlay-primary .uk-heading-line>::before,.uk-section-primary:not(.uk-preserve-color) .uk-heading-line>::after,.uk-section-primary:not(.uk-preserve-color) .uk-heading-line>::before,.uk-section-secondary:not(.uk-preserve-color) .uk-heading-line>::after,.uk-section-secondary:not(.uk-preserve-color) .uk-heading-line>::before,.uk-tile-primary:not(.uk-preserve-color) .uk-heading-line>::after,.uk-tile-primary:not(.uk-preserve-color) .uk-heading-line>::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-heading-line>::after,.uk-tile-secondary:not(.uk-preserve-color) .uk-heading-line>::before{border-bottom-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-divider-icon,.uk-card-primary>:not([class*=uk-card-media]) .uk-divider-icon,.uk-card-secondary.uk-card-body .uk-divider-icon,.uk-card-secondary>:not([class*=uk-card-media]) .uk-divider-icon,.uk-light .uk-divider-icon,.uk-offcanvas-bar .uk-divider-icon,.uk-overlay-primary .uk-divider-icon,.uk-section-primary:not(.uk-preserve-color) .uk-divider-icon,.uk-section-secondary:not(.uk-preserve-color) .uk-divider-icon,.uk-tile-primary:not(.uk-preserve-color) .uk-divider-icon,.uk-tile-secondary:not(.uk-preserve-color) .uk-divider-icon{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.2%29%22%20stroke-width%3D%222%22%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%227%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-card-primary.uk-card-body .uk-divider-icon::after,.uk-card-primary.uk-card-body .uk-divider-icon::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-divider-icon::after,.uk-card-primary>:not([class*=uk-card-media]) .uk-divider-icon::before,.uk-card-secondary.uk-card-body .uk-divider-icon::after,.uk-card-secondary.uk-card-body .uk-divider-icon::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-divider-icon::after,.uk-card-secondary>:not([class*=uk-card-media]) .uk-divider-icon::before,.uk-light .uk-divider-icon::after,.uk-light .uk-divider-icon::before,.uk-offcanvas-bar .uk-divider-icon::after,.uk-offcanvas-bar .uk-divider-icon::before,.uk-overlay-primary .uk-divider-icon::after,.uk-overlay-primary .uk-divider-icon::before,.uk-section-primary:not(.uk-preserve-color) .uk-divider-icon::after,.uk-section-primary:not(.uk-preserve-color) .uk-divider-icon::before,.uk-section-secondary:not(.uk-preserve-color) .uk-divider-icon::after,.uk-section-secondary:not(.uk-preserve-color) .uk-divider-icon::before,.uk-tile-primary:not(.uk-preserve-color) .uk-divider-icon::after,.uk-tile-primary:not(.uk-preserve-color) .uk-divider-icon::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-divider-icon::after,.uk-tile-secondary:not(.uk-preserve-color) .uk-divider-icon::before{border-bottom-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-divider-small::after,.uk-card-primary>:not([class*=uk-card-media]) .uk-divider-small::after,.uk-card-secondary.uk-card-body .uk-divider-small::after,.uk-card-secondary>:not([class*=uk-card-media]) .uk-divider-small::after,.uk-light .uk-divider-small::after,.uk-offcanvas-bar .uk-divider-small::after,.uk-overlay-primary .uk-divider-small::after,.uk-section-primary:not(.uk-preserve-color) .uk-divider-small::after,.uk-section-secondary:not(.uk-preserve-color) .uk-divider-small::after,.uk-tile-primary:not(.uk-preserve-color) .uk-divider-small::after,.uk-tile-secondary:not(.uk-preserve-color) .uk-divider-small::after{border-top-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-divider-vertical,.uk-card-primary>:not([class*=uk-card-media]) .uk-divider-vertical,.uk-card-secondary.uk-card-body .uk-divider-vertical,.uk-card-secondary>:not([class*=uk-card-media]) .uk-divider-vertical,.uk-light .uk-divider-vertical,.uk-offcanvas-bar .uk-divider-vertical,.uk-overlay-primary .uk-divider-vertical,.uk-section-primary:not(.uk-preserve-color) .uk-divider-vertical,.uk-section-secondary:not(.uk-preserve-color) .uk-divider-vertical,.uk-tile-primary:not(.uk-preserve-color) .uk-divider-vertical,.uk-tile-secondary:not(.uk-preserve-color) .uk-divider-vertical{border-left-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-list-muted>::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-list-muted>::before,.uk-card-secondary.uk-card-body .uk-list-muted>::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-list-muted>::before,.uk-light .uk-list-muted>::before,.uk-offcanvas-bar .uk-list-muted>::before,.uk-overlay-primary .uk-list-muted>::before,.uk-section-primary:not(.uk-preserve-color) .uk-list-muted>::before,.uk-section-secondary:not(.uk-preserve-color) .uk-list-muted>::before,.uk-tile-primary:not(.uk-preserve-color) .uk-list-muted>::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-list-muted>::before{color:rgba(255,255,255,.5)!important}.uk-card-primary.uk-card-body .uk-list-emphasis>::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-list-emphasis>::before,.uk-card-secondary.uk-card-body .uk-list-emphasis>::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-list-emphasis>::before,.uk-light .uk-list-emphasis>::before,.uk-offcanvas-bar .uk-list-emphasis>::before,.uk-overlay-primary .uk-list-emphasis>::before,.uk-section-primary:not(.uk-preserve-color) .uk-list-emphasis>::before,.uk-section-secondary:not(.uk-preserve-color) .uk-list-emphasis>::before,.uk-tile-primary:not(.uk-preserve-color) .uk-list-emphasis>::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-list-emphasis>::before{color:#fff!important}.uk-card-primary.uk-card-body .uk-list-primary>::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-list-primary>::before,.uk-card-secondary.uk-card-body .uk-list-primary>::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-list-primary>::before,.uk-light .uk-list-primary>::before,.uk-offcanvas-bar .uk-list-primary>::before,.uk-overlay-primary .uk-list-primary>::before,.uk-section-primary:not(.uk-preserve-color) .uk-list-primary>::before,.uk-section-secondary:not(.uk-preserve-color) .uk-list-primary>::before,.uk-tile-primary:not(.uk-preserve-color) .uk-list-primary>::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-list-primary>::before{color:#fff!important}.uk-card-primary.uk-card-body .uk-list-secondary>::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-list-secondary>::before,.uk-card-secondary.uk-card-body .uk-list-secondary>::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-list-secondary>::before,.uk-light .uk-list-secondary>::before,.uk-offcanvas-bar .uk-list-secondary>::before,.uk-overlay-primary .uk-list-secondary>::before,.uk-section-primary:not(.uk-preserve-color) .uk-list-secondary>::before,.uk-section-secondary:not(.uk-preserve-color) .uk-list-secondary>::before,.uk-tile-primary:not(.uk-preserve-color) .uk-list-secondary>::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-list-secondary>::before{color:#fff!important}.uk-card-primary.uk-card-body .uk-list-bullet>::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-list-bullet>::before,.uk-card-secondary.uk-card-body .uk-list-bullet>::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-list-bullet>::before,.uk-light .uk-list-bullet>::before,.uk-offcanvas-bar .uk-list-bullet>::before,.uk-overlay-primary .uk-list-bullet>::before,.uk-section-primary:not(.uk-preserve-color) .uk-list-bullet>::before,.uk-section-secondary:not(.uk-preserve-color) .uk-list-bullet>::before,.uk-tile-primary:not(.uk-preserve-color) .uk-list-bullet>::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-list-bullet>::before{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22rgba%28255,%20255,%20255,%200.7%29%22%20cx%3D%223%22%20cy%3D%223%22%20r%3D%223%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-card-primary.uk-card-body .uk-list-divider>:nth-child(n+2),.uk-card-primary>:not([class*=uk-card-media]) .uk-list-divider>:nth-child(n+2),.uk-card-secondary.uk-card-body .uk-list-divider>:nth-child(n+2),.uk-card-secondary>:not([class*=uk-card-media]) .uk-list-divider>:nth-child(n+2),.uk-light .uk-list-divider>:nth-child(n+2),.uk-offcanvas-bar .uk-list-divider>:nth-child(n+2),.uk-overlay-primary .uk-list-divider>:nth-child(n+2),.uk-section-primary:not(.uk-preserve-color) .uk-list-divider>:nth-child(n+2),.uk-section-secondary:not(.uk-preserve-color) .uk-list-divider>:nth-child(n+2),.uk-tile-primary:not(.uk-preserve-color) .uk-list-divider>:nth-child(n+2),.uk-tile-secondary:not(.uk-preserve-color) .uk-list-divider>:nth-child(n+2){border-top-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-list-striped>:nth-of-type(odd),.uk-card-primary>:not([class*=uk-card-media]) .uk-list-striped>:nth-of-type(odd),.uk-card-secondary.uk-card-body .uk-list-striped>:nth-of-type(odd),.uk-card-secondary>:not([class*=uk-card-media]) .uk-list-striped>:nth-of-type(odd),.uk-light .uk-list-striped>:nth-of-type(odd),.uk-offcanvas-bar .uk-list-striped>:nth-of-type(odd),.uk-overlay-primary .uk-list-striped>:nth-of-type(odd),.uk-section-primary:not(.uk-preserve-color) .uk-list-striped>:nth-of-type(odd),.uk-section-secondary:not(.uk-preserve-color) .uk-list-striped>:nth-of-type(odd),.uk-tile-primary:not(.uk-preserve-color) .uk-list-striped>:nth-of-type(odd),.uk-tile-secondary:not(.uk-preserve-color) .uk-list-striped>:nth-of-type(odd){border-top-color:rgba(255,255,255,.2);border-bottom-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-list-striped>:nth-of-type(odd),.uk-card-primary>:not([class*=uk-card-media]) .uk-list-striped>:nth-of-type(odd),.uk-card-secondary.uk-card-body .uk-list-striped>:nth-of-type(odd),.uk-card-secondary>:not([class*=uk-card-media]) .uk-list-striped>:nth-of-type(odd),.uk-light .uk-list-striped>:nth-of-type(odd),.uk-offcanvas-bar .uk-list-striped>:nth-of-type(odd),.uk-overlay-primary .uk-list-striped>:nth-of-type(odd),.uk-section-primary:not(.uk-preserve-color) .uk-list-striped>:nth-of-type(odd),.uk-section-secondary:not(.uk-preserve-color) .uk-list-striped>:nth-of-type(odd),.uk-tile-primary:not(.uk-preserve-color) .uk-list-striped>:nth-of-type(odd),.uk-tile-secondary:not(.uk-preserve-color) .uk-list-striped>:nth-of-type(odd){background-color:rgba(255,255,255,.1)}.uk-card-primary.uk-card-body .uk-table th,.uk-card-primary>:not([class*=uk-card-media]) .uk-table th,.uk-card-secondary.uk-card-body .uk-table th,.uk-card-secondary>:not([class*=uk-card-media]) .uk-table th,.uk-light .uk-table th,.uk-offcanvas-bar .uk-table th,.uk-overlay-primary .uk-table th,.uk-section-primary:not(.uk-preserve-color) .uk-table th,.uk-section-secondary:not(.uk-preserve-color) .uk-table th,.uk-tile-primary:not(.uk-preserve-color) .uk-table th,.uk-tile-secondary:not(.uk-preserve-color) .uk-table th{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-table caption,.uk-card-primary>:not([class*=uk-card-media]) .uk-table caption,.uk-card-secondary.uk-card-body .uk-table caption,.uk-card-secondary>:not([class*=uk-card-media]) .uk-table caption,.uk-light .uk-table caption,.uk-offcanvas-bar .uk-table caption,.uk-overlay-primary .uk-table caption,.uk-section-primary:not(.uk-preserve-color) .uk-table caption,.uk-section-secondary:not(.uk-preserve-color) .uk-table caption,.uk-tile-primary:not(.uk-preserve-color) .uk-table caption,.uk-tile-secondary:not(.uk-preserve-color) .uk-table caption{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-table tbody tr.uk-active,.uk-card-primary.uk-card-body .uk-table>tr.uk-active,.uk-card-primary>:not([class*=uk-card-media]) .uk-table tbody tr.uk-active,.uk-card-primary>:not([class*=uk-card-media]) .uk-table>tr.uk-active,.uk-card-secondary.uk-card-body .uk-table tbody tr.uk-active,.uk-card-secondary.uk-card-body .uk-table>tr.uk-active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-table tbody tr.uk-active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-table>tr.uk-active,.uk-light .uk-table tbody tr.uk-active,.uk-light .uk-table>tr.uk-active,.uk-offcanvas-bar .uk-table tbody tr.uk-active,.uk-offcanvas-bar .uk-table>tr.uk-active,.uk-overlay-primary .uk-table tbody tr.uk-active,.uk-overlay-primary .uk-table>tr.uk-active,.uk-section-primary:not(.uk-preserve-color) .uk-table tbody tr.uk-active,.uk-section-primary:not(.uk-preserve-color) .uk-table>tr.uk-active,.uk-section-secondary:not(.uk-preserve-color) .uk-table tbody tr.uk-active,.uk-section-secondary:not(.uk-preserve-color) .uk-table>tr.uk-active,.uk-tile-primary:not(.uk-preserve-color) .uk-table tbody tr.uk-active,.uk-tile-primary:not(.uk-preserve-color) .uk-table>tr.uk-active,.uk-tile-secondary:not(.uk-preserve-color) .uk-table tbody tr.uk-active,.uk-tile-secondary:not(.uk-preserve-color) .uk-table>tr.uk-active{background:rgba(255,255,255,.08)}.uk-card-primary.uk-card-body .uk-table-divider>:first-child>tr:not(:first-child),.uk-card-primary.uk-card-body .uk-table-divider>:not(:first-child)>tr,.uk-card-primary.uk-card-body .uk-table-divider>tr:not(:first-child),.uk-card-primary>:not([class*=uk-card-media]) .uk-table-divider>:first-child>tr:not(:first-child),.uk-card-primary>:not([class*=uk-card-media]) .uk-table-divider>:not(:first-child)>tr,.uk-card-primary>:not([class*=uk-card-media]) .uk-table-divider>tr:not(:first-child),.uk-card-secondary.uk-card-body .uk-table-divider>:first-child>tr:not(:first-child),.uk-card-secondary.uk-card-body .uk-table-divider>:not(:first-child)>tr,.uk-card-secondary.uk-card-body .uk-table-divider>tr:not(:first-child),.uk-card-secondary>:not([class*=uk-card-media]) .uk-table-divider>:first-child>tr:not(:first-child),.uk-card-secondary>:not([class*=uk-card-media]) .uk-table-divider>:not(:first-child)>tr,.uk-card-secondary>:not([class*=uk-card-media]) .uk-table-divider>tr:not(:first-child),.uk-light .uk-table-divider>:first-child>tr:not(:first-child),.uk-light .uk-table-divider>:not(:first-child)>tr,.uk-light .uk-table-divider>tr:not(:first-child),.uk-offcanvas-bar .uk-table-divider>:first-child>tr:not(:first-child),.uk-offcanvas-bar .uk-table-divider>:not(:first-child)>tr,.uk-offcanvas-bar .uk-table-divider>tr:not(:first-child),.uk-overlay-primary .uk-table-divider>:first-child>tr:not(:first-child),.uk-overlay-primary .uk-table-divider>:not(:first-child)>tr,.uk-overlay-primary .uk-table-divider>tr:not(:first-child),.uk-section-primary:not(.uk-preserve-color) .uk-table-divider>:first-child>tr:not(:first-child),.uk-section-primary:not(.uk-preserve-color) .uk-table-divider>:not(:first-child)>tr,.uk-section-primary:not(.uk-preserve-color) .uk-table-divider>tr:not(:first-child),.uk-section-secondary:not(.uk-preserve-color) .uk-table-divider>:first-child>tr:not(:first-child),.uk-section-secondary:not(.uk-preserve-color) .uk-table-divider>:not(:first-child)>tr,.uk-section-secondary:not(.uk-preserve-color) .uk-table-divider>tr:not(:first-child),.uk-tile-primary:not(.uk-preserve-color) .uk-table-divider>:first-child>tr:not(:first-child),.uk-tile-primary:not(.uk-preserve-color) .uk-table-divider>:not(:first-child)>tr,.uk-tile-primary:not(.uk-preserve-color) .uk-table-divider>tr:not(:first-child),.uk-tile-secondary:not(.uk-preserve-color) .uk-table-divider>:first-child>tr:not(:first-child),.uk-tile-secondary:not(.uk-preserve-color) .uk-table-divider>:not(:first-child)>tr,.uk-tile-secondary:not(.uk-preserve-color) .uk-table-divider>tr:not(:first-child){border-top-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-table-striped tbody tr:nth-of-type(odd),.uk-card-primary.uk-card-body .uk-table-striped>tr:nth-of-type(odd),.uk-card-primary>:not([class*=uk-card-media]) .uk-table-striped tbody tr:nth-of-type(odd),.uk-card-primary>:not([class*=uk-card-media]) .uk-table-striped>tr:nth-of-type(odd),.uk-card-secondary.uk-card-body .uk-table-striped tbody tr:nth-of-type(odd),.uk-card-secondary.uk-card-body .uk-table-striped>tr:nth-of-type(odd),.uk-card-secondary>:not([class*=uk-card-media]) .uk-table-striped tbody tr:nth-of-type(odd),.uk-card-secondary>:not([class*=uk-card-media]) .uk-table-striped>tr:nth-of-type(odd),.uk-light .uk-table-striped tbody tr:nth-of-type(odd),.uk-light .uk-table-striped>tr:nth-of-type(odd),.uk-offcanvas-bar .uk-table-striped tbody tr:nth-of-type(odd),.uk-offcanvas-bar .uk-table-striped>tr:nth-of-type(odd),.uk-overlay-primary .uk-table-striped tbody tr:nth-of-type(odd),.uk-overlay-primary .uk-table-striped>tr:nth-of-type(odd),.uk-section-primary:not(.uk-preserve-color) .uk-table-striped tbody tr:nth-of-type(odd),.uk-section-primary:not(.uk-preserve-color) .uk-table-striped>tr:nth-of-type(odd),.uk-section-secondary:not(.uk-preserve-color) .uk-table-striped tbody tr:nth-of-type(odd),.uk-section-secondary:not(.uk-preserve-color) .uk-table-striped>tr:nth-of-type(odd),.uk-tile-primary:not(.uk-preserve-color) .uk-table-striped tbody tr:nth-of-type(odd),.uk-tile-primary:not(.uk-preserve-color) .uk-table-striped>tr:nth-of-type(odd),.uk-tile-secondary:not(.uk-preserve-color) .uk-table-striped tbody tr:nth-of-type(odd),.uk-tile-secondary:not(.uk-preserve-color) .uk-table-striped>tr:nth-of-type(odd){background:rgba(255,255,255,.1);border-top-color:rgba(255,255,255,.2);border-bottom-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-table-hover tbody tr:hover,.uk-card-primary.uk-card-body .uk-table-hover>tr:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-table-hover tbody tr:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-table-hover>tr:hover,.uk-card-secondary.uk-card-body .uk-table-hover tbody tr:hover,.uk-card-secondary.uk-card-body .uk-table-hover>tr:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-table-hover tbody tr:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-table-hover>tr:hover,.uk-light .uk-table-hover tbody tr:hover,.uk-light .uk-table-hover>tr:hover,.uk-offcanvas-bar .uk-table-hover tbody tr:hover,.uk-offcanvas-bar .uk-table-hover>tr:hover,.uk-overlay-primary .uk-table-hover tbody tr:hover,.uk-overlay-primary .uk-table-hover>tr:hover,.uk-section-primary:not(.uk-preserve-color) .uk-table-hover tbody tr:hover,.uk-section-primary:not(.uk-preserve-color) .uk-table-hover>tr:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-table-hover tbody tr:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-table-hover>tr:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-table-hover tbody tr:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-table-hover>tr:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-table-hover tbody tr:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-table-hover>tr:hover{background:rgba(255,255,255,.08)}.uk-card-primary.uk-card-body .uk-icon-link,.uk-card-primary>:not([class*=uk-card-media]) .uk-icon-link,.uk-card-secondary.uk-card-body .uk-icon-link,.uk-card-secondary>:not([class*=uk-card-media]) .uk-icon-link,.uk-light .uk-icon-link,.uk-offcanvas-bar .uk-icon-link,.uk-overlay-primary .uk-icon-link,.uk-section-primary:not(.uk-preserve-color) .uk-icon-link,.uk-section-secondary:not(.uk-preserve-color) .uk-icon-link,.uk-tile-primary:not(.uk-preserve-color) .uk-icon-link,.uk-tile-secondary:not(.uk-preserve-color) .uk-icon-link{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-icon-link:focus,.uk-card-primary.uk-card-body .uk-icon-link:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-icon-link:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-icon-link:hover,.uk-card-secondary.uk-card-body .uk-icon-link:focus,.uk-card-secondary.uk-card-body .uk-icon-link:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-icon-link:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-icon-link:hover,.uk-light .uk-icon-link:focus,.uk-light .uk-icon-link:hover,.uk-offcanvas-bar .uk-icon-link:focus,.uk-offcanvas-bar .uk-icon-link:hover,.uk-overlay-primary .uk-icon-link:focus,.uk-overlay-primary .uk-icon-link:hover,.uk-section-primary:not(.uk-preserve-color) .uk-icon-link:focus,.uk-section-primary:not(.uk-preserve-color) .uk-icon-link:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-icon-link:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-icon-link:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-icon-link:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-icon-link:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-icon-link:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-icon-link:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-active>.uk-icon-link,.uk-card-primary.uk-card-body .uk-icon-link:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-active>.uk-icon-link,.uk-card-primary>:not([class*=uk-card-media]) .uk-icon-link:active,.uk-card-secondary.uk-card-body .uk-active>.uk-icon-link,.uk-card-secondary.uk-card-body .uk-icon-link:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-active>.uk-icon-link,.uk-card-secondary>:not([class*=uk-card-media]) .uk-icon-link:active,.uk-light .uk-active>.uk-icon-link,.uk-light .uk-icon-link:active,.uk-offcanvas-bar .uk-active>.uk-icon-link,.uk-offcanvas-bar .uk-icon-link:active,.uk-overlay-primary .uk-active>.uk-icon-link,.uk-overlay-primary .uk-icon-link:active,.uk-section-primary:not(.uk-preserve-color) .uk-active>.uk-icon-link,.uk-section-primary:not(.uk-preserve-color) .uk-icon-link:active,.uk-section-secondary:not(.uk-preserve-color) .uk-active>.uk-icon-link,.uk-section-secondary:not(.uk-preserve-color) .uk-icon-link:active,.uk-tile-primary:not(.uk-preserve-color) .uk-active>.uk-icon-link,.uk-tile-primary:not(.uk-preserve-color) .uk-icon-link:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-active>.uk-icon-link,.uk-tile-secondary:not(.uk-preserve-color) .uk-icon-link:active{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-icon-button,.uk-card-primary>:not([class*=uk-card-media]) .uk-icon-button,.uk-card-secondary.uk-card-body .uk-icon-button,.uk-card-secondary>:not([class*=uk-card-media]) .uk-icon-button,.uk-light .uk-icon-button,.uk-offcanvas-bar .uk-icon-button,.uk-overlay-primary .uk-icon-button,.uk-section-primary:not(.uk-preserve-color) .uk-icon-button,.uk-section-secondary:not(.uk-preserve-color) .uk-icon-button,.uk-tile-primary:not(.uk-preserve-color) .uk-icon-button,.uk-tile-secondary:not(.uk-preserve-color) .uk-icon-button{background-color:rgba(255,255,255,.1);color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-icon-button:focus,.uk-card-primary.uk-card-body .uk-icon-button:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-icon-button:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-icon-button:hover,.uk-card-secondary.uk-card-body .uk-icon-button:focus,.uk-card-secondary.uk-card-body .uk-icon-button:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-icon-button:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-icon-button:hover,.uk-light .uk-icon-button:focus,.uk-light .uk-icon-button:hover,.uk-offcanvas-bar .uk-icon-button:focus,.uk-offcanvas-bar .uk-icon-button:hover,.uk-overlay-primary .uk-icon-button:focus,.uk-overlay-primary .uk-icon-button:hover,.uk-section-primary:not(.uk-preserve-color) .uk-icon-button:focus,.uk-section-primary:not(.uk-preserve-color) .uk-icon-button:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-icon-button:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-icon-button:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-icon-button:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-icon-button:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-icon-button:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-icon-button:hover{background-color:rgba(242,242,242,.1);color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-icon-button:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-icon-button:active,.uk-card-secondary.uk-card-body .uk-icon-button:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-icon-button:active,.uk-light .uk-icon-button:active,.uk-offcanvas-bar .uk-icon-button:active,.uk-overlay-primary .uk-icon-button:active,.uk-section-primary:not(.uk-preserve-color) .uk-icon-button:active,.uk-section-secondary:not(.uk-preserve-color) .uk-icon-button:active,.uk-tile-primary:not(.uk-preserve-color) .uk-icon-button:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-icon-button:active{background-color:rgba(230,230,230,.1);color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-input,.uk-card-primary.uk-card-body .uk-select,.uk-card-primary.uk-card-body .uk-textarea,.uk-card-primary>:not([class*=uk-card-media]) .uk-input,.uk-card-primary>:not([class*=uk-card-media]) .uk-select,.uk-card-primary>:not([class*=uk-card-media]) .uk-textarea,.uk-card-secondary.uk-card-body .uk-input,.uk-card-secondary.uk-card-body .uk-select,.uk-card-secondary.uk-card-body .uk-textarea,.uk-card-secondary>:not([class*=uk-card-media]) .uk-input,.uk-card-secondary>:not([class*=uk-card-media]) .uk-select,.uk-card-secondary>:not([class*=uk-card-media]) .uk-textarea,.uk-light .uk-input,.uk-light .uk-select,.uk-light .uk-textarea,.uk-offcanvas-bar .uk-input,.uk-offcanvas-bar .uk-select,.uk-offcanvas-bar .uk-textarea,.uk-overlay-primary .uk-input,.uk-overlay-primary .uk-select,.uk-overlay-primary .uk-textarea,.uk-section-primary:not(.uk-preserve-color) .uk-input,.uk-section-primary:not(.uk-preserve-color) .uk-select,.uk-section-primary:not(.uk-preserve-color) .uk-textarea,.uk-section-secondary:not(.uk-preserve-color) .uk-input,.uk-section-secondary:not(.uk-preserve-color) .uk-select,.uk-section-secondary:not(.uk-preserve-color) .uk-textarea,.uk-tile-primary:not(.uk-preserve-color) .uk-input,.uk-tile-primary:not(.uk-preserve-color) .uk-select,.uk-tile-primary:not(.uk-preserve-color) .uk-textarea,.uk-tile-secondary:not(.uk-preserve-color) .uk-input,.uk-tile-secondary:not(.uk-preserve-color) .uk-select,.uk-tile-secondary:not(.uk-preserve-color) .uk-textarea{background-color:rgba(255,255,255,.1);color:rgba(255,255,255,.7);background-clip:padding-box;border-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-input:focus,.uk-card-primary.uk-card-body .uk-select:focus,.uk-card-primary.uk-card-body .uk-textarea:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-input:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-select:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-textarea:focus,.uk-card-secondary.uk-card-body .uk-input:focus,.uk-card-secondary.uk-card-body .uk-select:focus,.uk-card-secondary.uk-card-body .uk-textarea:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-input:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-select:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-textarea:focus,.uk-light .uk-input:focus,.uk-light .uk-select:focus,.uk-light .uk-textarea:focus,.uk-offcanvas-bar .uk-input:focus,.uk-offcanvas-bar .uk-select:focus,.uk-offcanvas-bar .uk-textarea:focus,.uk-overlay-primary .uk-input:focus,.uk-overlay-primary .uk-select:focus,.uk-overlay-primary .uk-textarea:focus,.uk-section-primary:not(.uk-preserve-color) .uk-input:focus,.uk-section-primary:not(.uk-preserve-color) .uk-select:focus,.uk-section-primary:not(.uk-preserve-color) .uk-textarea:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-input:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-select:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-textarea:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-input:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-select:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-textarea:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-input:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-select:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-textarea:focus{background-color:rgba(255,255,255,.1);color:rgba(255,255,255,.7);border-color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-input::-ms-input-placeholder,.uk-card-primary>:not([class*=uk-card-media]) .uk-input::-ms-input-placeholder,.uk-card-secondary.uk-card-body .uk-input::-ms-input-placeholder,.uk-card-secondary>:not([class*=uk-card-media]) .uk-input::-ms-input-placeholder,.uk-light .uk-input::-ms-input-placeholder,.uk-offcanvas-bar .uk-input::-ms-input-placeholder,.uk-overlay-primary .uk-input::-ms-input-placeholder,.uk-section-primary:not(.uk-preserve-color) .uk-input::-ms-input-placeholder,.uk-section-secondary:not(.uk-preserve-color) .uk-input::-ms-input-placeholder,.uk-tile-primary:not(.uk-preserve-color) .uk-input::-ms-input-placeholder,.uk-tile-secondary:not(.uk-preserve-color) .uk-input::-ms-input-placeholder{color:rgba(255,255,255,.5)!important}.uk-card-primary.uk-card-body .uk-input::placeholder,.uk-card-primary>:not([class*=uk-card-media]) .uk-input::placeholder,.uk-card-secondary.uk-card-body .uk-input::placeholder,.uk-card-secondary>:not([class*=uk-card-media]) .uk-input::placeholder,.uk-light .uk-input::placeholder,.uk-offcanvas-bar .uk-input::placeholder,.uk-overlay-primary .uk-input::placeholder,.uk-section-primary:not(.uk-preserve-color) .uk-input::placeholder,.uk-section-secondary:not(.uk-preserve-color) .uk-input::placeholder,.uk-tile-primary:not(.uk-preserve-color) .uk-input::placeholder,.uk-tile-secondary:not(.uk-preserve-color) .uk-input::placeholder{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-textarea::-ms-input-placeholder,.uk-card-primary>:not([class*=uk-card-media]) .uk-textarea::-ms-input-placeholder,.uk-card-secondary.uk-card-body .uk-textarea::-ms-input-placeholder,.uk-card-secondary>:not([class*=uk-card-media]) .uk-textarea::-ms-input-placeholder,.uk-light .uk-textarea::-ms-input-placeholder,.uk-offcanvas-bar .uk-textarea::-ms-input-placeholder,.uk-overlay-primary .uk-textarea::-ms-input-placeholder,.uk-section-primary:not(.uk-preserve-color) .uk-textarea::-ms-input-placeholder,.uk-section-secondary:not(.uk-preserve-color) .uk-textarea::-ms-input-placeholder,.uk-tile-primary:not(.uk-preserve-color) .uk-textarea::-ms-input-placeholder,.uk-tile-secondary:not(.uk-preserve-color) .uk-textarea::-ms-input-placeholder{color:rgba(255,255,255,.5)!important}.uk-card-primary.uk-card-body .uk-textarea::placeholder,.uk-card-primary>:not([class*=uk-card-media]) .uk-textarea::placeholder,.uk-card-secondary.uk-card-body .uk-textarea::placeholder,.uk-card-secondary>:not([class*=uk-card-media]) .uk-textarea::placeholder,.uk-light .uk-textarea::placeholder,.uk-offcanvas-bar .uk-textarea::placeholder,.uk-overlay-primary .uk-textarea::placeholder,.uk-section-primary:not(.uk-preserve-color) .uk-textarea::placeholder,.uk-section-secondary:not(.uk-preserve-color) .uk-textarea::placeholder,.uk-tile-primary:not(.uk-preserve-color) .uk-textarea::placeholder,.uk-tile-secondary:not(.uk-preserve-color) .uk-textarea::placeholder{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-select:not([multiple]):not([size]),.uk-card-primary>:not([class*=uk-card-media]) .uk-select:not([multiple]):not([size]),.uk-card-secondary.uk-card-body .uk-select:not([multiple]):not([size]),.uk-card-secondary>:not([class*=uk-card-media]) .uk-select:not([multiple]):not([size]),.uk-light .uk-select:not([multiple]):not([size]),.uk-offcanvas-bar .uk-select:not([multiple]):not([size]),.uk-overlay-primary .uk-select:not([multiple]):not([size]),.uk-section-primary:not(.uk-preserve-color) .uk-select:not([multiple]):not([size]),.uk-section-secondary:not(.uk-preserve-color) .uk-select:not([multiple]):not([size]),.uk-tile-primary:not(.uk-preserve-color) .uk-select:not([multiple]):not([size]),.uk-tile-secondary:not(.uk-preserve-color) .uk-select:not([multiple]):not([size]){background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22rgba%28255,%20255,%20255,%200.7%29%22%20points%3D%2212%201%209%206%2015%206%22%20%2F%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22rgba%28255,%20255,%20255,%200.7%29%22%20points%3D%2212%2013%209%208%2015%208%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-card-primary.uk-card-body .uk-input[list]:focus,.uk-card-primary.uk-card-body .uk-input[list]:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-input[list]:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-input[list]:hover,.uk-card-secondary.uk-card-body .uk-input[list]:focus,.uk-card-secondary.uk-card-body .uk-input[list]:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-input[list]:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-input[list]:hover,.uk-light .uk-input[list]:focus,.uk-light .uk-input[list]:hover,.uk-offcanvas-bar .uk-input[list]:focus,.uk-offcanvas-bar .uk-input[list]:hover,.uk-overlay-primary .uk-input[list]:focus,.uk-overlay-primary .uk-input[list]:hover,.uk-section-primary:not(.uk-preserve-color) .uk-input[list]:focus,.uk-section-primary:not(.uk-preserve-color) .uk-input[list]:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-input[list]:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-input[list]:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-input[list]:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-input[list]:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-input[list]:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-input[list]:hover{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2216%22%20viewBox%3D%220%200%2024%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22rgba%28255,%20255,%20255,%200.7%29%22%20points%3D%2212%2012%208%206%2016%206%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-card-primary.uk-card-body .uk-checkbox,.uk-card-primary.uk-card-body .uk-radio,.uk-card-primary>:not([class*=uk-card-media]) .uk-checkbox,.uk-card-primary>:not([class*=uk-card-media]) .uk-radio,.uk-card-secondary.uk-card-body .uk-checkbox,.uk-card-secondary.uk-card-body .uk-radio,.uk-card-secondary>:not([class*=uk-card-media]) .uk-checkbox,.uk-card-secondary>:not([class*=uk-card-media]) .uk-radio,.uk-light .uk-checkbox,.uk-light .uk-radio,.uk-offcanvas-bar .uk-checkbox,.uk-offcanvas-bar .uk-radio,.uk-overlay-primary .uk-checkbox,.uk-overlay-primary .uk-radio,.uk-section-primary:not(.uk-preserve-color) .uk-checkbox,.uk-section-primary:not(.uk-preserve-color) .uk-radio,.uk-section-secondary:not(.uk-preserve-color) .uk-checkbox,.uk-section-secondary:not(.uk-preserve-color) .uk-radio,.uk-tile-primary:not(.uk-preserve-color) .uk-checkbox,.uk-tile-primary:not(.uk-preserve-color) .uk-radio,.uk-tile-secondary:not(.uk-preserve-color) .uk-checkbox,.uk-tile-secondary:not(.uk-preserve-color) .uk-radio{background-color:rgba(242,242,242,.1);border-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-checkbox:focus,.uk-card-primary.uk-card-body .uk-radio:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-checkbox:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-radio:focus,.uk-card-secondary.uk-card-body .uk-checkbox:focus,.uk-card-secondary.uk-card-body .uk-radio:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-checkbox:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-radio:focus,.uk-light .uk-checkbox:focus,.uk-light .uk-radio:focus,.uk-offcanvas-bar .uk-checkbox:focus,.uk-offcanvas-bar .uk-radio:focus,.uk-overlay-primary .uk-checkbox:focus,.uk-overlay-primary .uk-radio:focus,.uk-section-primary:not(.uk-preserve-color) .uk-checkbox:focus,.uk-section-primary:not(.uk-preserve-color) .uk-radio:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-checkbox:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-radio:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-checkbox:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-radio:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-checkbox:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-radio:focus{border-color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-checkbox:checked,.uk-card-primary.uk-card-body .uk-checkbox:indeterminate,.uk-card-primary.uk-card-body .uk-radio:checked,.uk-card-primary>:not([class*=uk-card-media]) .uk-checkbox:checked,.uk-card-primary>:not([class*=uk-card-media]) .uk-checkbox:indeterminate,.uk-card-primary>:not([class*=uk-card-media]) .uk-radio:checked,.uk-card-secondary.uk-card-body .uk-checkbox:checked,.uk-card-secondary.uk-card-body .uk-checkbox:indeterminate,.uk-card-secondary.uk-card-body .uk-radio:checked,.uk-card-secondary>:not([class*=uk-card-media]) .uk-checkbox:checked,.uk-card-secondary>:not([class*=uk-card-media]) .uk-checkbox:indeterminate,.uk-card-secondary>:not([class*=uk-card-media]) .uk-radio:checked,.uk-light .uk-checkbox:checked,.uk-light .uk-checkbox:indeterminate,.uk-light .uk-radio:checked,.uk-offcanvas-bar .uk-checkbox:checked,.uk-offcanvas-bar .uk-checkbox:indeterminate,.uk-offcanvas-bar .uk-radio:checked,.uk-overlay-primary .uk-checkbox:checked,.uk-overlay-primary .uk-checkbox:indeterminate,.uk-overlay-primary .uk-radio:checked,.uk-section-primary:not(.uk-preserve-color) .uk-checkbox:checked,.uk-section-primary:not(.uk-preserve-color) .uk-checkbox:indeterminate,.uk-section-primary:not(.uk-preserve-color) .uk-radio:checked,.uk-section-secondary:not(.uk-preserve-color) .uk-checkbox:checked,.uk-section-secondary:not(.uk-preserve-color) .uk-checkbox:indeterminate,.uk-section-secondary:not(.uk-preserve-color) .uk-radio:checked,.uk-tile-primary:not(.uk-preserve-color) .uk-checkbox:checked,.uk-tile-primary:not(.uk-preserve-color) .uk-checkbox:indeterminate,.uk-tile-primary:not(.uk-preserve-color) .uk-radio:checked,.uk-tile-secondary:not(.uk-preserve-color) .uk-checkbox:checked,.uk-tile-secondary:not(.uk-preserve-color) .uk-checkbox:indeterminate,.uk-tile-secondary:not(.uk-preserve-color) .uk-radio:checked{background-color:#fff;border-color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-checkbox:checked:focus,.uk-card-primary.uk-card-body .uk-checkbox:indeterminate:focus,.uk-card-primary.uk-card-body .uk-radio:checked:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-checkbox:checked:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-checkbox:indeterminate:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-radio:checked:focus,.uk-card-secondary.uk-card-body .uk-checkbox:checked:focus,.uk-card-secondary.uk-card-body .uk-checkbox:indeterminate:focus,.uk-card-secondary.uk-card-body .uk-radio:checked:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-checkbox:checked:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-checkbox:indeterminate:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-radio:checked:focus,.uk-light .uk-checkbox:checked:focus,.uk-light .uk-checkbox:indeterminate:focus,.uk-light .uk-radio:checked:focus,.uk-offcanvas-bar .uk-checkbox:checked:focus,.uk-offcanvas-bar .uk-checkbox:indeterminate:focus,.uk-offcanvas-bar .uk-radio:checked:focus,.uk-overlay-primary .uk-checkbox:checked:focus,.uk-overlay-primary .uk-checkbox:indeterminate:focus,.uk-overlay-primary .uk-radio:checked:focus,.uk-section-primary:not(.uk-preserve-color) .uk-checkbox:checked:focus,.uk-section-primary:not(.uk-preserve-color) .uk-checkbox:indeterminate:focus,.uk-section-primary:not(.uk-preserve-color) .uk-radio:checked:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-checkbox:checked:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-checkbox:indeterminate:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-radio:checked:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-checkbox:checked:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-checkbox:indeterminate:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-radio:checked:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-checkbox:checked:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-checkbox:indeterminate:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-radio:checked:focus{background-color:#e6e6e6}.uk-card-primary.uk-card-body .uk-radio:checked,.uk-card-primary>:not([class*=uk-card-media]) .uk-radio:checked,.uk-card-secondary.uk-card-body .uk-radio:checked,.uk-card-secondary>:not([class*=uk-card-media]) .uk-radio:checked,.uk-light .uk-radio:checked,.uk-offcanvas-bar .uk-radio:checked,.uk-overlay-primary .uk-radio:checked,.uk-section-primary:not(.uk-preserve-color) .uk-radio:checked,.uk-section-secondary:not(.uk-preserve-color) .uk-radio:checked,.uk-tile-primary:not(.uk-preserve-color) .uk-radio:checked,.uk-tile-secondary:not(.uk-preserve-color) .uk-radio:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Ccircle%20fill%3D%22%23666%22%20cx%3D%228%22%20cy%3D%228%22%20r%3D%222%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-card-primary.uk-card-body .uk-checkbox:checked,.uk-card-primary>:not([class*=uk-card-media]) .uk-checkbox:checked,.uk-card-secondary.uk-card-body .uk-checkbox:checked,.uk-card-secondary>:not([class*=uk-card-media]) .uk-checkbox:checked,.uk-light .uk-checkbox:checked,.uk-offcanvas-bar .uk-checkbox:checked,.uk-overlay-primary .uk-checkbox:checked,.uk-section-primary:not(.uk-preserve-color) .uk-checkbox:checked,.uk-section-secondary:not(.uk-preserve-color) .uk-checkbox:checked,.uk-tile-primary:not(.uk-preserve-color) .uk-checkbox:checked,.uk-tile-secondary:not(.uk-preserve-color) .uk-checkbox:checked{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2211%22%20viewBox%3D%220%200%2014%2011%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolygon%20fill%3D%22%23666%22%20points%3D%2212%201%205%207.5%202%205%201%205.5%205%2010%2013%201.5%22%20%2F%3E%0A%3C%2Fsvg%3E%0A")}.uk-card-primary.uk-card-body .uk-checkbox:indeterminate,.uk-card-primary>:not([class*=uk-card-media]) .uk-checkbox:indeterminate,.uk-card-secondary.uk-card-body .uk-checkbox:indeterminate,.uk-card-secondary>:not([class*=uk-card-media]) .uk-checkbox:indeterminate,.uk-light .uk-checkbox:indeterminate,.uk-offcanvas-bar .uk-checkbox:indeterminate,.uk-overlay-primary .uk-checkbox:indeterminate,.uk-section-primary:not(.uk-preserve-color) .uk-checkbox:indeterminate,.uk-section-secondary:not(.uk-preserve-color) .uk-checkbox:indeterminate,.uk-tile-primary:not(.uk-preserve-color) .uk-checkbox:indeterminate,.uk-tile-secondary:not(.uk-preserve-color) .uk-checkbox:indeterminate{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22%23666%22%20x%3D%223%22%20y%3D%228%22%20width%3D%2210%22%20height%3D%221%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-card-primary.uk-card-body .uk-form-label,.uk-card-primary>:not([class*=uk-card-media]) .uk-form-label,.uk-card-secondary.uk-card-body .uk-form-label,.uk-card-secondary>:not([class*=uk-card-media]) .uk-form-label,.uk-light .uk-form-label,.uk-offcanvas-bar .uk-form-label,.uk-overlay-primary .uk-form-label,.uk-section-primary:not(.uk-preserve-color) .uk-form-label,.uk-section-secondary:not(.uk-preserve-color) .uk-form-label,.uk-tile-primary:not(.uk-preserve-color) .uk-form-label,.uk-tile-secondary:not(.uk-preserve-color) .uk-form-label{color:#fff}.uk-card-primary.uk-card-body .uk-form-icon,.uk-card-primary>:not([class*=uk-card-media]) .uk-form-icon,.uk-card-secondary.uk-card-body .uk-form-icon,.uk-card-secondary>:not([class*=uk-card-media]) .uk-form-icon,.uk-light .uk-form-icon,.uk-offcanvas-bar .uk-form-icon,.uk-overlay-primary .uk-form-icon,.uk-section-primary:not(.uk-preserve-color) .uk-form-icon,.uk-section-secondary:not(.uk-preserve-color) .uk-form-icon,.uk-tile-primary:not(.uk-preserve-color) .uk-form-icon,.uk-tile-secondary:not(.uk-preserve-color) .uk-form-icon{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-form-icon:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-form-icon:hover,.uk-card-secondary.uk-card-body .uk-form-icon:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-form-icon:hover,.uk-light .uk-form-icon:hover,.uk-offcanvas-bar .uk-form-icon:hover,.uk-overlay-primary .uk-form-icon:hover,.uk-section-primary:not(.uk-preserve-color) .uk-form-icon:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-form-icon:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-form-icon:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-form-icon:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-button-default,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-default,.uk-card-secondary.uk-card-body .uk-button-default,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-default,.uk-light .uk-button-default,.uk-offcanvas-bar .uk-button-default,.uk-overlay-primary .uk-button-default,.uk-section-primary:not(.uk-preserve-color) .uk-button-default,.uk-section-secondary:not(.uk-preserve-color) .uk-button-default,.uk-tile-primary:not(.uk-preserve-color) .uk-button-default,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-default{background-color:transparent;color:#fff;border-color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-button-default:focus,.uk-card-primary.uk-card-body .uk-button-default:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-default:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-default:hover,.uk-card-secondary.uk-card-body .uk-button-default:focus,.uk-card-secondary.uk-card-body .uk-button-default:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-default:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-default:hover,.uk-light .uk-button-default:focus,.uk-light .uk-button-default:hover,.uk-offcanvas-bar .uk-button-default:focus,.uk-offcanvas-bar .uk-button-default:hover,.uk-overlay-primary .uk-button-default:focus,.uk-overlay-primary .uk-button-default:hover,.uk-section-primary:not(.uk-preserve-color) .uk-button-default:focus,.uk-section-primary:not(.uk-preserve-color) .uk-button-default:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-button-default:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-button-default:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-button-default:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-button-default:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-default:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-default:hover{background-color:transparent;color:#fff;border-color:#fff}.uk-card-primary.uk-card-body .uk-button-default.uk-active,.uk-card-primary.uk-card-body .uk-button-default:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-default.uk-active,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-default:active,.uk-card-secondary.uk-card-body .uk-button-default.uk-active,.uk-card-secondary.uk-card-body .uk-button-default:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-default.uk-active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-default:active,.uk-light .uk-button-default.uk-active,.uk-light .uk-button-default:active,.uk-offcanvas-bar .uk-button-default.uk-active,.uk-offcanvas-bar .uk-button-default:active,.uk-overlay-primary .uk-button-default.uk-active,.uk-overlay-primary .uk-button-default:active,.uk-section-primary:not(.uk-preserve-color) .uk-button-default.uk-active,.uk-section-primary:not(.uk-preserve-color) .uk-button-default:active,.uk-section-secondary:not(.uk-preserve-color) .uk-button-default.uk-active,.uk-section-secondary:not(.uk-preserve-color) .uk-button-default:active,.uk-tile-primary:not(.uk-preserve-color) .uk-button-default.uk-active,.uk-tile-primary:not(.uk-preserve-color) .uk-button-default:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-default.uk-active,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-default:active{background-color:transparent;color:#fff;border-color:#fff}.uk-card-primary.uk-card-body .uk-button-primary,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-primary,.uk-card-secondary.uk-card-body .uk-button-primary,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-primary,.uk-light .uk-button-primary,.uk-offcanvas-bar .uk-button-primary,.uk-overlay-primary .uk-button-primary,.uk-section-primary:not(.uk-preserve-color) .uk-button-primary,.uk-section-secondary:not(.uk-preserve-color) .uk-button-primary,.uk-tile-primary:not(.uk-preserve-color) .uk-button-primary,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-primary{background-color:#fff;color:#666}.uk-card-primary.uk-card-body .uk-button-primary:focus,.uk-card-primary.uk-card-body .uk-button-primary:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-primary:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-primary:hover,.uk-card-secondary.uk-card-body .uk-button-primary:focus,.uk-card-secondary.uk-card-body .uk-button-primary:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-primary:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-primary:hover,.uk-light .uk-button-primary:focus,.uk-light .uk-button-primary:hover,.uk-offcanvas-bar .uk-button-primary:focus,.uk-offcanvas-bar .uk-button-primary:hover,.uk-overlay-primary .uk-button-primary:focus,.uk-overlay-primary .uk-button-primary:hover,.uk-section-primary:not(.uk-preserve-color) .uk-button-primary:focus,.uk-section-primary:not(.uk-preserve-color) .uk-button-primary:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-button-primary:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-button-primary:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-button-primary:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-button-primary:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-primary:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-primary:hover{background-color:#f2f2f2;color:#666}.uk-card-primary.uk-card-body .uk-button-primary.uk-active,.uk-card-primary.uk-card-body .uk-button-primary:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-primary.uk-active,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-primary:active,.uk-card-secondary.uk-card-body .uk-button-primary.uk-active,.uk-card-secondary.uk-card-body .uk-button-primary:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-primary.uk-active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-primary:active,.uk-light .uk-button-primary.uk-active,.uk-light .uk-button-primary:active,.uk-offcanvas-bar .uk-button-primary.uk-active,.uk-offcanvas-bar .uk-button-primary:active,.uk-overlay-primary .uk-button-primary.uk-active,.uk-overlay-primary .uk-button-primary:active,.uk-section-primary:not(.uk-preserve-color) .uk-button-primary.uk-active,.uk-section-primary:not(.uk-preserve-color) .uk-button-primary:active,.uk-section-secondary:not(.uk-preserve-color) .uk-button-primary.uk-active,.uk-section-secondary:not(.uk-preserve-color) .uk-button-primary:active,.uk-tile-primary:not(.uk-preserve-color) .uk-button-primary.uk-active,.uk-tile-primary:not(.uk-preserve-color) .uk-button-primary:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-primary.uk-active,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-primary:active{background-color:#e6e6e6;color:#666}.uk-card-primary.uk-card-body .uk-button-secondary,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-secondary,.uk-card-secondary.uk-card-body .uk-button-secondary,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-secondary,.uk-light .uk-button-secondary,.uk-offcanvas-bar .uk-button-secondary,.uk-overlay-primary .uk-button-secondary,.uk-section-primary:not(.uk-preserve-color) .uk-button-secondary,.uk-section-secondary:not(.uk-preserve-color) .uk-button-secondary,.uk-tile-primary:not(.uk-preserve-color) .uk-button-secondary,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-secondary{background-color:#fff;color:#666}.uk-card-primary.uk-card-body .uk-button-secondary:focus,.uk-card-primary.uk-card-body .uk-button-secondary:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-secondary:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-secondary:hover,.uk-card-secondary.uk-card-body .uk-button-secondary:focus,.uk-card-secondary.uk-card-body .uk-button-secondary:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-secondary:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-secondary:hover,.uk-light .uk-button-secondary:focus,.uk-light .uk-button-secondary:hover,.uk-offcanvas-bar .uk-button-secondary:focus,.uk-offcanvas-bar .uk-button-secondary:hover,.uk-overlay-primary .uk-button-secondary:focus,.uk-overlay-primary .uk-button-secondary:hover,.uk-section-primary:not(.uk-preserve-color) .uk-button-secondary:focus,.uk-section-primary:not(.uk-preserve-color) .uk-button-secondary:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-button-secondary:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-button-secondary:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-button-secondary:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-button-secondary:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-secondary:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-secondary:hover{background-color:#f2f2f2;color:#666}.uk-card-primary.uk-card-body .uk-button-secondary.uk-active,.uk-card-primary.uk-card-body .uk-button-secondary:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-secondary.uk-active,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-secondary:active,.uk-card-secondary.uk-card-body .uk-button-secondary.uk-active,.uk-card-secondary.uk-card-body .uk-button-secondary:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-secondary.uk-active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-secondary:active,.uk-light .uk-button-secondary.uk-active,.uk-light .uk-button-secondary:active,.uk-offcanvas-bar .uk-button-secondary.uk-active,.uk-offcanvas-bar .uk-button-secondary:active,.uk-overlay-primary .uk-button-secondary.uk-active,.uk-overlay-primary .uk-button-secondary:active,.uk-section-primary:not(.uk-preserve-color) .uk-button-secondary.uk-active,.uk-section-primary:not(.uk-preserve-color) .uk-button-secondary:active,.uk-section-secondary:not(.uk-preserve-color) .uk-button-secondary.uk-active,.uk-section-secondary:not(.uk-preserve-color) .uk-button-secondary:active,.uk-tile-primary:not(.uk-preserve-color) .uk-button-secondary.uk-active,.uk-tile-primary:not(.uk-preserve-color) .uk-button-secondary:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-secondary.uk-active,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-secondary:active{background-color:#e6e6e6;color:#666}.uk-card-primary.uk-card-body .uk-button-text,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-text,.uk-card-secondary.uk-card-body .uk-button-text,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-text,.uk-light .uk-button-text,.uk-offcanvas-bar .uk-button-text,.uk-overlay-primary .uk-button-text,.uk-section-primary:not(.uk-preserve-color) .uk-button-text,.uk-section-secondary:not(.uk-preserve-color) .uk-button-text,.uk-tile-primary:not(.uk-preserve-color) .uk-button-text,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-text{color:#fff}.uk-card-primary.uk-card-body .uk-button-text::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-text::before,.uk-card-secondary.uk-card-body .uk-button-text::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-text::before,.uk-light .uk-button-text::before,.uk-offcanvas-bar .uk-button-text::before,.uk-overlay-primary .uk-button-text::before,.uk-section-primary:not(.uk-preserve-color) .uk-button-text::before,.uk-section-secondary:not(.uk-preserve-color) .uk-button-text::before,.uk-tile-primary:not(.uk-preserve-color) .uk-button-text::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-text::before{border-bottom-color:#fff}.uk-card-primary.uk-card-body .uk-button-text:focus,.uk-card-primary.uk-card-body .uk-button-text:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-text:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-text:hover,.uk-card-secondary.uk-card-body .uk-button-text:focus,.uk-card-secondary.uk-card-body .uk-button-text:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-text:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-text:hover,.uk-light .uk-button-text:focus,.uk-light .uk-button-text:hover,.uk-offcanvas-bar .uk-button-text:focus,.uk-offcanvas-bar .uk-button-text:hover,.uk-overlay-primary .uk-button-text:focus,.uk-overlay-primary .uk-button-text:hover,.uk-section-primary:not(.uk-preserve-color) .uk-button-text:focus,.uk-section-primary:not(.uk-preserve-color) .uk-button-text:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-button-text:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-button-text:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-button-text:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-button-text:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-text:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-text:hover{color:#fff}.uk-card-primary.uk-card-body .uk-button-text:disabled,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-text:disabled,.uk-card-secondary.uk-card-body .uk-button-text:disabled,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-text:disabled,.uk-light .uk-button-text:disabled,.uk-offcanvas-bar .uk-button-text:disabled,.uk-overlay-primary .uk-button-text:disabled,.uk-section-primary:not(.uk-preserve-color) .uk-button-text:disabled,.uk-section-secondary:not(.uk-preserve-color) .uk-button-text:disabled,.uk-tile-primary:not(.uk-preserve-color) .uk-button-text:disabled,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-text:disabled{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-button-link,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-link,.uk-card-secondary.uk-card-body .uk-button-link,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-link,.uk-light .uk-button-link,.uk-offcanvas-bar .uk-button-link,.uk-overlay-primary .uk-button-link,.uk-section-primary:not(.uk-preserve-color) .uk-button-link,.uk-section-secondary:not(.uk-preserve-color) .uk-button-link,.uk-tile-primary:not(.uk-preserve-color) .uk-button-link,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-link{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-button-link:focus,.uk-card-primary.uk-card-body .uk-button-link:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-link:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-button-link:hover,.uk-card-secondary.uk-card-body .uk-button-link:focus,.uk-card-secondary.uk-card-body .uk-button-link:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-link:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-button-link:hover,.uk-light .uk-button-link:focus,.uk-light .uk-button-link:hover,.uk-offcanvas-bar .uk-button-link:focus,.uk-offcanvas-bar .uk-button-link:hover,.uk-overlay-primary .uk-button-link:focus,.uk-overlay-primary .uk-button-link:hover,.uk-section-primary:not(.uk-preserve-color) .uk-button-link:focus,.uk-section-primary:not(.uk-preserve-color) .uk-button-link:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-button-link:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-button-link:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-button-link:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-button-link:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-link:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-button-link:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body.uk-card-badge,.uk-card-primary>:not([class*=uk-card-media]).uk-card-badge,.uk-card-secondary.uk-card-body.uk-card-badge,.uk-card-secondary>:not([class*=uk-card-media]).uk-card-badge,.uk-light.uk-card-badge,.uk-offcanvas-bar.uk-card-badge,.uk-overlay-primary.uk-card-badge,.uk-section-primary:not(.uk-preserve-color).uk-card-badge,.uk-section-secondary:not(.uk-preserve-color).uk-card-badge,.uk-tile-primary:not(.uk-preserve-color).uk-card-badge,.uk-tile-secondary:not(.uk-preserve-color).uk-card-badge{background-color:#fff;color:#666}.uk-card-primary.uk-card-body .uk-close,.uk-card-primary>:not([class*=uk-card-media]) .uk-close,.uk-card-secondary.uk-card-body .uk-close,.uk-card-secondary>:not([class*=uk-card-media]) .uk-close,.uk-light .uk-close,.uk-offcanvas-bar .uk-close,.uk-overlay-primary .uk-close,.uk-section-primary:not(.uk-preserve-color) .uk-close,.uk-section-secondary:not(.uk-preserve-color) .uk-close,.uk-tile-primary:not(.uk-preserve-color) .uk-close,.uk-tile-secondary:not(.uk-preserve-color) .uk-close{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-close:focus,.uk-card-primary.uk-card-body .uk-close:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-close:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-close:hover,.uk-card-secondary.uk-card-body .uk-close:focus,.uk-card-secondary.uk-card-body .uk-close:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-close:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-close:hover,.uk-light .uk-close:focus,.uk-light .uk-close:hover,.uk-offcanvas-bar .uk-close:focus,.uk-offcanvas-bar .uk-close:hover,.uk-overlay-primary .uk-close:focus,.uk-overlay-primary .uk-close:hover,.uk-section-primary:not(.uk-preserve-color) .uk-close:focus,.uk-section-primary:not(.uk-preserve-color) .uk-close:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-close:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-close:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-close:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-close:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-close:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-close:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-totop,.uk-card-primary>:not([class*=uk-card-media]) .uk-totop,.uk-card-secondary.uk-card-body .uk-totop,.uk-card-secondary>:not([class*=uk-card-media]) .uk-totop,.uk-light .uk-totop,.uk-offcanvas-bar .uk-totop,.uk-overlay-primary .uk-totop,.uk-section-primary:not(.uk-preserve-color) .uk-totop,.uk-section-secondary:not(.uk-preserve-color) .uk-totop,.uk-tile-primary:not(.uk-preserve-color) .uk-totop,.uk-tile-secondary:not(.uk-preserve-color) .uk-totop{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-totop:focus,.uk-card-primary.uk-card-body .uk-totop:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-totop:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-totop:hover,.uk-card-secondary.uk-card-body .uk-totop:focus,.uk-card-secondary.uk-card-body .uk-totop:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-totop:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-totop:hover,.uk-light .uk-totop:focus,.uk-light .uk-totop:hover,.uk-offcanvas-bar .uk-totop:focus,.uk-offcanvas-bar .uk-totop:hover,.uk-overlay-primary .uk-totop:focus,.uk-overlay-primary .uk-totop:hover,.uk-section-primary:not(.uk-preserve-color) .uk-totop:focus,.uk-section-primary:not(.uk-preserve-color) .uk-totop:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-totop:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-totop:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-totop:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-totop:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-totop:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-totop:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-totop:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-totop:active,.uk-card-secondary.uk-card-body .uk-totop:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-totop:active,.uk-light .uk-totop:active,.uk-offcanvas-bar .uk-totop:active,.uk-overlay-primary .uk-totop:active,.uk-section-primary:not(.uk-preserve-color) .uk-totop:active,.uk-section-secondary:not(.uk-preserve-color) .uk-totop:active,.uk-tile-primary:not(.uk-preserve-color) .uk-totop:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-totop:active{color:#fff}.uk-card-primary.uk-card-body .uk-marker,.uk-card-primary>:not([class*=uk-card-media]) .uk-marker,.uk-card-secondary.uk-card-body .uk-marker,.uk-card-secondary>:not([class*=uk-card-media]) .uk-marker,.uk-light .uk-marker,.uk-offcanvas-bar .uk-marker,.uk-overlay-primary .uk-marker,.uk-section-primary:not(.uk-preserve-color) .uk-marker,.uk-section-secondary:not(.uk-preserve-color) .uk-marker,.uk-tile-primary:not(.uk-preserve-color) .uk-marker,.uk-tile-secondary:not(.uk-preserve-color) .uk-marker{background:#f8f8f8;color:#666}.uk-card-primary.uk-card-body .uk-marker:focus,.uk-card-primary.uk-card-body .uk-marker:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-marker:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-marker:hover,.uk-card-secondary.uk-card-body .uk-marker:focus,.uk-card-secondary.uk-card-body .uk-marker:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-marker:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-marker:hover,.uk-light .uk-marker:focus,.uk-light .uk-marker:hover,.uk-offcanvas-bar .uk-marker:focus,.uk-offcanvas-bar .uk-marker:hover,.uk-overlay-primary .uk-marker:focus,.uk-overlay-primary .uk-marker:hover,.uk-section-primary:not(.uk-preserve-color) .uk-marker:focus,.uk-section-primary:not(.uk-preserve-color) .uk-marker:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-marker:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-marker:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-marker:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-marker:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-marker:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-marker:hover{color:#666}.uk-card-primary.uk-card-body .uk-badge,.uk-card-primary>:not([class*=uk-card-media]) .uk-badge,.uk-card-secondary.uk-card-body .uk-badge,.uk-card-secondary>:not([class*=uk-card-media]) .uk-badge,.uk-light .uk-badge,.uk-offcanvas-bar .uk-badge,.uk-overlay-primary .uk-badge,.uk-section-primary:not(.uk-preserve-color) .uk-badge,.uk-section-secondary:not(.uk-preserve-color) .uk-badge,.uk-tile-primary:not(.uk-preserve-color) .uk-badge,.uk-tile-secondary:not(.uk-preserve-color) .uk-badge{background-color:#fff;color:#666}.uk-card-primary.uk-card-body .uk-badge:focus,.uk-card-primary.uk-card-body .uk-badge:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-badge:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-badge:hover,.uk-card-secondary.uk-card-body .uk-badge:focus,.uk-card-secondary.uk-card-body .uk-badge:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-badge:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-badge:hover,.uk-light .uk-badge:focus,.uk-light .uk-badge:hover,.uk-offcanvas-bar .uk-badge:focus,.uk-offcanvas-bar .uk-badge:hover,.uk-overlay-primary .uk-badge:focus,.uk-overlay-primary .uk-badge:hover,.uk-section-primary:not(.uk-preserve-color) .uk-badge:focus,.uk-section-primary:not(.uk-preserve-color) .uk-badge:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-badge:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-badge:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-badge:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-badge:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-badge:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-badge:hover{color:#666}.uk-card-primary.uk-card-body .uk-label,.uk-card-primary>:not([class*=uk-card-media]) .uk-label,.uk-card-secondary.uk-card-body .uk-label,.uk-card-secondary>:not([class*=uk-card-media]) .uk-label,.uk-light .uk-label,.uk-offcanvas-bar .uk-label,.uk-overlay-primary .uk-label,.uk-section-primary:not(.uk-preserve-color) .uk-label,.uk-section-secondary:not(.uk-preserve-color) .uk-label,.uk-tile-primary:not(.uk-preserve-color) .uk-label,.uk-tile-secondary:not(.uk-preserve-color) .uk-label{background-color:#fff;color:#666}.uk-card-primary.uk-card-body .uk-article-meta,.uk-card-primary>:not([class*=uk-card-media]) .uk-article-meta,.uk-card-secondary.uk-card-body .uk-article-meta,.uk-card-secondary>:not([class*=uk-card-media]) .uk-article-meta,.uk-light .uk-article-meta,.uk-offcanvas-bar .uk-article-meta,.uk-overlay-primary .uk-article-meta,.uk-section-primary:not(.uk-preserve-color) .uk-article-meta,.uk-section-secondary:not(.uk-preserve-color) .uk-article-meta,.uk-tile-primary:not(.uk-preserve-color) .uk-article-meta,.uk-tile-secondary:not(.uk-preserve-color) .uk-article-meta{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-search-input,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-input,.uk-card-secondary.uk-card-body .uk-search-input,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-input,.uk-light .uk-search-input,.uk-offcanvas-bar .uk-search-input,.uk-overlay-primary .uk-search-input,.uk-section-primary:not(.uk-preserve-color) .uk-search-input,.uk-section-secondary:not(.uk-preserve-color) .uk-search-input,.uk-tile-primary:not(.uk-preserve-color) .uk-search-input,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-input{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-search-input:-ms-input-placeholder,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-input:-ms-input-placeholder,.uk-card-secondary.uk-card-body .uk-search-input:-ms-input-placeholder,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-input:-ms-input-placeholder,.uk-light .uk-search-input:-ms-input-placeholder,.uk-offcanvas-bar .uk-search-input:-ms-input-placeholder,.uk-overlay-primary .uk-search-input:-ms-input-placeholder,.uk-section-primary:not(.uk-preserve-color) .uk-search-input:-ms-input-placeholder,.uk-section-secondary:not(.uk-preserve-color) .uk-search-input:-ms-input-placeholder,.uk-tile-primary:not(.uk-preserve-color) .uk-search-input:-ms-input-placeholder,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-input:-ms-input-placeholder{color:rgba(255,255,255,.5)!important}.uk-card-primary.uk-card-body .uk-search-input::placeholder,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-input::placeholder,.uk-card-secondary.uk-card-body .uk-search-input::placeholder,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-input::placeholder,.uk-light .uk-search-input::placeholder,.uk-offcanvas-bar .uk-search-input::placeholder,.uk-overlay-primary .uk-search-input::placeholder,.uk-section-primary:not(.uk-preserve-color) .uk-search-input::placeholder,.uk-section-secondary:not(.uk-preserve-color) .uk-search-input::placeholder,.uk-tile-primary:not(.uk-preserve-color) .uk-search-input::placeholder,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-input::placeholder{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-search .uk-search-icon,.uk-card-primary>:not([class*=uk-card-media]) .uk-search .uk-search-icon,.uk-card-secondary.uk-card-body .uk-search .uk-search-icon,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search .uk-search-icon,.uk-light .uk-search .uk-search-icon,.uk-offcanvas-bar .uk-search .uk-search-icon,.uk-overlay-primary .uk-search .uk-search-icon,.uk-section-primary:not(.uk-preserve-color) .uk-search .uk-search-icon,.uk-section-secondary:not(.uk-preserve-color) .uk-search .uk-search-icon,.uk-tile-primary:not(.uk-preserve-color) .uk-search .uk-search-icon,.uk-tile-secondary:not(.uk-preserve-color) .uk-search .uk-search-icon{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-search .uk-search-icon:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-search .uk-search-icon:hover,.uk-card-secondary.uk-card-body .uk-search .uk-search-icon:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search .uk-search-icon:hover,.uk-light .uk-search .uk-search-icon:hover,.uk-offcanvas-bar .uk-search .uk-search-icon:hover,.uk-overlay-primary .uk-search .uk-search-icon:hover,.uk-section-primary:not(.uk-preserve-color) .uk-search .uk-search-icon:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-search .uk-search-icon:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-search .uk-search-icon:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-search .uk-search-icon:hover{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-search-default .uk-search-input,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-default .uk-search-input,.uk-card-secondary.uk-card-body .uk-search-default .uk-search-input,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-default .uk-search-input,.uk-light .uk-search-default .uk-search-input,.uk-offcanvas-bar .uk-search-default .uk-search-input,.uk-overlay-primary .uk-search-default .uk-search-input,.uk-section-primary:not(.uk-preserve-color) .uk-search-default .uk-search-input,.uk-section-secondary:not(.uk-preserve-color) .uk-search-default .uk-search-input,.uk-tile-primary:not(.uk-preserve-color) .uk-search-default .uk-search-input,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-default .uk-search-input{background-color:transparent;border-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-search-default .uk-search-input:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-default .uk-search-input:focus,.uk-card-secondary.uk-card-body .uk-search-default .uk-search-input:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-default .uk-search-input:focus,.uk-light .uk-search-default .uk-search-input:focus,.uk-offcanvas-bar .uk-search-default .uk-search-input:focus,.uk-overlay-primary .uk-search-default .uk-search-input:focus,.uk-section-primary:not(.uk-preserve-color) .uk-search-default .uk-search-input:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-search-default .uk-search-input:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-search-default .uk-search-input:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-default .uk-search-input:focus{background-color:transparent}.uk-card-primary.uk-card-body .uk-search-navbar .uk-search-input,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-navbar .uk-search-input,.uk-card-secondary.uk-card-body .uk-search-navbar .uk-search-input,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-navbar .uk-search-input,.uk-light .uk-search-navbar .uk-search-input,.uk-offcanvas-bar .uk-search-navbar .uk-search-input,.uk-overlay-primary .uk-search-navbar .uk-search-input,.uk-section-primary:not(.uk-preserve-color) .uk-search-navbar .uk-search-input,.uk-section-secondary:not(.uk-preserve-color) .uk-search-navbar .uk-search-input,.uk-tile-primary:not(.uk-preserve-color) .uk-search-navbar .uk-search-input,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-navbar .uk-search-input{background-color:transparent}.uk-card-primary.uk-card-body .uk-search-large .uk-search-input,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-large .uk-search-input,.uk-card-secondary.uk-card-body .uk-search-large .uk-search-input,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-large .uk-search-input,.uk-light .uk-search-large .uk-search-input,.uk-offcanvas-bar .uk-search-large .uk-search-input,.uk-overlay-primary .uk-search-large .uk-search-input,.uk-section-primary:not(.uk-preserve-color) .uk-search-large .uk-search-input,.uk-section-secondary:not(.uk-preserve-color) .uk-search-large .uk-search-input,.uk-tile-primary:not(.uk-preserve-color) .uk-search-large .uk-search-input,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-large .uk-search-input{background-color:transparent}.uk-card-primary.uk-card-body .uk-search-toggle,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-toggle,.uk-card-secondary.uk-card-body .uk-search-toggle,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-toggle,.uk-light .uk-search-toggle,.uk-offcanvas-bar .uk-search-toggle,.uk-overlay-primary .uk-search-toggle,.uk-section-primary:not(.uk-preserve-color) .uk-search-toggle,.uk-section-secondary:not(.uk-preserve-color) .uk-search-toggle,.uk-tile-primary:not(.uk-preserve-color) .uk-search-toggle,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-toggle{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-search-toggle:focus,.uk-card-primary.uk-card-body .uk-search-toggle:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-toggle:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-search-toggle:hover,.uk-card-secondary.uk-card-body .uk-search-toggle:focus,.uk-card-secondary.uk-card-body .uk-search-toggle:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-toggle:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-search-toggle:hover,.uk-light .uk-search-toggle:focus,.uk-light .uk-search-toggle:hover,.uk-offcanvas-bar .uk-search-toggle:focus,.uk-offcanvas-bar .uk-search-toggle:hover,.uk-overlay-primary .uk-search-toggle:focus,.uk-overlay-primary .uk-search-toggle:hover,.uk-section-primary:not(.uk-preserve-color) .uk-search-toggle:focus,.uk-section-primary:not(.uk-preserve-color) .uk-search-toggle:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-search-toggle:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-search-toggle:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-search-toggle:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-search-toggle:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-toggle:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-search-toggle:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-accordion-title,.uk-card-primary>:not([class*=uk-card-media]) .uk-accordion-title,.uk-card-secondary.uk-card-body .uk-accordion-title,.uk-card-secondary>:not([class*=uk-card-media]) .uk-accordion-title,.uk-light .uk-accordion-title,.uk-offcanvas-bar .uk-accordion-title,.uk-overlay-primary .uk-accordion-title,.uk-section-primary:not(.uk-preserve-color) .uk-accordion-title,.uk-section-secondary:not(.uk-preserve-color) .uk-accordion-title,.uk-tile-primary:not(.uk-preserve-color) .uk-accordion-title,.uk-tile-secondary:not(.uk-preserve-color) .uk-accordion-title{color:#fff}.uk-card-primary.uk-card-body .uk-accordion-title:focus,.uk-card-primary.uk-card-body .uk-accordion-title:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-accordion-title:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-accordion-title:hover,.uk-card-secondary.uk-card-body .uk-accordion-title:focus,.uk-card-secondary.uk-card-body .uk-accordion-title:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-accordion-title:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-accordion-title:hover,.uk-light .uk-accordion-title:focus,.uk-light .uk-accordion-title:hover,.uk-offcanvas-bar .uk-accordion-title:focus,.uk-offcanvas-bar .uk-accordion-title:hover,.uk-overlay-primary .uk-accordion-title:focus,.uk-overlay-primary .uk-accordion-title:hover,.uk-section-primary:not(.uk-preserve-color) .uk-accordion-title:focus,.uk-section-primary:not(.uk-preserve-color) .uk-accordion-title:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-accordion-title:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-accordion-title:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-accordion-title:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-accordion-title:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-accordion-title:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-accordion-title:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-grid-divider>:not(.uk-first-column)::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-grid-divider>:not(.uk-first-column)::before,.uk-card-secondary.uk-card-body .uk-grid-divider>:not(.uk-first-column)::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-grid-divider>:not(.uk-first-column)::before,.uk-light .uk-grid-divider>:not(.uk-first-column)::before,.uk-offcanvas-bar .uk-grid-divider>:not(.uk-first-column)::before,.uk-overlay-primary .uk-grid-divider>:not(.uk-first-column)::before,.uk-section-primary:not(.uk-preserve-color) .uk-grid-divider>:not(.uk-first-column)::before,.uk-section-secondary:not(.uk-preserve-color) .uk-grid-divider>:not(.uk-first-column)::before,.uk-tile-primary:not(.uk-preserve-color) .uk-grid-divider>:not(.uk-first-column)::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-grid-divider>:not(.uk-first-column)::before{border-left-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-card-secondary.uk-card-body .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-light .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-offcanvas-bar .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-overlay-primary .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-section-primary:not(.uk-preserve-color) .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-section-secondary:not(.uk-preserve-color) .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-tile-primary:not(.uk-preserve-color) .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-grid-divider.uk-grid-stack>.uk-grid-margin::before{border-top-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-nav-parent-icon>.uk-parent>a::after,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-parent-icon>.uk-parent>a::after,.uk-card-secondary.uk-card-body .uk-nav-parent-icon>.uk-parent>a::after,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-parent-icon>.uk-parent>a::after,.uk-light .uk-nav-parent-icon>.uk-parent>a::after,.uk-offcanvas-bar .uk-nav-parent-icon>.uk-parent>a::after,.uk-overlay-primary .uk-nav-parent-icon>.uk-parent>a::after,.uk-section-primary:not(.uk-preserve-color) .uk-nav-parent-icon>.uk-parent>a::after,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-parent-icon>.uk-parent>a::after,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-parent-icon>.uk-parent>a::after,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-parent-icon>.uk-parent>a::after{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.7%29%22%20stroke-width%3D%221.1%22%20points%3D%2210%201%204%207%2010%2013%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-card-primary.uk-card-body .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-card-secondary.uk-card-body .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-light .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-offcanvas-bar .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-overlay-primary .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-section-primary:not(.uk-preserve-color) .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-parent-icon>.uk-parent.uk-open>a::after,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-parent-icon>.uk-parent.uk-open>a::after{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Cpolyline%20fill%3D%22none%22%20stroke%3D%22rgba%28255,%20255,%20255,%200.7%29%22%20stroke-width%3D%221.1%22%20points%3D%221%204%207%2010%2013%204%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-card-primary.uk-card-body .uk-nav-default>li>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default>li>a,.uk-card-secondary.uk-card-body .uk-nav-default>li>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default>li>a,.uk-light .uk-nav-default>li>a,.uk-offcanvas-bar .uk-nav-default>li>a,.uk-overlay-primary .uk-nav-default>li>a,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default>li>a,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default>li>a,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default>li>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default>li>a{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-nav-default>li>a:focus,.uk-card-primary.uk-card-body .uk-nav-default>li>a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default>li>a:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default>li>a:hover,.uk-card-secondary.uk-card-body .uk-nav-default>li>a:focus,.uk-card-secondary.uk-card-body .uk-nav-default>li>a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default>li>a:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default>li>a:hover,.uk-light .uk-nav-default>li>a:focus,.uk-light .uk-nav-default>li>a:hover,.uk-offcanvas-bar .uk-nav-default>li>a:focus,.uk-offcanvas-bar .uk-nav-default>li>a:hover,.uk-overlay-primary .uk-nav-default>li>a:focus,.uk-overlay-primary .uk-nav-default>li>a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default>li>a:focus,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default>li>a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default>li>a:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default>li>a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default>li>a:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default>li>a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default>li>a:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default>li>a:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-nav-default>li.uk-active>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default>li.uk-active>a,.uk-card-secondary.uk-card-body .uk-nav-default>li.uk-active>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default>li.uk-active>a,.uk-light .uk-nav-default>li.uk-active>a,.uk-offcanvas-bar .uk-nav-default>li.uk-active>a,.uk-overlay-primary .uk-nav-default>li.uk-active>a,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default>li.uk-active>a,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default>li.uk-active>a,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default>li.uk-active>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default>li.uk-active>a{color:#fff}.uk-card-primary.uk-card-body .uk-nav-default .uk-nav-header,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-header,.uk-card-secondary.uk-card-body .uk-nav-default .uk-nav-header,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-header,.uk-light .uk-nav-default .uk-nav-header,.uk-offcanvas-bar .uk-nav-default .uk-nav-header,.uk-overlay-primary .uk-nav-default .uk-nav-header,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-header,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-header,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-header,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-header{color:#fff}.uk-card-primary.uk-card-body .uk-nav-default .uk-nav-divider,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-divider,.uk-card-secondary.uk-card-body .uk-nav-default .uk-nav-divider,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-divider,.uk-light .uk-nav-default .uk-nav-divider,.uk-offcanvas-bar .uk-nav-default .uk-nav-divider,.uk-overlay-primary .uk-nav-default .uk-nav-divider,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-divider,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-divider,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-divider,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-divider{border-top-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-nav-default .uk-nav-sub a,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-sub a,.uk-card-secondary.uk-card-body .uk-nav-default .uk-nav-sub a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-sub a,.uk-light .uk-nav-default .uk-nav-sub a,.uk-offcanvas-bar .uk-nav-default .uk-nav-sub a,.uk-overlay-primary .uk-nav-default .uk-nav-sub a,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-nav-default .uk-nav-sub a:focus,.uk-card-primary.uk-card-body .uk-nav-default .uk-nav-sub a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-sub a:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-sub a:hover,.uk-card-secondary.uk-card-body .uk-nav-default .uk-nav-sub a:focus,.uk-card-secondary.uk-card-body .uk-nav-default .uk-nav-sub a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-sub a:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-sub a:hover,.uk-light .uk-nav-default .uk-nav-sub a:focus,.uk-light .uk-nav-default .uk-nav-sub a:hover,.uk-offcanvas-bar .uk-nav-default .uk-nav-sub a:focus,.uk-offcanvas-bar .uk-nav-default .uk-nav-sub a:hover,.uk-overlay-primary .uk-nav-default .uk-nav-sub a:focus,.uk-overlay-primary .uk-nav-default .uk-nav-sub a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a:focus,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub a:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-card-secondary.uk-card-body .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-light .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-offcanvas-bar .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-overlay-primary .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-section-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub li.uk-active>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-default .uk-nav-sub li.uk-active>a{color:#fff}.uk-card-primary.uk-card-body .uk-nav-primary>li>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary>li>a,.uk-card-secondary.uk-card-body .uk-nav-primary>li>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary>li>a,.uk-light .uk-nav-primary>li>a,.uk-offcanvas-bar .uk-nav-primary>li>a,.uk-overlay-primary .uk-nav-primary>li>a,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary>li>a,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary>li>a,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary>li>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary>li>a{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-nav-primary>li>a:focus,.uk-card-primary.uk-card-body .uk-nav-primary>li>a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary>li>a:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary>li>a:hover,.uk-card-secondary.uk-card-body .uk-nav-primary>li>a:focus,.uk-card-secondary.uk-card-body .uk-nav-primary>li>a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary>li>a:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary>li>a:hover,.uk-light .uk-nav-primary>li>a:focus,.uk-light .uk-nav-primary>li>a:hover,.uk-offcanvas-bar .uk-nav-primary>li>a:focus,.uk-offcanvas-bar .uk-nav-primary>li>a:hover,.uk-overlay-primary .uk-nav-primary>li>a:focus,.uk-overlay-primary .uk-nav-primary>li>a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary>li>a:focus,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary>li>a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary>li>a:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary>li>a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary>li>a:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary>li>a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary>li>a:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary>li>a:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-nav-primary>li.uk-active>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary>li.uk-active>a,.uk-card-secondary.uk-card-body .uk-nav-primary>li.uk-active>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary>li.uk-active>a,.uk-light .uk-nav-primary>li.uk-active>a,.uk-offcanvas-bar .uk-nav-primary>li.uk-active>a,.uk-overlay-primary .uk-nav-primary>li.uk-active>a,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary>li.uk-active>a,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary>li.uk-active>a,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary>li.uk-active>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary>li.uk-active>a{color:#fff}.uk-card-primary.uk-card-body .uk-nav-primary .uk-nav-header,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-header,.uk-card-secondary.uk-card-body .uk-nav-primary .uk-nav-header,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-header,.uk-light .uk-nav-primary .uk-nav-header,.uk-offcanvas-bar .uk-nav-primary .uk-nav-header,.uk-overlay-primary .uk-nav-primary .uk-nav-header,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-header,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-header,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-header,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-header{color:#fff}.uk-card-primary.uk-card-body .uk-nav-primary .uk-nav-divider,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-divider,.uk-card-secondary.uk-card-body .uk-nav-primary .uk-nav-divider,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-divider,.uk-light .uk-nav-primary .uk-nav-divider,.uk-offcanvas-bar .uk-nav-primary .uk-nav-divider,.uk-overlay-primary .uk-nav-primary .uk-nav-divider,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-divider,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-divider,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-divider,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-divider{border-top-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-nav-primary .uk-nav-sub a,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-sub a,.uk-card-secondary.uk-card-body .uk-nav-primary .uk-nav-sub a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-sub a,.uk-light .uk-nav-primary .uk-nav-sub a,.uk-offcanvas-bar .uk-nav-primary .uk-nav-sub a,.uk-overlay-primary .uk-nav-primary .uk-nav-sub a,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-nav-primary .uk-nav-sub a:focus,.uk-card-primary.uk-card-body .uk-nav-primary .uk-nav-sub a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-sub a:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-sub a:hover,.uk-card-secondary.uk-card-body .uk-nav-primary .uk-nav-sub a:focus,.uk-card-secondary.uk-card-body .uk-nav-primary .uk-nav-sub a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-sub a:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-sub a:hover,.uk-light .uk-nav-primary .uk-nav-sub a:focus,.uk-light .uk-nav-primary .uk-nav-sub a:hover,.uk-offcanvas-bar .uk-nav-primary .uk-nav-sub a:focus,.uk-offcanvas-bar .uk-nav-primary .uk-nav-sub a:hover,.uk-overlay-primary .uk-nav-primary .uk-nav-sub a:focus,.uk-overlay-primary .uk-nav-primary .uk-nav-sub a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a:focus,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub a:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-card-secondary.uk-card-body .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-light .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-offcanvas-bar .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-overlay-primary .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-section-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-section-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-tile-primary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub li.uk-active>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-nav-primary .uk-nav-sub li.uk-active>a{color:#fff}.uk-card-primary.uk-card-body .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-card-primary>:not([class*=uk-card-media]) .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-card-secondary.uk-card-body .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-card-secondary>:not([class*=uk-card-media]) .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-light .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-offcanvas-bar .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-overlay-primary .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-section-primary:not(.uk-preserve-color) .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-section-secondary:not(.uk-preserve-color) .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-tile-primary:not(.uk-preserve-color) .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider),.uk-tile-secondary:not(.uk-preserve-color) .uk-nav.uk-nav-divider>:not(.uk-nav-divider)+:not(.uk-nav-header,.uk-nav-divider){border-top-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-navbar-nav>li>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-nav>li>a,.uk-card-secondary.uk-card-body .uk-navbar-nav>li>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-nav>li>a,.uk-light .uk-navbar-nav>li>a,.uk-offcanvas-bar .uk-navbar-nav>li>a,.uk-overlay-primary .uk-navbar-nav>li>a,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-nav>li>a,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-nav>li>a,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-nav>li>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-nav>li>a{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-navbar-nav>li:hover>a,.uk-card-primary.uk-card-body .uk-navbar-nav>li>a.uk-open,.uk-card-primary.uk-card-body .uk-navbar-nav>li>a:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-nav>li:hover>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-nav>li>a.uk-open,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-nav>li>a:focus,.uk-card-secondary.uk-card-body .uk-navbar-nav>li:hover>a,.uk-card-secondary.uk-card-body .uk-navbar-nav>li>a.uk-open,.uk-card-secondary.uk-card-body .uk-navbar-nav>li>a:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-nav>li:hover>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-nav>li>a.uk-open,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-nav>li>a:focus,.uk-light .uk-navbar-nav>li:hover>a,.uk-light .uk-navbar-nav>li>a.uk-open,.uk-light .uk-navbar-nav>li>a:focus,.uk-offcanvas-bar .uk-navbar-nav>li:hover>a,.uk-offcanvas-bar .uk-navbar-nav>li>a.uk-open,.uk-offcanvas-bar .uk-navbar-nav>li>a:focus,.uk-overlay-primary .uk-navbar-nav>li:hover>a,.uk-overlay-primary .uk-navbar-nav>li>a.uk-open,.uk-overlay-primary .uk-navbar-nav>li>a:focus,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-nav>li:hover>a,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-nav>li>a.uk-open,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-nav>li>a:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-nav>li:hover>a,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-nav>li>a.uk-open,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-nav>li>a:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-nav>li:hover>a,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-nav>li>a.uk-open,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-nav>li>a:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-nav>li:hover>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-nav>li>a.uk-open,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-nav>li>a:focus{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-navbar-nav>li>a:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-nav>li>a:active,.uk-card-secondary.uk-card-body .uk-navbar-nav>li>a:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-nav>li>a:active,.uk-light .uk-navbar-nav>li>a:active,.uk-offcanvas-bar .uk-navbar-nav>li>a:active,.uk-overlay-primary .uk-navbar-nav>li>a:active,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-nav>li>a:active,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-nav>li>a:active,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-nav>li>a:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-nav>li>a:active{color:#fff}.uk-card-primary.uk-card-body .uk-navbar-nav>li.uk-active>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-nav>li.uk-active>a,.uk-card-secondary.uk-card-body .uk-navbar-nav>li.uk-active>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-nav>li.uk-active>a,.uk-light .uk-navbar-nav>li.uk-active>a,.uk-offcanvas-bar .uk-navbar-nav>li.uk-active>a,.uk-overlay-primary .uk-navbar-nav>li.uk-active>a,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-nav>li.uk-active>a,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-nav>li.uk-active>a,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-nav>li.uk-active>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-nav>li.uk-active>a{color:#fff}.uk-card-primary.uk-card-body .uk-navbar-item,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-item,.uk-card-secondary.uk-card-body .uk-navbar-item,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-item,.uk-light .uk-navbar-item,.uk-offcanvas-bar .uk-navbar-item,.uk-overlay-primary .uk-navbar-item,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-item,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-item,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-item,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-item{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-navbar-toggle,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-toggle,.uk-card-secondary.uk-card-body .uk-navbar-toggle,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-toggle,.uk-light .uk-navbar-toggle,.uk-offcanvas-bar .uk-navbar-toggle,.uk-overlay-primary .uk-navbar-toggle,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-toggle,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-toggle,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-toggle,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-toggle{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-navbar-toggle.uk-open,.uk-card-primary.uk-card-body .uk-navbar-toggle:focus,.uk-card-primary.uk-card-body .uk-navbar-toggle:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-toggle.uk-open,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-toggle:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-navbar-toggle:hover,.uk-card-secondary.uk-card-body .uk-navbar-toggle.uk-open,.uk-card-secondary.uk-card-body .uk-navbar-toggle:focus,.uk-card-secondary.uk-card-body .uk-navbar-toggle:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-toggle.uk-open,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-toggle:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-navbar-toggle:hover,.uk-light .uk-navbar-toggle.uk-open,.uk-light .uk-navbar-toggle:focus,.uk-light .uk-navbar-toggle:hover,.uk-offcanvas-bar .uk-navbar-toggle.uk-open,.uk-offcanvas-bar .uk-navbar-toggle:focus,.uk-offcanvas-bar .uk-navbar-toggle:hover,.uk-overlay-primary .uk-navbar-toggle.uk-open,.uk-overlay-primary .uk-navbar-toggle:focus,.uk-overlay-primary .uk-navbar-toggle:hover,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-toggle.uk-open,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-toggle:focus,.uk-section-primary:not(.uk-preserve-color) .uk-navbar-toggle:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-toggle.uk-open,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-toggle:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-navbar-toggle:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-toggle.uk-open,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-toggle:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-navbar-toggle:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-toggle.uk-open,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-toggle:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-navbar-toggle:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-subnav>*>:first-child,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav>*>:first-child,.uk-card-secondary.uk-card-body .uk-subnav>*>:first-child,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav>*>:first-child,.uk-light .uk-subnav>*>:first-child,.uk-offcanvas-bar .uk-subnav>*>:first-child,.uk-overlay-primary .uk-subnav>*>:first-child,.uk-section-primary:not(.uk-preserve-color) .uk-subnav>*>:first-child,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav>*>:first-child,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav>*>:first-child,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav>*>:first-child{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-subnav>*>a:focus,.uk-card-primary.uk-card-body .uk-subnav>*>a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav>*>a:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav>*>a:hover,.uk-card-secondary.uk-card-body .uk-subnav>*>a:focus,.uk-card-secondary.uk-card-body .uk-subnav>*>a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav>*>a:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav>*>a:hover,.uk-light .uk-subnav>*>a:focus,.uk-light .uk-subnav>*>a:hover,.uk-offcanvas-bar .uk-subnav>*>a:focus,.uk-offcanvas-bar .uk-subnav>*>a:hover,.uk-overlay-primary .uk-subnav>*>a:focus,.uk-overlay-primary .uk-subnav>*>a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-subnav>*>a:focus,.uk-section-primary:not(.uk-preserve-color) .uk-subnav>*>a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav>*>a:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav>*>a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav>*>a:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav>*>a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav>*>a:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav>*>a:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-subnav>.uk-active>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav>.uk-active>a,.uk-card-secondary.uk-card-body .uk-subnav>.uk-active>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav>.uk-active>a,.uk-light .uk-subnav>.uk-active>a,.uk-offcanvas-bar .uk-subnav>.uk-active>a,.uk-overlay-primary .uk-subnav>.uk-active>a,.uk-section-primary:not(.uk-preserve-color) .uk-subnav>.uk-active>a,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav>.uk-active>a,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav>.uk-active>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav>.uk-active>a{color:#fff}.uk-card-primary.uk-card-body .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-card-secondary.uk-card-body .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-light .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-offcanvas-bar .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-overlay-primary .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-section-primary:not(.uk-preserve-color) .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav-divider>:nth-child(n+2):not(.uk-first-column)::before{border-left-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-subnav-pill>*>:first-child,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav-pill>*>:first-child,.uk-card-secondary.uk-card-body .uk-subnav-pill>*>:first-child,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav-pill>*>:first-child,.uk-light .uk-subnav-pill>*>:first-child,.uk-offcanvas-bar .uk-subnav-pill>*>:first-child,.uk-overlay-primary .uk-subnav-pill>*>:first-child,.uk-section-primary:not(.uk-preserve-color) .uk-subnav-pill>*>:first-child,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav-pill>*>:first-child,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav-pill>*>:first-child,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav-pill>*>:first-child{background-color:transparent;color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-subnav-pill>*>a:focus,.uk-card-primary.uk-card-body .uk-subnav-pill>*>a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav-pill>*>a:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav-pill>*>a:hover,.uk-card-secondary.uk-card-body .uk-subnav-pill>*>a:focus,.uk-card-secondary.uk-card-body .uk-subnav-pill>*>a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav-pill>*>a:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav-pill>*>a:hover,.uk-light .uk-subnav-pill>*>a:focus,.uk-light .uk-subnav-pill>*>a:hover,.uk-offcanvas-bar .uk-subnav-pill>*>a:focus,.uk-offcanvas-bar .uk-subnav-pill>*>a:hover,.uk-overlay-primary .uk-subnav-pill>*>a:focus,.uk-overlay-primary .uk-subnav-pill>*>a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-subnav-pill>*>a:focus,.uk-section-primary:not(.uk-preserve-color) .uk-subnav-pill>*>a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav-pill>*>a:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav-pill>*>a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav-pill>*>a:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav-pill>*>a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav-pill>*>a:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav-pill>*>a:hover{background-color:rgba(255,255,255,.1);color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-subnav-pill>*>a:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav-pill>*>a:active,.uk-card-secondary.uk-card-body .uk-subnav-pill>*>a:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav-pill>*>a:active,.uk-light .uk-subnav-pill>*>a:active,.uk-offcanvas-bar .uk-subnav-pill>*>a:active,.uk-overlay-primary .uk-subnav-pill>*>a:active,.uk-section-primary:not(.uk-preserve-color) .uk-subnav-pill>*>a:active,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav-pill>*>a:active,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav-pill>*>a:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav-pill>*>a:active{background-color:rgba(255,255,255,.1);color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-subnav-pill>.uk-active>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav-pill>.uk-active>a,.uk-card-secondary.uk-card-body .uk-subnav-pill>.uk-active>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav-pill>.uk-active>a,.uk-light .uk-subnav-pill>.uk-active>a,.uk-offcanvas-bar .uk-subnav-pill>.uk-active>a,.uk-overlay-primary .uk-subnav-pill>.uk-active>a,.uk-section-primary:not(.uk-preserve-color) .uk-subnav-pill>.uk-active>a,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav-pill>.uk-active>a,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav-pill>.uk-active>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav-pill>.uk-active>a{background-color:#fff;color:#666}.uk-card-primary.uk-card-body .uk-subnav>.uk-disabled>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-subnav>.uk-disabled>a,.uk-card-secondary.uk-card-body .uk-subnav>.uk-disabled>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-subnav>.uk-disabled>a,.uk-light .uk-subnav>.uk-disabled>a,.uk-offcanvas-bar .uk-subnav>.uk-disabled>a,.uk-overlay-primary .uk-subnav>.uk-disabled>a,.uk-section-primary:not(.uk-preserve-color) .uk-subnav>.uk-disabled>a,.uk-section-secondary:not(.uk-preserve-color) .uk-subnav>.uk-disabled>a,.uk-tile-primary:not(.uk-preserve-color) .uk-subnav>.uk-disabled>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-subnav>.uk-disabled>a{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-breadcrumb>*>*,.uk-card-primary>:not([class*=uk-card-media]) .uk-breadcrumb>*>*,.uk-card-secondary.uk-card-body .uk-breadcrumb>*>*,.uk-card-secondary>:not([class*=uk-card-media]) .uk-breadcrumb>*>*,.uk-light .uk-breadcrumb>*>*,.uk-offcanvas-bar .uk-breadcrumb>*>*,.uk-overlay-primary .uk-breadcrumb>*>*,.uk-section-primary:not(.uk-preserve-color) .uk-breadcrumb>*>*,.uk-section-secondary:not(.uk-preserve-color) .uk-breadcrumb>*>*,.uk-tile-primary:not(.uk-preserve-color) .uk-breadcrumb>*>*,.uk-tile-secondary:not(.uk-preserve-color) .uk-breadcrumb>*>*{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-breadcrumb>*>:focus,.uk-card-primary.uk-card-body .uk-breadcrumb>*>:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-breadcrumb>*>:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-breadcrumb>*>:hover,.uk-card-secondary.uk-card-body .uk-breadcrumb>*>:focus,.uk-card-secondary.uk-card-body .uk-breadcrumb>*>:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-breadcrumb>*>:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-breadcrumb>*>:hover,.uk-light .uk-breadcrumb>*>:focus,.uk-light .uk-breadcrumb>*>:hover,.uk-offcanvas-bar .uk-breadcrumb>*>:focus,.uk-offcanvas-bar .uk-breadcrumb>*>:hover,.uk-overlay-primary .uk-breadcrumb>*>:focus,.uk-overlay-primary .uk-breadcrumb>*>:hover,.uk-section-primary:not(.uk-preserve-color) .uk-breadcrumb>*>:focus,.uk-section-primary:not(.uk-preserve-color) .uk-breadcrumb>*>:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-breadcrumb>*>:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-breadcrumb>*>:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-breadcrumb>*>:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-breadcrumb>*>:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-breadcrumb>*>:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-breadcrumb>*>:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-breadcrumb>:last-child>*,.uk-card-primary>:not([class*=uk-card-media]) .uk-breadcrumb>:last-child>*,.uk-card-secondary.uk-card-body .uk-breadcrumb>:last-child>*,.uk-card-secondary>:not([class*=uk-card-media]) .uk-breadcrumb>:last-child>*,.uk-light .uk-breadcrumb>:last-child>*,.uk-offcanvas-bar .uk-breadcrumb>:last-child>*,.uk-overlay-primary .uk-breadcrumb>:last-child>*,.uk-section-primary:not(.uk-preserve-color) .uk-breadcrumb>:last-child>*,.uk-section-secondary:not(.uk-preserve-color) .uk-breadcrumb>:last-child>*,.uk-tile-primary:not(.uk-preserve-color) .uk-breadcrumb>:last-child>*,.uk-tile-secondary:not(.uk-preserve-color) .uk-breadcrumb>:last-child>*{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-card-secondary.uk-card-body .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-light .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-offcanvas-bar .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-overlay-primary .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-section-primary:not(.uk-preserve-color) .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-section-secondary:not(.uk-preserve-color) .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-tile-primary:not(.uk-preserve-color) .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-breadcrumb>:nth-child(n+2):not(.uk-first-column)::before{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-pagination>*>*,.uk-card-primary>:not([class*=uk-card-media]) .uk-pagination>*>*,.uk-card-secondary.uk-card-body .uk-pagination>*>*,.uk-card-secondary>:not([class*=uk-card-media]) .uk-pagination>*>*,.uk-light .uk-pagination>*>*,.uk-offcanvas-bar .uk-pagination>*>*,.uk-overlay-primary .uk-pagination>*>*,.uk-section-primary:not(.uk-preserve-color) .uk-pagination>*>*,.uk-section-secondary:not(.uk-preserve-color) .uk-pagination>*>*,.uk-tile-primary:not(.uk-preserve-color) .uk-pagination>*>*,.uk-tile-secondary:not(.uk-preserve-color) .uk-pagination>*>*{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-pagination>*>:focus,.uk-card-primary.uk-card-body .uk-pagination>*>:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-pagination>*>:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-pagination>*>:hover,.uk-card-secondary.uk-card-body .uk-pagination>*>:focus,.uk-card-secondary.uk-card-body .uk-pagination>*>:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-pagination>*>:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-pagination>*>:hover,.uk-light .uk-pagination>*>:focus,.uk-light .uk-pagination>*>:hover,.uk-offcanvas-bar .uk-pagination>*>:focus,.uk-offcanvas-bar .uk-pagination>*>:hover,.uk-overlay-primary .uk-pagination>*>:focus,.uk-overlay-primary .uk-pagination>*>:hover,.uk-section-primary:not(.uk-preserve-color) .uk-pagination>*>:focus,.uk-section-primary:not(.uk-preserve-color) .uk-pagination>*>:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-pagination>*>:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-pagination>*>:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-pagination>*>:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-pagination>*>:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-pagination>*>:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-pagination>*>:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-pagination>.uk-active>*,.uk-card-primary>:not([class*=uk-card-media]) .uk-pagination>.uk-active>*,.uk-card-secondary.uk-card-body .uk-pagination>.uk-active>*,.uk-card-secondary>:not([class*=uk-card-media]) .uk-pagination>.uk-active>*,.uk-light .uk-pagination>.uk-active>*,.uk-offcanvas-bar .uk-pagination>.uk-active>*,.uk-overlay-primary .uk-pagination>.uk-active>*,.uk-section-primary:not(.uk-preserve-color) .uk-pagination>.uk-active>*,.uk-section-secondary:not(.uk-preserve-color) .uk-pagination>.uk-active>*,.uk-tile-primary:not(.uk-preserve-color) .uk-pagination>.uk-active>*,.uk-tile-secondary:not(.uk-preserve-color) .uk-pagination>.uk-active>*{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-pagination>.uk-disabled>*,.uk-card-primary>:not([class*=uk-card-media]) .uk-pagination>.uk-disabled>*,.uk-card-secondary.uk-card-body .uk-pagination>.uk-disabled>*,.uk-card-secondary>:not([class*=uk-card-media]) .uk-pagination>.uk-disabled>*,.uk-light .uk-pagination>.uk-disabled>*,.uk-offcanvas-bar .uk-pagination>.uk-disabled>*,.uk-overlay-primary .uk-pagination>.uk-disabled>*,.uk-section-primary:not(.uk-preserve-color) .uk-pagination>.uk-disabled>*,.uk-section-secondary:not(.uk-preserve-color) .uk-pagination>.uk-disabled>*,.uk-tile-primary:not(.uk-preserve-color) .uk-pagination>.uk-disabled>*,.uk-tile-secondary:not(.uk-preserve-color) .uk-pagination>.uk-disabled>*{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-tab::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-tab::before,.uk-card-secondary.uk-card-body .uk-tab::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-tab::before,.uk-light .uk-tab::before,.uk-offcanvas-bar .uk-tab::before,.uk-overlay-primary .uk-tab::before,.uk-section-primary:not(.uk-preserve-color) .uk-tab::before,.uk-section-secondary:not(.uk-preserve-color) .uk-tab::before,.uk-tile-primary:not(.uk-preserve-color) .uk-tab::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-tab::before{border-color:rgba(255,255,255,.2)}.uk-card-primary.uk-card-body .uk-tab>*>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-tab>*>a,.uk-card-secondary.uk-card-body .uk-tab>*>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-tab>*>a,.uk-light .uk-tab>*>a,.uk-offcanvas-bar .uk-tab>*>a,.uk-overlay-primary .uk-tab>*>a,.uk-section-primary:not(.uk-preserve-color) .uk-tab>*>a,.uk-section-secondary:not(.uk-preserve-color) .uk-tab>*>a,.uk-tile-primary:not(.uk-preserve-color) .uk-tab>*>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-tab>*>a{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-tab>*>a:focus,.uk-card-primary.uk-card-body .uk-tab>*>a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-tab>*>a:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-tab>*>a:hover,.uk-card-secondary.uk-card-body .uk-tab>*>a:focus,.uk-card-secondary.uk-card-body .uk-tab>*>a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-tab>*>a:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-tab>*>a:hover,.uk-light .uk-tab>*>a:focus,.uk-light .uk-tab>*>a:hover,.uk-offcanvas-bar .uk-tab>*>a:focus,.uk-offcanvas-bar .uk-tab>*>a:hover,.uk-overlay-primary .uk-tab>*>a:focus,.uk-overlay-primary .uk-tab>*>a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-tab>*>a:focus,.uk-section-primary:not(.uk-preserve-color) .uk-tab>*>a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-tab>*>a:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-tab>*>a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-tab>*>a:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-tab>*>a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-tab>*>a:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-tab>*>a:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-tab>.uk-active>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-tab>.uk-active>a,.uk-card-secondary.uk-card-body .uk-tab>.uk-active>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-tab>.uk-active>a,.uk-light .uk-tab>.uk-active>a,.uk-offcanvas-bar .uk-tab>.uk-active>a,.uk-overlay-primary .uk-tab>.uk-active>a,.uk-section-primary:not(.uk-preserve-color) .uk-tab>.uk-active>a,.uk-section-secondary:not(.uk-preserve-color) .uk-tab>.uk-active>a,.uk-tile-primary:not(.uk-preserve-color) .uk-tab>.uk-active>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-tab>.uk-active>a{color:#fff;border-color:#fff}.uk-card-primary.uk-card-body .uk-tab>.uk-disabled>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-tab>.uk-disabled>a,.uk-card-secondary.uk-card-body .uk-tab>.uk-disabled>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-tab>.uk-disabled>a,.uk-light .uk-tab>.uk-disabled>a,.uk-offcanvas-bar .uk-tab>.uk-disabled>a,.uk-overlay-primary .uk-tab>.uk-disabled>a,.uk-section-primary:not(.uk-preserve-color) .uk-tab>.uk-disabled>a,.uk-section-secondary:not(.uk-preserve-color) .uk-tab>.uk-disabled>a,.uk-tile-primary:not(.uk-preserve-color) .uk-tab>.uk-disabled>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-tab>.uk-disabled>a{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-slidenav,.uk-card-primary>:not([class*=uk-card-media]) .uk-slidenav,.uk-card-secondary.uk-card-body .uk-slidenav,.uk-card-secondary>:not([class*=uk-card-media]) .uk-slidenav,.uk-light .uk-slidenav,.uk-offcanvas-bar .uk-slidenav,.uk-overlay-primary .uk-slidenav,.uk-section-primary:not(.uk-preserve-color) .uk-slidenav,.uk-section-secondary:not(.uk-preserve-color) .uk-slidenav,.uk-tile-primary:not(.uk-preserve-color) .uk-slidenav,.uk-tile-secondary:not(.uk-preserve-color) .uk-slidenav{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-slidenav:focus,.uk-card-primary.uk-card-body .uk-slidenav:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-slidenav:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-slidenav:hover,.uk-card-secondary.uk-card-body .uk-slidenav:focus,.uk-card-secondary.uk-card-body .uk-slidenav:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-slidenav:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-slidenav:hover,.uk-light .uk-slidenav:focus,.uk-light .uk-slidenav:hover,.uk-offcanvas-bar .uk-slidenav:focus,.uk-offcanvas-bar .uk-slidenav:hover,.uk-overlay-primary .uk-slidenav:focus,.uk-overlay-primary .uk-slidenav:hover,.uk-section-primary:not(.uk-preserve-color) .uk-slidenav:focus,.uk-section-primary:not(.uk-preserve-color) .uk-slidenav:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-slidenav:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-slidenav:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-slidenav:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-slidenav:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-slidenav:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-slidenav:hover{color:rgba(255,255,255,.95)}.uk-card-primary.uk-card-body .uk-slidenav:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-slidenav:active,.uk-card-secondary.uk-card-body .uk-slidenav:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-slidenav:active,.uk-light .uk-slidenav:active,.uk-offcanvas-bar .uk-slidenav:active,.uk-overlay-primary .uk-slidenav:active,.uk-section-primary:not(.uk-preserve-color) .uk-slidenav:active,.uk-section-secondary:not(.uk-preserve-color) .uk-slidenav:active,.uk-tile-primary:not(.uk-preserve-color) .uk-slidenav:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-slidenav:active{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-dotnav>*>*,.uk-card-primary>:not([class*=uk-card-media]) .uk-dotnav>*>*,.uk-card-secondary.uk-card-body .uk-dotnav>*>*,.uk-card-secondary>:not([class*=uk-card-media]) .uk-dotnav>*>*,.uk-light .uk-dotnav>*>*,.uk-offcanvas-bar .uk-dotnav>*>*,.uk-overlay-primary .uk-dotnav>*>*,.uk-section-primary:not(.uk-preserve-color) .uk-dotnav>*>*,.uk-section-secondary:not(.uk-preserve-color) .uk-dotnav>*>*,.uk-tile-primary:not(.uk-preserve-color) .uk-dotnav>*>*,.uk-tile-secondary:not(.uk-preserve-color) .uk-dotnav>*>*{background-color:transparent;border-color:rgba(255,255,255,.9)}.uk-card-primary.uk-card-body .uk-dotnav>*>:focus,.uk-card-primary.uk-card-body .uk-dotnav>*>:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-dotnav>*>:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-dotnav>*>:hover,.uk-card-secondary.uk-card-body .uk-dotnav>*>:focus,.uk-card-secondary.uk-card-body .uk-dotnav>*>:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-dotnav>*>:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-dotnav>*>:hover,.uk-light .uk-dotnav>*>:focus,.uk-light .uk-dotnav>*>:hover,.uk-offcanvas-bar .uk-dotnav>*>:focus,.uk-offcanvas-bar .uk-dotnav>*>:hover,.uk-overlay-primary .uk-dotnav>*>:focus,.uk-overlay-primary .uk-dotnav>*>:hover,.uk-section-primary:not(.uk-preserve-color) .uk-dotnav>*>:focus,.uk-section-primary:not(.uk-preserve-color) .uk-dotnav>*>:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-dotnav>*>:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-dotnav>*>:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-dotnav>*>:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-dotnav>*>:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-dotnav>*>:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-dotnav>*>:hover{background-color:rgba(255,255,255,.9);border-color:transparent}.uk-card-primary.uk-card-body .uk-dotnav>*>:active,.uk-card-primary>:not([class*=uk-card-media]) .uk-dotnav>*>:active,.uk-card-secondary.uk-card-body .uk-dotnav>*>:active,.uk-card-secondary>:not([class*=uk-card-media]) .uk-dotnav>*>:active,.uk-light .uk-dotnav>*>:active,.uk-offcanvas-bar .uk-dotnav>*>:active,.uk-overlay-primary .uk-dotnav>*>:active,.uk-section-primary:not(.uk-preserve-color) .uk-dotnav>*>:active,.uk-section-secondary:not(.uk-preserve-color) .uk-dotnav>*>:active,.uk-tile-primary:not(.uk-preserve-color) .uk-dotnav>*>:active,.uk-tile-secondary:not(.uk-preserve-color) .uk-dotnav>*>:active{background-color:rgba(255,255,255,.5);border-color:transparent}.uk-card-primary.uk-card-body .uk-dotnav>.uk-active>*,.uk-card-primary>:not([class*=uk-card-media]) .uk-dotnav>.uk-active>*,.uk-card-secondary.uk-card-body .uk-dotnav>.uk-active>*,.uk-card-secondary>:not([class*=uk-card-media]) .uk-dotnav>.uk-active>*,.uk-light .uk-dotnav>.uk-active>*,.uk-offcanvas-bar .uk-dotnav>.uk-active>*,.uk-overlay-primary .uk-dotnav>.uk-active>*,.uk-section-primary:not(.uk-preserve-color) .uk-dotnav>.uk-active>*,.uk-section-secondary:not(.uk-preserve-color) .uk-dotnav>.uk-active>*,.uk-tile-primary:not(.uk-preserve-color) .uk-dotnav>.uk-active>*,.uk-tile-secondary:not(.uk-preserve-color) .uk-dotnav>.uk-active>*{background-color:rgba(255,255,255,.9);border-color:transparent}.uk-card-primary.uk-card-body .uk-iconnav>*>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-iconnav>*>a,.uk-card-secondary.uk-card-body .uk-iconnav>*>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-iconnav>*>a,.uk-light .uk-iconnav>*>a,.uk-offcanvas-bar .uk-iconnav>*>a,.uk-overlay-primary .uk-iconnav>*>a,.uk-section-primary:not(.uk-preserve-color) .uk-iconnav>*>a,.uk-section-secondary:not(.uk-preserve-color) .uk-iconnav>*>a,.uk-tile-primary:not(.uk-preserve-color) .uk-iconnav>*>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-iconnav>*>a{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-iconnav>*>a:focus,.uk-card-primary.uk-card-body .uk-iconnav>*>a:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-iconnav>*>a:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-iconnav>*>a:hover,.uk-card-secondary.uk-card-body .uk-iconnav>*>a:focus,.uk-card-secondary.uk-card-body .uk-iconnav>*>a:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-iconnav>*>a:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-iconnav>*>a:hover,.uk-light .uk-iconnav>*>a:focus,.uk-light .uk-iconnav>*>a:hover,.uk-offcanvas-bar .uk-iconnav>*>a:focus,.uk-offcanvas-bar .uk-iconnav>*>a:hover,.uk-overlay-primary .uk-iconnav>*>a:focus,.uk-overlay-primary .uk-iconnav>*>a:hover,.uk-section-primary:not(.uk-preserve-color) .uk-iconnav>*>a:focus,.uk-section-primary:not(.uk-preserve-color) .uk-iconnav>*>a:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-iconnav>*>a:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-iconnav>*>a:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-iconnav>*>a:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-iconnav>*>a:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-iconnav>*>a:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-iconnav>*>a:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-iconnav>.uk-active>a,.uk-card-primary>:not([class*=uk-card-media]) .uk-iconnav>.uk-active>a,.uk-card-secondary.uk-card-body .uk-iconnav>.uk-active>a,.uk-card-secondary>:not([class*=uk-card-media]) .uk-iconnav>.uk-active>a,.uk-light .uk-iconnav>.uk-active>a,.uk-offcanvas-bar .uk-iconnav>.uk-active>a,.uk-overlay-primary .uk-iconnav>.uk-active>a,.uk-section-primary:not(.uk-preserve-color) .uk-iconnav>.uk-active>a,.uk-section-secondary:not(.uk-preserve-color) .uk-iconnav>.uk-active>a,.uk-tile-primary:not(.uk-preserve-color) .uk-iconnav>.uk-active>a,.uk-tile-secondary:not(.uk-preserve-color) .uk-iconnav>.uk-active>a{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-text-lead,.uk-card-primary>:not([class*=uk-card-media]) .uk-text-lead,.uk-card-secondary.uk-card-body .uk-text-lead,.uk-card-secondary>:not([class*=uk-card-media]) .uk-text-lead,.uk-light .uk-text-lead,.uk-offcanvas-bar .uk-text-lead,.uk-overlay-primary .uk-text-lead,.uk-section-primary:not(.uk-preserve-color) .uk-text-lead,.uk-section-secondary:not(.uk-preserve-color) .uk-text-lead,.uk-tile-primary:not(.uk-preserve-color) .uk-text-lead,.uk-tile-secondary:not(.uk-preserve-color) .uk-text-lead{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-text-meta,.uk-card-primary>:not([class*=uk-card-media]) .uk-text-meta,.uk-card-secondary.uk-card-body .uk-text-meta,.uk-card-secondary>:not([class*=uk-card-media]) .uk-text-meta,.uk-light .uk-text-meta,.uk-offcanvas-bar .uk-text-meta,.uk-overlay-primary .uk-text-meta,.uk-section-primary:not(.uk-preserve-color) .uk-text-meta,.uk-section-secondary:not(.uk-preserve-color) .uk-text-meta,.uk-tile-primary:not(.uk-preserve-color) .uk-text-meta,.uk-tile-secondary:not(.uk-preserve-color) .uk-text-meta{color:rgba(255,255,255,.5)}.uk-card-primary.uk-card-body .uk-text-muted,.uk-card-primary>:not([class*=uk-card-media]) .uk-text-muted,.uk-card-secondary.uk-card-body .uk-text-muted,.uk-card-secondary>:not([class*=uk-card-media]) .uk-text-muted,.uk-light .uk-text-muted,.uk-offcanvas-bar .uk-text-muted,.uk-overlay-primary .uk-text-muted,.uk-section-primary:not(.uk-preserve-color) .uk-text-muted,.uk-section-secondary:not(.uk-preserve-color) .uk-text-muted,.uk-tile-primary:not(.uk-preserve-color) .uk-text-muted,.uk-tile-secondary:not(.uk-preserve-color) .uk-text-muted{color:rgba(255,255,255,.5)!important}.uk-card-primary.uk-card-body .uk-text-emphasis,.uk-card-primary>:not([class*=uk-card-media]) .uk-text-emphasis,.uk-card-secondary.uk-card-body .uk-text-emphasis,.uk-card-secondary>:not([class*=uk-card-media]) .uk-text-emphasis,.uk-light .uk-text-emphasis,.uk-offcanvas-bar .uk-text-emphasis,.uk-overlay-primary .uk-text-emphasis,.uk-section-primary:not(.uk-preserve-color) .uk-text-emphasis,.uk-section-secondary:not(.uk-preserve-color) .uk-text-emphasis,.uk-tile-primary:not(.uk-preserve-color) .uk-text-emphasis,.uk-tile-secondary:not(.uk-preserve-color) .uk-text-emphasis{color:#fff!important}.uk-card-primary.uk-card-body .uk-text-primary,.uk-card-primary>:not([class*=uk-card-media]) .uk-text-primary,.uk-card-secondary.uk-card-body .uk-text-primary,.uk-card-secondary>:not([class*=uk-card-media]) .uk-text-primary,.uk-light .uk-text-primary,.uk-offcanvas-bar .uk-text-primary,.uk-overlay-primary .uk-text-primary,.uk-section-primary:not(.uk-preserve-color) .uk-text-primary,.uk-section-secondary:not(.uk-preserve-color) .uk-text-primary,.uk-tile-primary:not(.uk-preserve-color) .uk-text-primary,.uk-tile-secondary:not(.uk-preserve-color) .uk-text-primary{color:#fff!important}.uk-card-primary.uk-card-body .uk-text-secondary,.uk-card-primary>:not([class*=uk-card-media]) .uk-text-secondary,.uk-card-secondary.uk-card-body .uk-text-secondary,.uk-card-secondary>:not([class*=uk-card-media]) .uk-text-secondary,.uk-light .uk-text-secondary,.uk-offcanvas-bar .uk-text-secondary,.uk-overlay-primary .uk-text-secondary,.uk-section-primary:not(.uk-preserve-color) .uk-text-secondary,.uk-section-secondary:not(.uk-preserve-color) .uk-text-secondary,.uk-tile-primary:not(.uk-preserve-color) .uk-text-secondary,.uk-tile-secondary:not(.uk-preserve-color) .uk-text-secondary{color:#fff!important}.uk-card-primary.uk-card-body .uk-column-divider,.uk-card-primary>:not([class*=uk-card-media]) .uk-column-divider,.uk-card-secondary.uk-card-body .uk-column-divider,.uk-card-secondary>:not([class*=uk-card-media]) .uk-column-divider,.uk-light .uk-column-divider,.uk-offcanvas-bar .uk-column-divider,.uk-overlay-primary .uk-column-divider,.uk-section-primary:not(.uk-preserve-color) .uk-column-divider,.uk-section-secondary:not(.uk-preserve-color) .uk-column-divider,.uk-tile-primary:not(.uk-preserve-color) .uk-column-divider,.uk-tile-secondary:not(.uk-preserve-color) .uk-column-divider{column-rule-color:rgba(255,255,255,0.2)}.uk-card-primary.uk-card-body .uk-logo,.uk-card-primary>:not([class*=uk-card-media]) .uk-logo,.uk-card-secondary.uk-card-body .uk-logo,.uk-card-secondary>:not([class*=uk-card-media]) .uk-logo,.uk-light .uk-logo,.uk-offcanvas-bar .uk-logo,.uk-overlay-primary .uk-logo,.uk-section-primary:not(.uk-preserve-color) .uk-logo,.uk-section-secondary:not(.uk-preserve-color) .uk-logo,.uk-tile-primary:not(.uk-preserve-color) .uk-logo,.uk-tile-secondary:not(.uk-preserve-color) .uk-logo{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-logo:focus,.uk-card-primary.uk-card-body .uk-logo:hover,.uk-card-primary>:not([class*=uk-card-media]) .uk-logo:focus,.uk-card-primary>:not([class*=uk-card-media]) .uk-logo:hover,.uk-card-secondary.uk-card-body .uk-logo:focus,.uk-card-secondary.uk-card-body .uk-logo:hover,.uk-card-secondary>:not([class*=uk-card-media]) .uk-logo:focus,.uk-card-secondary>:not([class*=uk-card-media]) .uk-logo:hover,.uk-light .uk-logo:focus,.uk-light .uk-logo:hover,.uk-offcanvas-bar .uk-logo:focus,.uk-offcanvas-bar .uk-logo:hover,.uk-overlay-primary .uk-logo:focus,.uk-overlay-primary .uk-logo:hover,.uk-section-primary:not(.uk-preserve-color) .uk-logo:focus,.uk-section-primary:not(.uk-preserve-color) .uk-logo:hover,.uk-section-secondary:not(.uk-preserve-color) .uk-logo:focus,.uk-section-secondary:not(.uk-preserve-color) .uk-logo:hover,.uk-tile-primary:not(.uk-preserve-color) .uk-logo:focus,.uk-tile-primary:not(.uk-preserve-color) .uk-logo:hover,.uk-tile-secondary:not(.uk-preserve-color) .uk-logo:focus,.uk-tile-secondary:not(.uk-preserve-color) .uk-logo:hover{color:rgba(255,255,255,.7)}.uk-card-primary.uk-card-body .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-card-primary>:not([class*=uk-card-media]) .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-card-secondary.uk-card-body .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-card-secondary>:not([class*=uk-card-media]) .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-light .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-offcanvas-bar .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-overlay-primary .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-section-primary:not(.uk-preserve-color) .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-section-secondary:not(.uk-preserve-color) .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-tile-primary:not(.uk-preserve-color) .uk-logo>:not(.uk-logo-inverse):not(:only-of-type),.uk-tile-secondary:not(.uk-preserve-color) .uk-logo>:not(.uk-logo-inverse):not(:only-of-type){display:none}.uk-card-primary.uk-card-body .uk-logo-inverse,.uk-card-primary>:not([class*=uk-card-media]) .uk-logo-inverse,.uk-card-secondary.uk-card-body .uk-logo-inverse,.uk-card-secondary>:not([class*=uk-card-media]) .uk-logo-inverse,.uk-light .uk-logo-inverse,.uk-offcanvas-bar .uk-logo-inverse,.uk-overlay-primary .uk-logo-inverse,.uk-section-primary:not(.uk-preserve-color) .uk-logo-inverse,.uk-section-secondary:not(.uk-preserve-color) .uk-logo-inverse,.uk-tile-primary:not(.uk-preserve-color) .uk-logo-inverse,.uk-tile-secondary:not(.uk-preserve-color) .uk-logo-inverse{display:inline}.uk-card-primary.uk-card-body .uk-accordion-title::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-accordion-title::before,.uk-card-secondary.uk-card-body .uk-accordion-title::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-accordion-title::before,.uk-light .uk-accordion-title::before,.uk-offcanvas-bar .uk-accordion-title::before,.uk-overlay-primary .uk-accordion-title::before,.uk-section-primary:not(.uk-preserve-color) .uk-accordion-title::before,.uk-section-secondary:not(.uk-preserve-color) .uk-accordion-title::before,.uk-tile-primary:not(.uk-preserve-color) .uk-accordion-title::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-accordion-title::before{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22rgba%28255,%20255,%20255,%200.7%29%22%20width%3D%2213%22%20height%3D%221%22%20x%3D%220%22%20y%3D%226%22%20%2F%3E%0A%20%20%20%20%3Crect%20fill%3D%22rgba%28255,%20255,%20255,%200.7%29%22%20width%3D%221%22%20height%3D%2213%22%20x%3D%226%22%20y%3D%220%22%20%2F%3E%0A%3C%2Fsvg%3E")}.uk-card-primary.uk-card-body .uk-open>.uk-accordion-title::before,.uk-card-primary>:not([class*=uk-card-media]) .uk-open>.uk-accordion-title::before,.uk-card-secondary.uk-card-body .uk-open>.uk-accordion-title::before,.uk-card-secondary>:not([class*=uk-card-media]) .uk-open>.uk-accordion-title::before,.uk-light .uk-open>.uk-accordion-title::before,.uk-offcanvas-bar .uk-open>.uk-accordion-title::before,.uk-overlay-primary .uk-open>.uk-accordion-title::before,.uk-section-primary:not(.uk-preserve-color) .uk-open>.uk-accordion-title::before,.uk-section-secondary:not(.uk-preserve-color) .uk-open>.uk-accordion-title::before,.uk-tile-primary:not(.uk-preserve-color) .uk-open>.uk-accordion-title::before,.uk-tile-secondary:not(.uk-preserve-color) .uk-open>.uk-accordion-title::before{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2213%22%20height%3D%2213%22%20viewBox%3D%220%200%2013%2013%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%3Crect%20fill%3D%22rgba%28255,%20255,%20255,%200.7%29%22%20width%3D%2213%22%20height%3D%221%22%20x%3D%220%22%20y%3D%226%22%20%2F%3E%0A%3C%2Fsvg%3E")}@media print{*,::after,::before{background:0 0!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}a,a:visited{text-decoration:underline}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}} \ No newline at end of file diff --git a/media/uikit-v3/index.html b/media/uikit-v3/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/uikit-v3/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/uikit-v3/js/index.html b/media/uikit-v3/js/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/media/uikit-v3/js/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/media/uikit-v3/js/uikit-icons.min.js b/media/uikit-v3/js/uikit-icons.min.js new file mode 100644 index 0000000..e880e27 --- /dev/null +++ b/media/uikit-v3/js/uikit-icons.min.js @@ -0,0 +1,3 @@ +/*! UIkit 3.6.22 | https://www.getuikit.com | (c) 2014 - 2021 YOOtheme | MIT License */ + +!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define("uikiticons",i):(t="undefined"!=typeof globalThis?globalThis:t||self).UIkitIcons=i()}(this,function(){"use strict";function i(t){i.installed||t.icon.add({"500px":'',album:'',"arrow-down":'',"arrow-left":'',"arrow-right":'',"arrow-up":'',ban:'',behance:'',bell:'',bold:'',bolt:'',bookmark:'',calendar:'',camera:'',cart:'',check:'',"chevron-double-left":'',"chevron-double-right":'',"chevron-down":'',"chevron-left":'',"chevron-right":'',"chevron-up":'',clock:'',close:'',"cloud-download":'',"cloud-upload":'',code:'',cog:'',comment:'',commenting:'',comments:'',copy:'',"credit-card":'',database:'',desktop:'',discord:'',download:'',dribbble:'',etsy:'',expand:'',facebook:'',"file-edit":'',"file-pdf":'',"file-text":'',file:'',flickr:'',folder:'',forward:'',foursquare:'',future:'',"git-branch":'',"git-fork":'',"github-alt":'',github:'',gitter:'',google:'',grid:'',happy:'',hashtag:'',heart:'',history:'',home:'',image:'',info:'',instagram:'',italic:'',joomla:'',laptop:'',lifesaver:'',link:'',linkedin:'',list:'',location:'',lock:'',mail:'',menu:'',microphone:'',"minus-circle":'',minus:'',"more-vertical":'',more:'',move:'',nut:'',pagekit:'',"paint-bucket":'',pencil:'',"phone-landscape":'',phone:'',pinterest:'',"play-circle":'',play:'',"plus-circle":'',plus:'',print:'',pull:'',push:'',question:'',"quote-right":'',receiver:'',reddit:'',refresh:'',reply:'',rss:'',search:'',server:'',settings:'',shrink:'',"sign-in":'',"sign-out":'',social:'',soundcloud:'',star:'',strikethrough:'',table:'',"tablet-landscape":'',tablet:'',tag:'',thumbnails:'',tiktok:'',trash:'',"triangle-down":'',"triangle-left":'',"triangle-right":'',"triangle-up":'',tripadvisor:'',tumblr:'',tv:'',twitch:'',twitter:'',uikit:'',unlock:'',upload:'',user:'',users:'',"video-camera":'',vimeo:'',warning:'',whatsapp:'',wordpress:'',world:'',xing:'',yelp:'',youtube:''})}return"undefined"!=typeof window&&window.UIkit&&window.UIkit.use(i),i}); \ No newline at end of file diff --git a/media/uikit-v3/js/uikit.min.js b/media/uikit-v3/js/uikit.min.js new file mode 100644 index 0000000..58dde2b --- /dev/null +++ b/media/uikit-v3/js/uikit.min.js @@ -0,0 +1,3 @@ +/*! UIkit 3.6.22 | https://www.getuikit.com | (c) 2014 - 2021 YOOtheme | MIT License */ + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define("uikit",e):(t="undefined"!=typeof globalThis?globalThis:t||self).UIkit=e()}(this,function(){"use strict";var t=Object.prototype,n=t.hasOwnProperty;function c(t,e){return n.call(t,e)}var e=/\B([A-Z])/g,d=rt(function(t){return t.replace(e,"-$1").toLowerCase()}),i=/-(\w)/g,f=rt(function(t){return t.replace(i,r)}),p=rt(function(t){return t.length?r(0,t.charAt(0))+t.slice(1):""});function r(t,e){return e?e.toUpperCase():""}var o=String.prototype,s=o.startsWith||function(t){return 0===this.lastIndexOf(t,0)};function g(t,e){return s.call(t,e)}var a=o.endsWith||function(t){return this.substr(-t.length)===t};function u(t,e){return a.call(t,e)}var h=Array.prototype,l=function(t,e){return!!~this.indexOf(t,e)},m=o.includes||l,v=h.includes||l;function w(t,e){return t&&(z(t)?m:v).call(t,e)}var b=h.findIndex||function(t){for(var e=arguments,n=0;n=e.left&&t.y<=e.bottom&&t.y>=e.top}var nt={ratio:function(t,e,n){var i="width"===e?"height":"width",r={};return r[i]=t[e]?Math.round(n*t[i]/t[e]):t[i],r[e]=n,r},contain:function(n,i){var r=this;return G(n=Y({},n),function(t,e){return n=n[e]>i[e]?r.ratio(n,e,i[e]):n}),n},cover:function(n,i){var r=this;return G(n=this.contain(n,i),function(t,e){return n=n[e]")&&(e=e.slice(1)),_(t)?zt.call(t,e):W(t).map(function(t){return Nt(t,e)}).filter(Boolean)}function Bt(t,e){return z(e)?Mt(t,e)||!!Nt(t,e):t===e||(T(e)?e.documentElement:F(e)).contains(F(t))}function Dt(t,e){for(var n=[];t=Tt(t);)e&&!Mt(t,e)||n.push(t);return n}function Ot(t,e){t=(t=F(t))?W(t.children):[];return e?Ct(t,e):t}function Pt(t,e){return e?W(t).indexOf(F(e)):Ot(Tt(t)).indexOf(t)}function Ht(t,e){return F(t)||Ft(t,jt(t,e))}function Lt(t,e){var n=W(t);return n.length&&n||Wt(t,jt(t,e))}function jt(t,e){return void 0===e&&(e=document),z(t)&&Ut(t)||T(e)?e:e.ownerDocument}function Ft(t,e){return F(Vt(t,e,"querySelector"))}function Wt(t,e){return W(Vt(t,e,"querySelectorAll"))}function Vt(t,o,e){if(void 0===o&&(o=document),!t||!z(t))return null;t=t.replace(qt,"$1 *"),Ut(t)&&(t=Xt(t).map(function(t,e){var n,i,r=o;return"!"===t[0]&&(i=t.substr(1).trim().split(" "),r=Nt(Tt(o),i[0]),t=i.slice(1).join(" ").trim()),"-"===t[0]&&(n=t.substr(1).trim().split(" "),r=Mt(i=(r||o).previousElementSibling,t.substr(1))?i:null,t=n.slice(1).join(" ")),r?function(t){var e=[];for(;t.parentNode;){if(t.id){e.unshift("#"+Kt(t.id));break}var n=t.tagName;"HTML"!==n&&(n+=":nth-child("+(Pt(t)+1)+")"),e.unshift(n),t=t.parentNode}return e.join(" > ")}(r)+" "+t:null}).filter(Boolean).join(","),o=document);try{return o[e](t)}catch(t){return null}}var Rt=/(^|[^\\],)\s*[!>+~-]/,qt=/([!>+~-])(?=\s+[!>+~-]|\s*$)/g,Ut=rt(function(t){return t.match(Rt)}),Yt=/.*?[^\\](?:,|$)/g,Xt=rt(function(t){return t.match(Yt).map(function(t){return t.replace(/,$/,"").trim()})});var Gt=ct&&window.CSS&&CSS.escape||function(t){return t.replace(/([^\x7f-\uFFFF\w-])/g,function(t){return"\\"+t})};function Kt(t){return z(t)?Gt.call(null,t):""}function Jt(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var n,i,r=ne(t),o=r[0],s=r[1],a=r[2],u=r[3],c=r[4],o=se(o);return 1]*>/,Ce=/^<(\w+)\s*\/?>(?:<\/\1>)?$/;function _e(t){var e=Ce.exec(t);if(e)return document.createElement(e[1]);e=document.createElement("div");return Te.test(t)?e.insertAdjacentHTML("beforeend",t.trim()):e.textContent=t,1qn(t))})).reverse()}function Rn(t){return t===Un(t)?window:t}function qn(t){return(t===Un(t)?document.documentElement:t).clientHeight}function Un(t){t=V(t).document;return t.scrollingElement||t.documentElement}var Yn={width:["x","left","right"],height:["y","top","bottom"]};function Xn(t,e,h,l,d,n,i,r){h=Kn(h),l=Kn(l);var f={element:h,target:l};if(!t||!e)return f;var o,p=sn(t),m=sn(e),g=m;return Gn(g,h,p,-1),Gn(g,l,m,1),d=Jn(d,p.width,p.height),n=Jn(n,m.width,m.height),d.x+=n.x,d.y+=n.y,g.left+=d.x,g.top+=d.y,i&&(o=Vn(t).map(Rn),r&&!w(o,r)&&o.unshift(r),o=o.map(function(t){return sn(t)}),G(Yn,function(t,s){var a=t[0],u=t[1],c=t[2];!0!==i&&!w(i,a)||o.some(function(n){var t=h[a]===u?-p[s]:h[a]===c?p[s]:0,e=l[a]===u?m[s]:l[a]===c?-m[s]:0;if(g[u]n[c]){var i=p[s]/2,r="center"===l[a]?-m[s]/2:0;return"center"===h[a]&&(o(i,r)||o(-i,-r))||o(t,e)}function o(e,t){t=L((g[u]+e+t-2*d[a]).toFixed(4));if(t>=n[u]&&t+p[s]<=n[c])return g[u]=t,["element","target"].forEach(function(t){f[t][a]=e?f[t][a]===Yn[s][1]?Yn[s][2]:Yn[s][1]:f[t][a]}),!0}})})),sn(t,g),f}function Gn(r,o,s,a){G(Yn,function(t,e){var n=t[0],i=t[1],t=t[2];o[n]===t?r[i]+=s[e]*a:"center"===o[n]&&(r[i]+=s[e]*a/2)})}function Kn(t){var e=/left|center|right/,n=/top|center|bottom/;return 1===(t=(t||"").split(" ")).length&&(t=e.test(t[0])?t.concat("center"):n.test(t[0])?["center"].concat(t):["center","center"]),{x:e.test(t[0])?t[0]:"center",y:n.test(t[1])?t[1]:"center"}}function Jn(t,e,n){var i=(t||"").split(" "),t=i[0],i=i[1];return{x:t?L(t)*(u(t,"%")?e/100:1):0,y:i?L(i)*(u(i,"%")?n/100:1):0}}var Zn=Object.freeze({__proto__:null,ajax:me,getImage:ge,transition:Je,Transition:Ze,animate:tn,Animation:nn,attr:ot,hasAttr:st,removeAttr:at,data:ut,addClass:Be,removeClass:De,removeClasses:Oe,replaceClass:Pe,hasClass:He,toggleClass:Le,dimensions:on,offset:sn,position:an,offsetPosition:un,height:cn,width:hn,boxModelAdjust:dn,flipPosition:fn,toPx:pn,ready:function(t){var e;"loading"===document.readyState?e=Jt(document,"DOMContentLoaded",function(){e(),t()}):t()},empty:ve,html:we,prepend:function(e,t){return(e=Me(e)).hasChildNodes()?ke(t,function(t){return e.insertBefore(t,e.firstChild)}):be(e,t)},append:be,before:xe,after:ye,remove:$e,wrapAll:Se,wrapInner:Ie,unwrap:Ee,fragment:_e,apply:Ae,$:Me,$$:ze,inBrowser:ct,isIE:ht,isRtl:lt,hasTouch:pt,pointerDown:mt,pointerMove:gt,pointerUp:vt,pointerEnter:wt,pointerLeave:bt,pointerCancel:xt,on:Jt,off:Zt,once:Qt,trigger:te,createEvent:ee,toEventTargets:se,isTouch:ae,getEventPos:ue,fastdom:gn,isVoidElement:kt,isVisible:$t,selInput:St,isInput:It,isFocusable:Et,parent:Tt,filter:Ct,matches:Mt,closest:Nt,within:Bt,parents:Dt,children:Ot,index:Pt,hasOwn:c,hyphenate:d,camelize:f,ucfirst:p,startsWith:g,endsWith:u,includes:w,findIndex:x,isArray:y,isFunction:k,isObject:$,isPlainObject:I,isWindow:E,isDocument:T,isNode:C,isElement:_,isBoolean:M,isString:z,isNumber:N,isNumeric:B,isEmpty:D,isUndefined:O,toBoolean:P,toNumber:H,toFloat:L,toArray:j,toNode:F,toNodes:W,toWindow:V,toMs:R,isEqual:q,swap:U,assign:Y,last:X,each:G,sortBy:K,uniqueBy:J,clamp:Z,noop:Q,intersectRect:tt,pointInRect:et,Dimensions:nt,getIndex:it,memoize:rt,MouseTracker:kn,mergeOptions:En,parseOptions:Tn,play:Cn,pause:_n,mute:An,positionAt:Xn,Promise:he,Deferred:ce,query:Ht,queryAll:Lt,find:Ft,findAll:Wt,escape:Kt,css:Re,getCssVar:Xe,propName:Ge,isInView:Ln,scrollTop:jn,scrollIntoView:Fn,scrolledOver:Wn,scrollParents:Vn,getViewport:Rn,getViewportClientHeight:qn});function Qn(t){this._init(t)}var ti,ei,ni,ii,ri,oi,si,ai,ui,ci=rt(function(t){return!(!g(t,"uk-")&&!g(t,"data-uk-"))&&f(t.replace("data-uk-","").replace("uk-",""))});function hi(t,e){if(t)for(var n in t)t[n]._connected&&t[n]._callUpdate(e)}function li(t,e){var n={},i=t.args;void 0===i&&(i=[]);var r,o=t.props,s=t.el;if(!(o=void 0===o?{}:o))return n;for(r in o){var a=d(r),u=ut(s,a);O(u)||(u=o[r]===Boolean&&""===u||fi(o[r],u),("target"!==a||u&&!g(u,"_"))&&(n[r]=u))}var c,h=Tn(ut(s,e),i);for(c in h){var l=f(c);void 0!==o[l]&&(n[l]=fi(o[l],h[c]))}return n}function di(e,n,i){var t=(n=!I(n)?{name:i,handler:n}:n).name,r=n.el,o=n.handler,s=n.capture,a=n.passive,u=n.delegate,c=n.filter,h=n.self,r=k(r)?r.call(e):r||e.$el;y(r)?r.forEach(function(t){return di(e,Y({},n,{el:t}),i)}):!r||c&&!c.call(e)||e._events.push(Jt(r,t,u?z(u)?u:u.call(e):null,z(o)?e[o]:o.bind(e),{passive:a,capture:s,self:h}))}function fi(t,e){return t===Boolean?P(e):t===Number?H(e):"list"===t?y(n=e)?n:z(n)?n.split(/,(?![^(]*\))/).map(function(t){return B(t)?H(t):P(t.trim())}):[n]:t?t(e):e;var n}Qn.util=Zn,Qn.data="__uikit__",Qn.prefix="uk-",Qn.options={},Qn.version="3.6.22",ni=(ti=Qn).data,ti.use=function(t){if(!t.installed)return t.call(null,this),t.installed=!0,this},ti.mixin=function(t,e){(e=(z(e)?ti.component(e):e)||this).options=En(e.options,t)},ti.extend=function(t){t=t||{};function e(t){this._init(t)}return((e.prototype=Object.create(this.prototype)).constructor=e).options=En(this.options,t),e.super=this,e.extend=this.extend,e},ti.update=function(t,e){Dt(t=t?F(t):document.body).reverse().forEach(function(t){return hi(t[ni],e)}),Ae(t,function(t){return hi(t[ni],e)})},Object.defineProperty(ti,"container",{get:function(){return ei||document.body},set:function(t){ei=Me(t)}}),(ii=Qn).prototype._callHook=function(t){var e=this,t=this.$options[t];t&&t.forEach(function(t){return t.call(e)})},ii.prototype._callConnected=function(){this._connected||(this._data={},this._computeds={},this._initProps(),this._callHook("beforeConnect"),this._connected=!0,this._initEvents(),this._initObservers(),this._callHook("connected"),this._callUpdate())},ii.prototype._callDisconnected=function(){this._connected&&(this._callHook("beforeDisconnect"),this._disconnectObservers(),this._unbindEvents(),this._callHook("disconnected"),this._connected=!1,delete this._watch)},ii.prototype._callUpdate=function(t){var e=this;void 0===t&&(t="update"),this._connected&&("update"!==t&&"resize"!==t||this._callWatches(),this.$options.update&&(this._updates||(this._updates=new Set,gn.read(function(){!function(i){for(var r=this,o=this.$options.update,t=0;t *",active:!1,animation:[!0],collapsible:!0,multiple:!1,clsOpen:"uk-open",toggle:"> .uk-accordion-title",content:"> .uk-accordion-content",transition:"ease",offset:0},computed:{items:{get:function(t,e){return ze(t.targets,e)},watch:function(t,e){var n=this;t.forEach(function(t){return wi(Me(n.content,t),!He(t,n.clsOpen))}),e||He(t,this.clsOpen)||(t=!1!==this.active&&t[Number(this.active)]||!this.collapsible&&t[0])&&this.toggle(t,!1)},immediate:!0},toggles:function(t){var e=t.toggle;return this.items.map(function(t){return Me(e,t)})}},events:[{name:"click",delegate:function(){return this.targets+" "+this.$props.toggle},handler:function(t){t.preventDefault(),this.toggle(Pt(this.toggles,t.current))}}],methods:{toggle:function(t,r){var o=this,e=[this.items[it(t,this.items)]],t=Ct(this.items,"."+this.clsOpen);this.multiple||w(t,e[0])||(e=e.concat(t)),!this.collapsible&&t.length<2&&!Ct(e,":not(."+this.clsOpen+")").length||e.forEach(function(t){return o.toggleElement(t,!He(t,o.clsOpen),function(e,n){Le(e,o.clsOpen,n),ot(Me(o.$props.toggle,e),"aria-expanded",n);var i=Me((e._wrapper?"> * ":"")+o.content,e);if(!1!==r&&o.hasTransition)return e._wrapper||(e._wrapper=Se(i,"")),wi(i,!1),gi(o)(e._wrapper,n).then(function(){var t;wi(i,!n),delete e._wrapper,Ee(i),n&&(Ln(t=Me(o.$props.toggle,e))||Fn(t,{offset:o.offset}))});wi(i,!n)})})}}};function wi(t,e){t&&(t.hidden=e)}var bi={mixins:[pi,mi],args:"animation",props:{close:String},data:{animation:[!0],selClose:".uk-alert-close",duration:150,hideProps:Y({opacity:0},mi.data.hideProps)},events:[{name:"click",delegate:function(){return this.selClose},handler:function(t){t.preventDefault(),this.close()}}],methods:{close:function(){var t=this;this.toggleElement(this.$el).then(function(){return t.$destroy(!0)})}}},xi={args:"autoplay",props:{automute:Boolean,autoplay:Boolean},data:{automute:!1,autoplay:!0},computed:{inView:function(t){return"inview"===t.autoplay}},connected:function(){this.inView&&!st(this.$el,"preload")&&(this.$el.preload="none"),this.automute&&An(this.$el)},update:{read:function(){return{visible:$t(this.$el)&&"hidden"!==Re(this.$el,"visibility"),inView:this.inView&&Ln(this.$el)}},write:function(t){var e=t.visible,t=t.inView;!e||this.inView&&!t?_n(this.$el):(!0===this.autoplay||this.inView&&t)&&Cn(this.$el)},events:["resize","scroll"]}},yi={mixins:[pi,xi],props:{width:Number,height:Number},data:{automute:!0},update:{read:function(){var t=this.$el,e=function(t){for(;t=Tt(t);)if("static"!==Re(t,"position"))return t}(t)||Tt(t),n=e.offsetHeight,e=e.offsetWidth,n=nt.cover({width:this.width||t.naturalWidth||t.videoWidth||t.clientWidth,height:this.height||t.naturalHeight||t.videoHeight||t.clientHeight},{width:e+(e%2?1:0),height:n+(n%2?1:0)});return!(!n.width||!n.height)&&n},write:function(t){var e=t.height,t=t.width;Re(this.$el,{height:e,width:t})},events:["resize"]}};var ki,$i={props:{pos:String,offset:null,flip:Boolean,clsPos:String},data:{pos:"bottom-"+(lt?"right":"left"),flip:!0,offset:!1,clsPos:""},computed:{pos:function(t){t=t.pos;return(t+(w(t,"-")?"":"-center")).split("-")},dir:function(){return this.pos[0]},align:function(){return this.pos[1]}},methods:{positionAt:function(t,e,n){Oe(t,this.clsPos+"-(top|bottom|left|right)(-[a-z]+)?");var i,r=this.offset,o=this.getAxis();B(r)||(r=(i=Me(r))?sn(i)["x"===o?"left":"top"]-sn(e)["x"===o?"right":"bottom"]:0);r=Xn(t,e,"x"===o?fn(this.dir)+" "+this.align:this.align+" "+fn(this.dir),"x"===o?this.dir+" "+this.align:this.align+" "+this.dir,"x"===o?""+("left"===this.dir?-r:r):" "+("top"===this.dir?-r:r),null,this.flip,n).target,n=r.x,r=r.y;this.dir="x"===o?n:r,this.align="x"===o?r:n,Le(t,this.clsPos+"-"+this.dir+"-"+this.align,!1===this.offset)},getAxis:function(){return"top"===this.dir||"bottom"===this.dir?"y":"x"}}},Si={mixins:[$i,mi],args:"pos",props:{mode:"list",toggle:Boolean,boundary:Boolean,boundaryAlign:Boolean,delayShow:Number,delayHide:Number,clsDrop:String},data:{mode:["click","hover"],toggle:"- *",boundary:!0,boundaryAlign:!1,delayShow:0,delayHide:800,clsDrop:!1,animation:["uk-animation-fade"],cls:"uk-open"},computed:{boundary:function(t,e){t=t.boundary;return!0===t?window:Ht(t,e)},clsDrop:function(t){return t.clsDrop||"uk-"+this.$options.name},clsPos:function(){return this.clsDrop}},created:function(){this.tracker=new kn},connected:function(){Be(this.$el,this.clsDrop);var t=this.$props.toggle;this.toggle=t&&this.$create("toggle",Ht(t,this.$el),{target:this.$el,mode:this.mode})},disconnected:function(){this.isActive()&&(ki=null)},events:[{name:"click",delegate:function(){return"."+this.clsDrop+"-close"},handler:function(t){t.preventDefault(),this.hide(!1)}},{name:"click",delegate:function(){return'a[href^="#"]'},handler:function(t){var e=t.defaultPrevented,t=t.current.hash;e||!t||Bt(t,this.$el)||this.hide(!1)}},{name:"beforescroll",handler:function(){this.hide(!1)}},{name:"toggle",self:!0,handler:function(t,e){t.preventDefault(),this.isToggled()?this.hide(!1):this.show(e,!1)}},{name:"toggleshow",self:!0,handler:function(t,e){t.preventDefault(),this.show(e)}},{name:"togglehide",self:!0,handler:function(t){t.preventDefault(),this.hide()}},{name:wt+" focusin",filter:function(){return w(this.mode,"hover")},handler:function(t){ae(t)||this.clearTimers()}},{name:bt+" focusout",filter:function(){return w(this.mode,"hover")},handler:function(t){!ae(t)&&t.relatedTarget&&this.hide()}},{name:"toggled",self:!0,handler:function(t,e){e&&(this.clearTimers(),this.position())}},{name:"show",self:!0,handler:function(){var r=this;(ki=this).tracker.init(),Qt(this.$el,"hide",Jt(document,mt,function(t){var i=t.target;return!Bt(i,r.$el)&&Qt(document,vt+" "+xt+" scroll",function(t){var e=t.defaultPrevented,n=t.type,t=t.target;e||n!==vt||i!==t||r.toggle&&Bt(i,r.toggle.$el)||r.hide(!1)},!0)}),{self:!0}),Qt(this.$el,"hide",Jt(document,"keydown",function(t){27===t.keyCode&&r.hide(!1)}),{self:!0})}},{name:"beforehide",self:!0,handler:function(){this.clearTimers()}},{name:"hide",handler:function(t){t=t.target;this.$el===t?(ki=this.isActive()?null:ki,this.tracker.cancel()):ki=null===ki&&Bt(t,this.$el)&&this.isToggled()?this:ki}}],update:{write:function(){this.isToggled()&&!He(this.$el,this.clsEnter)&&this.position()},events:["resize"]},methods:{show:function(t,e){var n,i=this;if(void 0===t&&(t=this.toggle),void 0===e&&(e=!0),this.isToggled()&&t&&this.toggle&&t.$el!==this.toggle.$el&&this.hide(!1),this.toggle=t,this.clearTimers(),!this.isActive()){if(ki){if(e&&ki.isDelaying)return void(this.showTimer=setTimeout(this.show,10));for(;ki&&n!==ki&&!Bt(this.$el,ki.$el);)(n=ki).hide(!1)}this.showTimer=setTimeout(function(){return!i.isToggled()&&i.toggleElement(i.$el,!0)},e&&this.delayShow||0)}},hide:function(t){var e=this;void 0===t&&(t=!0);function n(){return e.toggleElement(e.$el,!1,!1)}var i,r;this.clearTimers(),this.isDelaying=(i=this.$el,r=[],Ae(i,function(t){return"static"!==Re(t,"position")&&r.push(t)}),r.some(function(t){return e.tracker.movesTo(t)})),t&&this.isDelaying?this.hideTimer=setTimeout(this.hide,50):t&&this.delayHide?this.hideTimer=setTimeout(n,this.delayHide):n()},clearTimers:function(){clearTimeout(this.showTimer),clearTimeout(this.hideTimer),this.showTimer=null,this.hideTimer=null,this.isDelaying=!1},isActive:function(){return ki===this},position:function(){De(this.$el,this.clsDrop+"-stack"),Le(this.$el,this.clsDrop+"-boundary",this.boundaryAlign);var t,e=sn(this.boundary),n=this.boundaryAlign?e:sn(this.toggle.$el);"justify"===this.align?(t="y"===this.getAxis()?"width":"height",Re(this.$el,t,n[t])):this.boundary&&this.$el.offsetWidth>Math.max(e.right-n.left,n.right-e.left)&&Be(this.$el,this.clsDrop+"-stack"),this.positionAt(this.$el,this.boundaryAlign?this.boundary:this.toggle.$el,this.boundary)}}};var Ii={mixins:[pi],args:"target",props:{target:Boolean},data:{target:!1},computed:{input:function(t,e){return Me(St,e)},state:function(){return this.input.nextElementSibling},target:function(t,e){t=t.target;return t&&(!0===t&&Tt(this.input)===e&&this.input.nextElementSibling||Ht(t,e))}},update:function(){var t,e,n=this.target,i=this.input;!n||n[e=It(n)?"value":"textContent"]!==(i=i.files&&i.files[0]?i.files[0].name:Mt(i,"select")&&(t=ze("option",i).filter(function(t){return t.selected})[0])?t.textContent:i.value)&&(n[e]=i)},events:[{name:"change",handler:function(){this.$update()}},{name:"reset",el:function(){return Nt(this.$el,"form")},handler:function(){this.$update()}}]},Ei={update:{read:function(t){var e=Ln(this.$el);if(!e||t.isInView===e)return!1;t.isInView=e},write:function(){this.$el.src=""+this.$el.src},events:["scroll","resize"]}},Ti={props:{margin:String,firstColumn:Boolean},data:{margin:"uk-margin-small-top",firstColumn:"uk-first-column"},update:{read:function(){var t=Ci(this.$el.children);return{rows:t,columns:function(t){for(var e=[],n=0;n=c[n]-1&&s[e]!==c[e]){i.push([o]);break}if(s[n]-1>c[e]||s[e]===c[e]){u.push(o);break}if(0===a){i.unshift([o]);break}}}return i}function Ai(t,e){var n=t.offsetTop,i=t.offsetLeft,r=t.offsetHeight,o=t.offsetWidth;return(e=void 0===e?!1:e)&&(n=(t=un(t))[0],i=t[1]),{top:n,left:i,bottom:n+r,right:i+o}}var Mi={extends:Ti,mixins:[pi],name:"grid",props:{masonry:Boolean,parallax:Number},data:{margin:"uk-grid-margin",clsStack:"uk-grid-stack",masonry:!1,parallax:0},connected:function(){this.masonry&&Be(this.$el,"uk-flex-top uk-flex-wrap-top")},update:[{write:function(t){t=t.columns;Le(this.$el,this.clsStack,t.length<2)},events:["resize"]},{read:function(t){var e=t.columns,n=t.rows;if(!e.length||!this.masonry&&!this.parallax||zi(this.$el))return t.translates=!1;var i,r,o=!1,s=Ot(this.$el),a=e.map(function(t){return t.reduce(function(t,e){return t+e.offsetHeight},0)}),u=(t=s,i=this.margin,L((s=t.filter(function(t){return He(t,i)})[0])?Re(s,"marginTop"):Re(t[0],"paddingLeft"))*(n.length-1)),c=Math.max.apply(Math,a)+u;this.masonry&&(e=e.map(function(t){return K(t,"offsetTop")}),t=e,r=n.map(function(t){return Math.max.apply(Math,t.map(function(t){return t.offsetHeight}))}),o=t.map(function(n){var i=0;return n.map(function(t,e){return i+=e?r[e-1]-n[e-1].offsetHeight:0})}));var h=Math.abs(this.parallax);return{padding:h=h&&a.reduce(function(t,e,n){return Math.max(t,e+u+(n%2?h:h/8)-c)},0),columns:e,translates:o,height:o?c:""}},write:function(t){var e=t.height,t=t.padding;Re(this.$el,"paddingBottom",t||""),!1!==e&&Re(this.$el,"height",e)},events:["resize"]},{read:function(t){t=t.height;return!zi(this.$el)&&{scrolled:!!this.parallax&&Wn(this.$el,t?t-cn(this.$el):0)*Math.abs(this.parallax)}},write:function(t){var e=t.columns,i=t.scrolled,r=t.translates;!1===i&&!r||e.forEach(function(t,n){return t.forEach(function(t,e){return Re(t,"transform",i||r?"translateY("+((r&&-r[n][e])+(i?n%2?i:i/8:0))+"px)":"")})})},events:["scroll","resize"]}]};function zi(t){return Ot(t).some(function(t){return"absolute"===Re(t,"position")})}var Ni=ht?{props:{selMinHeight:String},data:{selMinHeight:!1,forceHeight:!1},computed:{elements:function(t,e){t=t.selMinHeight;return t?ze(t,e):[e]}},update:[{read:function(){Re(this.elements,"height","")},order:-5,events:["resize"]},{write:function(){var n=this;this.elements.forEach(function(t){var e=L(Re(t,"minHeight"));e&&(n.forceHeight||Math.round(e+dn(t,"height","content-box"))>=t.offsetHeight)&&Re(t,"height",e)})},order:5,events:["resize"]}]}:{},Bi={mixins:[Ni],args:"target",props:{target:String,row:Boolean},data:{target:"> *",row:!0,forceHeight:!0},computed:{elements:function(t,e){return ze(t.target,e)}},update:{read:function(){return{rows:(this.row?Ci(this.elements):[this.elements]).map(Di)}},write:function(t){t.rows.forEach(function(t){var n=t.heights;return t.elements.forEach(function(t,e){return Re(t,"minHeight",n[e])})})},events:["resize"]}};function Di(t){if(t.length<2)return{heights:[""],elements:t};var n=t.map(Oi),i=Math.max.apply(Math,n),e=t.some(function(t){return t.style.minHeight}),r=t.some(function(t,e){return!t.style.minHeight&&n[e]"}return Fi[t][e]}(t,e)||t);return(t=Me(t.substr(t.indexOf("/g,Fi={};function Wi(t){return Math.ceil(Math.max.apply(Math,[0].concat(ze("[stroke]",t).map(function(t){try{return t.getTotalLength()}catch(t){return 0}}))))}function Vi(t,e){return Ri(t)&&Ri(e)&&qi(t)===qi(e)}function Ri(t){return t&&"svg"===t.tagName}function qi(t){return(t.innerHTML||(new XMLSerializer).serializeToString(t).replace(/(.*?)<\/svg>/g,"$1")).replace(/\s/g,"")}var Ui={spinner:'',totop:'',marker:'',"close-icon":'',"close-large":'',"navbar-toggle-icon":'',"overlay-icon":'',"pagination-next":'',"pagination-previous":'',"search-icon":'',"search-large":'',"search-navbar":'',"slidenav-next":'',"slidenav-next-large":'',"slidenav-previous":'',"slidenav-previous-large":''},Yi={install:function(r){r.icon.add=function(t,e){var n,i=z(t)?((n={})[t]=e,n):t;G(i,function(t,e){Ui[e]=t,delete Qi[e]}),r._initialized&&Ae(document.body,function(t){return G(r.getComponents(t),function(t){t.$options.isIcon&&t.icon in i&&t.$reset()})})}},extends:Hi,args:"icon",props:["icon"],data:{include:["focusable"]},isIcon:!0,beforeConnect:function(){Be(this.$el,"uk-icon")},methods:{getSvg:function(){var t=function(t){if(!Ui[t])return null;Qi[t]||(Qi[t]=Me((Ui[function(t){return lt?U(U(t,"left","right"),"previous","next"):t}(t)]||Ui[t]).trim()));return Qi[t].cloneNode(!0)}(this.icon);return t?he.resolve(t):he.reject("Icon not found.")}}},Xi={args:!1,extends:Yi,data:function(t){return{icon:d(t.constructor.options.name)}},beforeConnect:function(){Be(this.$el,this.$name)}},Gi={extends:Xi,beforeConnect:function(){Be(this.$el,"uk-slidenav")},computed:{icon:function(t,e){t=t.icon;return He(e,"uk-slidenav-large")?t+"-large":t}}},Ki={extends:Xi,computed:{icon:function(t,e){t=t.icon;return He(e,"uk-search-icon")&&Dt(e,".uk-search-large").length?"search-large":Dt(e,".uk-search-navbar").length?"search-navbar":t}}},Ji={extends:Xi,computed:{icon:function(){return"close-"+(He(this.$el,"uk-close-large")?"large":"icon")}}},Zi={extends:Xi,connected:function(){var e=this;this.svg.then(function(t){return t&&1!==e.ratio&&Re(Me("circle",t),"strokeWidth",1/e.ratio)})}},Qi={};var tr={args:"dataSrc",props:{dataSrc:String,dataSrcset:Boolean,sizes:String,width:Number,height:Number,offsetTop:String,offsetLeft:String,target:String},data:{dataSrc:"",dataSrcset:!1,sizes:!1,width:!1,height:!1,offsetTop:"50vh",offsetLeft:"50vw",target:!1},computed:{cacheKey:function(t){t=t.dataSrc;return this.$name+"."+t},width:function(t){var e=t.width,t=t.dataWidth;return e||t},height:function(t){var e=t.height,t=t.dataHeight;return e||t},sizes:function(t){var e=t.sizes,t=t.dataSizes;return e||t},isImg:function(t,e){return ar(e)},target:{get:function(t){t=t.target;return[this.$el].concat(Lt(t,this.$el))},watch:function(){this.observe()}},offsetTop:function(t){return pn(t.offsetTop,"height")},offsetLeft:function(t){return pn(t.offsetLeft,"width")}},connected:function(){window.IntersectionObserver?(cr[this.cacheKey]?er(this.$el,cr[this.cacheKey],this.dataSrcset,this.sizes):this.isImg&&this.width&&this.height&&er(this.$el,function(t,e,n){n&&(n=nt.ratio({width:t,height:e},"width",pn(ir(n))),t=n.width,e=n.height);return'data:image/svg+xml;utf8,'}(this.width,this.height,this.sizes)),this.observer=new IntersectionObserver(this.load,{rootMargin:this.offsetTop+"px "+this.offsetLeft+"px"}),requestAnimationFrame(this.observe)):er(this.$el,this.dataSrc,this.dataSrcset,this.sizes)},disconnected:function(){this.observer&&this.observer.disconnect()},update:{read:function(t){var e=this,t=t.image;return!!this.observer&&(t||"complete"!==document.readyState||this.load(this.observer.takeRecords()),!this.isImg&&void(t&&t.then(function(t){return t&&""!==t.currentSrc&&er(e.$el,ur(t))})))},write:function(t){var e,n,i;this.dataSrcset&&1!==window.devicePixelRatio&&(!(n=Re(this.$el,"backgroundSize")).match(/^(auto\s?)+$/)&&L(n)!==t.bgSize||(t.bgSize=(e=this.dataSrcset,n=this.sizes,i=pn(ir(n)),(e=(e.match(sr)||[]).map(L).sort(function(t,e){return t-e})).filter(function(t){return i<=t})[0]||e.pop()||""),Re(this.$el,"backgroundSize",t.bgSize+"px")))},events:["resize"]},methods:{load:function(t){var e=this;t.some(function(t){return O(t.isIntersecting)||t.isIntersecting})&&(this._data.image=ge(this.dataSrc,this.dataSrcset,this.sizes).then(function(t){return er(e.$el,ur(t),t.srcset,t.sizes),cr[e.cacheKey]=ur(t),t},function(t){return te(e.$el,new t.constructor(t.type,t))}),this.observer.disconnect())},observe:function(){var e=this;this._connected&&!this._data.image&&this.target.forEach(function(t){return e.observer.observe(t)})}}};function er(t,e,n,i){ar(t)?(i&&(t.sizes=i),n&&(t.srcset=n),e&&(t.src=e)):e&&!w(t.style.backgroundImage,e)&&(Re(t,"backgroundImage","url("+Kt(e)+")"),te(t,ee("load",!1)))}var nr=/\s*(.*?)\s*(\w+|calc\(.*?\))\s*(?:,|$)/g;function ir(t){var e,n;for(nr.lastIndex=0;e=nr.exec(t);)if(!e[1]||window.matchMedia(e[1]).matches){e=g(n=e[2],"calc")?n.slice(5,-1).replace(rr,function(t){return pn(t)}).replace(/ /g,"").match(or).reduce(function(t,e){return t+ +e},0):n;break}return e||"100vw"}var rr=/\d+(?:\w+|%)/g,or=/[+-]?(\d+)/g;var sr=/\s+\d+w\s*(?:,|$)/g;function ar(t){return"IMG"===t.tagName}function ur(t){return t.currentSrc||t.src}var cr,hr="__test__";try{(cr=window.sessionStorage||{})[hr]=1,delete cr[hr]}catch(t){cr={}}var lr={props:{media:Boolean},data:{media:!1},computed:{matchMedia:function(){var t=function(t){if(z(t))if("@"===t[0])t=L(Xe("breakpoint-"+t.substr(1)));else if(isNaN(t))return t;return!(!t||isNaN(t))&&"(min-width: "+t+"px)"}(this.media);return!t||window.matchMedia(t).matches}}};var dr={mixins:[pi,lr],props:{fill:String},data:{fill:"",clsWrapper:"uk-leader-fill",clsHide:"uk-leader-hide",attrFill:"data-fill"},computed:{fill:function(t){return t.fill||Xe("leader-fill-content")}},connected:function(){var t=Ie(this.$el,'');this.wrapper=t[0]},disconnected:function(){Ee(this.wrapper.childNodes)},update:{read:function(t){var e,n=t.changed,t=e=t.width;return{width:e=Math.floor(this.$el.offsetWidth/2),fill:this.fill,changed:n||t!==e,hide:!this.matchMedia}},write:function(t){Le(this.wrapper,this.clsHide,t.hide),t.changed&&(t.changed=!1,ot(this.wrapper,this.attrFill,new Array(t.width).join(t.fill)))},events:["resize"]}},fr={props:{container:Boolean},data:{container:!0},computed:{container:function(t){t=t.container;return!0===t&&this.$container||t&&Me(t)}}},pr=[],mr={mixins:[pi,fr,mi],props:{selPanel:String,selClose:String,escClose:Boolean,bgClose:Boolean,stack:Boolean},data:{cls:"uk-open",escClose:!0,bgClose:!0,overlay:!0,stack:!1},computed:{panel:function(t,e){return Me(t.selPanel,e)},transitionElement:function(){return this.panel},bgClose:function(t){return t.bgClose&&this.panel}},beforeDisconnect:function(){this.isToggled()&&this.toggleElement(this.$el,!1,!1)},events:[{name:"click",delegate:function(){return this.selClose},handler:function(t){t.preventDefault(),this.hide()}},{name:"toggle",self:!0,handler:function(t){t.defaultPrevented||(t.preventDefault(),this.isToggled()===w(pr,this)&&this.toggle())}},{name:"beforeshow",self:!0,handler:function(t){if(w(pr,this))return!1;!this.stack&&pr.length?(he.all(pr.map(function(t){return t.hide()})).then(this.show),t.preventDefault()):pr.push(this)}},{name:"show",self:!0,handler:function(){var r=this,t=document.documentElement;hn(window)>t.clientWidth&&this.overlay&&Re(document.body,"overflowY","scroll"),this.stack&&Re(this.$el,"zIndex",L(Re(this.$el,"zIndex"))+pr.length),Be(t,this.clsPage),this.bgClose&&Qt(this.$el,"hide",Jt(document,mt,function(t){var i=t.target;X(pr)!==r||r.overlay&&!Bt(i,r.$el)||Bt(i,r.panel)||Qt(document,vt+" "+xt+" scroll",function(t){var e=t.defaultPrevented,n=t.type,t=t.target;e||n!==vt||i!==t||r.hide()},!0)}),{self:!0}),this.escClose&&Qt(this.$el,"hide",Jt(document,"keydown",function(t){27===t.keyCode&&X(pr)===r&&r.hide()}),{self:!0})}},{name:"hidden",self:!0,handler:function(){var e=this;w(pr,this)&&pr.splice(pr.indexOf(this),1),pr.length||Re(document.body,"overflowY",""),Re(this.$el,"zIndex",""),pr.some(function(t){return t.clsPage===e.clsPage})||De(document.documentElement,this.clsPage)}}],methods:{toggle:function(){return this.isToggled()?this.hide():this.show()},show:function(){var e=this;return this.container&&Tt(this.$el)!==this.container?(be(this.container,this.$el),new he(function(t){return requestAnimationFrame(function(){return e.show().then(t)})})):this.toggleElement(this.$el,!0,gr(this))},hide:function(){return this.toggleElement(this.$el,!1,gr(this))}}};function gr(t){var s=t.transitionElement,a=t._toggle;return function(r,o){return new he(function(n,i){return Qt(r,"show hide",function(){r._reject&&r._reject(),r._reject=i,a(r,o);var t=Qt(s,"transitionstart",function(){Qt(s,"transitionend transitioncancel",n,{self:!0}),clearTimeout(e)},{self:!0}),e=setTimeout(function(){t(),n()},R(Re(s,"transitionDuration")))})}).then(function(){return delete r._reject})}}o={mixins:[pi,Ni],props:{dropdown:String,mode:"list",align:String,offset:Number,boundary:Boolean,boundaryAlign:Boolean,clsDrop:String,delayShow:Number,delayHide:Number,dropbar:Boolean,dropbarMode:String,dropbarAnchor:Boolean,duration:Number},data:{dropdown:".uk-navbar-nav > li",align:lt?"right":"left",clsDrop:"uk-navbar-dropdown",mode:void 0,offset:void 0,delayShow:void 0,delayHide:void 0,boundaryAlign:void 0,flip:"x",boundary:!0,dropbar:!1,dropbarMode:"slide",dropbarAnchor:!1,duration:200,forceHeight:!0,selMinHeight:".uk-navbar-nav > li > a, .uk-navbar-item, .uk-navbar-toggle"},computed:{boundary:function(t,e){var n=t.boundary,t=t.boundaryAlign;return!0===n||t?e:n},dropbarAnchor:function(t,e){return Ht(t.dropbarAnchor,e)},pos:function(t){return"bottom-"+t.align},dropbar:{get:function(t){t=t.dropbar;return t?(t=this._dropbar||Ht(t,this.$el)||Me("+ .uk-navbar-dropbar",this.$el))||(this._dropbar=Me("
    ")):null},watch:function(t){Be(t,"uk-navbar-dropbar")},immediate:!0},dropdowns:{get:function(t,e){return ze(t.dropdown+" ."+t.clsDrop,e)},watch:function(t){var e=this;this.$create("drop",t.filter(function(t){return!e.getDropdown(t)}),Y({},this.$props,{boundary:this.boundary,pos:this.pos,offset:this.dropbar||this.offset}))},immediate:!0}},disconnected:function(){this.dropbar&&$e(this.dropbar),delete this._dropbar},events:[{name:"mouseover",delegate:function(){return this.dropdown},handler:function(t){var e=t.current,t=this.getActive();t&&t.toggle&&!Bt(t.toggle.$el,e)&&!t.tracker.movesTo(t.$el)&&t.hide(!1)}},{name:"mouseleave",el:function(){return this.dropbar},handler:function(){var t=this.getActive();t&&!this.dropdowns.some(function(t){return Mt(t,":hover")})&&t.hide()}},{name:"beforeshow",capture:!0,filter:function(){return this.dropbar},handler:function(){Tt(this.dropbar)||ye(this.dropbarAnchor||this.$el,this.dropbar)}},{name:"show",filter:function(){return this.dropbar},handler:function(t,e){var n=e.$el,e=e.dir;He(n,this.clsDrop)&&("slide"===this.dropbarMode&&Be(this.dropbar,"uk-navbar-dropbar-slide"),this.clsDrop&&Be(n,this.clsDrop+"-dropbar"),"bottom"===e&&this.transitionTo(n.offsetHeight+L(Re(n,"marginTop"))+L(Re(n,"marginBottom")),n))}},{name:"beforehide",filter:function(){return this.dropbar},handler:function(t,e){var n=e.$el,e=this.getActive();Mt(this.dropbar,":hover")&&e&&e.$el===n&&t.preventDefault()}},{name:"hide",filter:function(){return this.dropbar},handler:function(t,e){var n=e.$el;!He(n,this.clsDrop)||(!(e=this.getActive())||e&&e.$el===n)&&this.transitionTo(0)}}],methods:{getActive:function(){var t=this.dropdowns.map(this.getDropdown).filter(function(t){return t&&t.isActive()})[0];return t&&w(t.mode,"hover")&&Bt(t.toggle.$el,this.$el)&&t},transitionTo:function(t,e){var n=this,i=this.dropbar,r=$t(i)?cn(i):0;return Re(e=r"),Be(Tt(this.panel),this.clsMode)),Re(document.documentElement,"overflowY",this.overlay?"hidden":""),Be(document.body,this.clsContainer,this.clsFlip),Re(document.body,"touch-action","pan-y pinch-zoom"),Re(this.$el,"display","block"),Be(this.$el,this.clsOverlay),Be(this.panel,this.clsSidebarAnimation,"reveal"!==this.mode?this.clsMode:""),cn(document.body),Be(document.body,this.clsContainerAnimation),this.clsContainerAnimation&&(vr().content+=",user-scalable=0")}},{name:"hide",self:!0,handler:function(){De(document.body,this.clsContainerAnimation),Re(document.body,"touch-action","")}},{name:"hidden",self:!0,handler:function(){var t;this.clsContainerAnimation&&((t=vr()).content=t.content.replace(/,user-scalable=0$/,"")),"reveal"===this.mode&&Ee(this.panel),De(this.panel,this.clsSidebarAnimation,this.clsMode),De(this.$el,this.clsOverlay),Re(this.$el,"display",""),De(document.body,this.clsContainer,this.clsFlip),Re(document.documentElement,"overflowY","")}},{name:"swipeLeft swipeRight",handler:function(t){this.isToggled()&&u(t.type,"Left")^this.flip&&this.hide()}}]};function vr(){return Me('meta[name="viewport"]',document.head)||be(document.head,'')}var t={mixins:[pi],props:{selContainer:String,selContent:String},data:{selContainer:".uk-modal",selContent:".uk-modal-dialog"},computed:{container:function(t,e){return Nt(e,t.selContainer)},content:function(t,e){return Nt(e,t.selContent)}},connected:function(){Re(this.$el,"minHeight",150)},update:{read:function(){return!!(this.content&&this.container&&$t(this.$el))&&{current:L(Re(this.$el,"maxHeight")),max:Math.max(150,cn(this.container)-(on(this.content).height-cn(this.$el)))}},write:function(t){var e=t.current,t=t.max;Re(this.$el,"maxHeight",t),Math.round(e)!==Math.round(t)&&te(this.$el,"resize")},events:["resize"]}},dt={props:{offset:Number},data:{offset:0},methods:{scrollTo:function(t){var e=this;t=t&&Me(t)||document.body,te(this.$el,"beforescroll",[this,t])&&Fn(t,{offset:this.offset}).then(function(){return te(e.$el,"scrolled",[e,t])})}},events:{click:function(t){t.defaultPrevented||(t.preventDefault(),this.scrollTo("#"+Kt(decodeURIComponent((this.$el.hash||"").substr(1)))))}}},wr="_ukScrollspy",ft={args:"cls",props:{cls:String,target:String,hidden:Boolean,offsetTop:Number,offsetLeft:Number,repeat:Boolean,delay:Number},data:function(){return{cls:!1,target:!1,hidden:!0,offsetTop:0,offsetLeft:0,repeat:!1,delay:0,inViewClass:"uk-scrollspy-inview"}},computed:{elements:{get:function(t,e){t=t.target;return t?ze(t,e):[e]},watch:function(t){this.hidden&&Re(Ct(t,":not(."+this.inViewClass+")"),"visibility","hidden")},immediate:!0}},disconnected:function(){var e=this;this.elements.forEach(function(t){De(t,e.inViewClass,t[wr]?t[wr].cls:""),delete t[wr]})},update:[{read:function(t){var e=this;if(!t.update)return he.resolve().then(function(){e.$emit(),t.update=!0}),!1;this.elements.forEach(function(t){t[wr]||(t[wr]={cls:ut(t,"uk-scrollspy-class")||e.cls}),t[wr].show=Ln(t,e.offsetTop,e.offsetLeft)})},write:function(n){var i=this;this.elements.forEach(function(t){var e=t[wr];!e.show||e.inview||e.queued?!e.show&&e.inview&&!e.queued&&i.repeat&&i.toggle(t,!1):(e.queued=!0,n.promise=(n.promise||he.resolve()).then(function(){return new he(function(t){return setTimeout(t,i.delay)})}).then(function(){i.toggle(t,!0),setTimeout(function(){e.queued=!1,i.$emit()},300)}))})},events:["scroll","resize"]}],methods:{toggle:function(t,e){var n=t[wr];n.off&&n.off(),Re(t,"visibility",!e&&this.hidden?"hidden":""),Le(t,this.inViewClass,e),Le(t,n.cls),/\buk-animation-/.test(n.cls)&&(n.off=Qt(t,"animationcancel animationend",function(){return Oe(t,"uk-animation-\\w*")})),te(t,e?"inview":"outview"),n.inview=e,this.$update(t)}}},_t={props:{cls:String,closest:String,scroll:Boolean,overflow:Boolean,offset:Number},data:{cls:"uk-active",closest:!1,scroll:!1,overflow:!0,offset:0},computed:{links:{get:function(t,e){return ze('a[href^="#"]',e).filter(function(t){return t.hash})},watch:function(t){this.scroll&&this.$create("scroll",t,{offset:this.offset||0})},immediate:!0},targets:function(){return ze(this.links.map(function(t){return Kt(t.hash).substr(1)}).join(","))},elements:function(t){t=t.closest;return Nt(this.links,t||"*")}},update:[{read:function(){var n=this,t=this.targets.length;if(!t||!$t(this.$el))return!1;var i=Vn(this.targets,/auto|scroll/,!0)[0],e=i.scrollTop,r=i.scrollHeight-qn(i),o=!1;return e===r?o=t-1:(this.targets.every(function(t,e){if(sn(t).top-sn(Rn(i)).top-n.offset<=0)return o=e,!0}),!1===o&&this.overflow&&(o=0)),{active:o}},write:function(t){t=t.active;this.links.forEach(function(t){return t.blur()}),De(this.elements,this.cls),!1!==t&&te(this.$el,"active",[t,Be(this.elements[t],this.cls)])},events:["scroll","resize"]}]},pe={mixins:[pi,lr],props:{top:null,bottom:Boolean,offset:String,animation:String,clsActive:String,clsInactive:String,clsFixed:String,clsBelow:String,selTarget:String,widthElement:Boolean,showOnUp:Boolean,targetOffset:Number},data:{top:0,bottom:!1,offset:0,animation:"",clsActive:"uk-active",clsInactive:"",clsFixed:"uk-sticky-fixed",clsBelow:"uk-sticky-below",selTarget:"",widthElement:!1,showOnUp:!1,targetOffset:!1},computed:{offset:function(t){return pn(t.offset)},selTarget:function(t,e){t=t.selTarget;return t&&Me(t,e)||e},widthElement:function(t,e){return Ht(t.widthElement,e)||this.placeholder},isActive:{get:function(){return He(this.selTarget,this.clsActive)},set:function(t){t&&!this.isActive?(Pe(this.selTarget,this.clsInactive,this.clsActive),te(this.$el,"active")):t||He(this.selTarget,this.clsInactive)||(Pe(this.selTarget,this.clsActive,this.clsInactive),te(this.$el,"inactive"))}}},connected:function(){this.placeholder=Me("+ .uk-sticky-placeholder",this.$el)||Me('
    '),this.isFixed=!1,this.isActive=!1},disconnected:function(){this.isFixed&&(this.hide(),De(this.selTarget,this.clsInactive)),$e(this.placeholder),this.placeholder=null,this.widthElement=null},events:[{name:"load hashchange popstate",el:function(){return window},handler:function(){var i,r=this;!1!==this.targetOffset&&location.hash&&0this.topOffset?(nn.cancel(this.$el),nn.out(this.$el,this.animation).then(function(){return n.hide()},Q)):this.hide()):nn.inProgress(this.$el)&&cthis.top,e=Math.max(0,this.offset);B(this.bottom)&&this.scroll>this.bottom-this.offset&&(e=this.bottom-this.scroll),Re(this.$el,{position:"fixed",top:e+"px",width:this.width}),this.isActive=t,Le(this.$el,this.clsBelow,this.scroll>this.bottomOffset),Be(this.$el,this.clsFixed)}}};function br(t,e){var n=e.$props,i=e.$el,e=e[t+"Offset"],t=n[t];if(t)return z(t)&&t.match(/^-?\d/)?e+pn(t):sn(!0===t?Tt(i):Ht(t,i)).bottom}var xr,yr,kr,Zn={mixins:[mi],args:"connect",props:{connect:String,toggle:String,active:Number,swiping:Boolean},data:{connect:"~.uk-switcher",toggle:"> * > :first-child",active:0,swiping:!0,cls:"uk-active",attrItem:"uk-switcher-item"},computed:{connects:{get:function(t,e){return Lt(t.connect,e)},watch:function(t){var n=this;this.swiping&&Re(t,"touch-action","pan-y pinch-zoom");var i=this.index();this.connects.forEach(function(t){return Ot(t).forEach(function(t,e){return Le(t,n.cls,e===i)})})},immediate:!0},toggles:{get:function(t,e){return ze(t.toggle,e).filter(function(t){return!Mt(t,".uk-disabled *, .uk-disabled, [disabled]")})},watch:function(t){var e=this.index();this.show(~e?e:t[this.active]||t[0])},immediate:!0},children:function(){var t=this;return Ot(this.$el).filter(function(e){return t.toggles.some(function(t){return Bt(t,e)})})}},events:[{name:"click",delegate:function(){return this.toggle},handler:function(t){t.preventDefault(),this.show(t.current)}},{name:"click",el:function(){return this.connects},delegate:function(){return"["+this.attrItem+"],[data-"+this.attrItem+"]"},handler:function(t){t.preventDefault(),this.show(ut(t.current,this.attrItem))}},{name:"swipeRight swipeLeft",filter:function(){return this.swiping},el:function(){return this.connects},handler:function(t){t=t.type;this.show(u(t,"Left")?"next":"previous")}}],methods:{index:function(){var e=this;return x(this.children,function(t){return He(t,e.cls)})},show:function(t){var n=this,i=this.index(),r=it(this.children[it(t,this.toggles,i)],Ot(this.$el));i!==r&&(this.children.forEach(function(t,e){Le(t,n.cls,r===e),ot(n.toggles[e],"aria-expanded",r===e)}),this.connects.forEach(function(t){var e=t.children;return n.toggleElement(W(e).filter(function(t){return He(t,n.cls)}),!1,0<=i).then(function(){return n.toggleElement(e[r],!0,0<=i)})}))}}},hr={mixins:[pi],extends:Zn,props:{media:Boolean},data:{media:960,attrItem:"uk-tab-item"},connected:function(){var t=He(this.$el,"uk-tab-left")?"uk-tab-left":!!He(this.$el,"uk-tab-right")&&"uk-tab-right";t&&this.$create("toggle",this.$el,{cls:t,mode:"media",media:this.media})}},Ni={mixins:[lr,mi],args:"target",props:{href:String,target:null,mode:"list",queued:Boolean},data:{href:!1,target:!1,mode:"click",queued:!0},connected:function(){Et(this.$el)||ot(this.$el,"tabindex","0")},computed:{target:{get:function(t,e){var n=t.href;return(t=Lt((t=t.target)||n,e)).length&&t||[e]},watch:function(){this.updateAria()},immediate:!0}},events:[{name:wt+" "+bt+" focus blur",filter:function(){return w(this.mode,"hover")},handler:function(t){ae(t)||this.toggle("toggle"+(w([wt,"focus"],t.type)?"show":"hide"))}},{name:"click",filter:function(){return w(this.mode,"click")||pt&&w(this.mode,"hover")},handler:function(t){var e;(Nt(t.target,'a[href="#"], a[href=""]')||(e=Nt(t.target,"a[href]"))&&(!$r(this.target,this.cls)||e.hash&&Mt(this.target,e.hash)))&&t.preventDefault(),this.toggle()}},{name:"toggled",self:!0,el:function(){return this.target},handler:function(t,e){this.updateAria(e)}}],update:{read:function(){return!(!w(this.mode,"media")||!this.media)&&{match:this.matchMedia}},write:function(t){var e=t.match,t=this.isToggled(this.target);(e?!t:t)&&this.toggle()},events:["resize"]},methods:{toggle:function(t){var n=this;if(te(this.target,t||"toggle",[this])){if(!this.queued)return this.toggleElement(this.target);var e,i=this.target.filter(function(t){return He(t,n.clsLeave)});i.length?this.target.forEach(function(t){var e=w(i,t);n.toggleElement(t,e,e)}):(e=this.target.filter(this.isToggled),this.toggleElement(e,!1).then(function(){return n.toggleElement(n.target.filter(function(t){return!w(e,t)}),!0)}))}},updateAria:function(t){ot(this.$el,"aria-expanded",M(t)?t:$r(this.target,this.cls))}}};function $r(t,e){return e?He(t,e.split(" ")[0]):$t(t)}function Sr(t){for(var e=t.addedNodes,n=t.removedNodes,i=0;i
    '+t+"
    ",e);return n.show(),Jt(n.$el,"hidden",function(){return he.resolve().then(function(){return n.$destroy(!0)})},{self:!0}),n},a.alert=function(e,t){return i(function(t){t=t.labels;return'
    '+(z(e)?e:we(e))+'
    "},t,function(t){return t.resolve()})},a.confirm=function(e,t){return i(function(t){t=t.labels;return'
    '+(z(e)?e:we(e))+'
    "},t,function(t){return t.reject()})},a.prompt=function(e,n,t){return i(function(t){t=t.labels;return'
    "},t,function(t){return t.resolve(null)},function(t){return Me("input",t.$el).value})},a.labels={ok:"Ok",cancel:"Cancel"}},mixins:[mr],data:{clsPage:"uk-modal-page",selPanel:".uk-modal-dialog",selClose:".uk-modal-close, .uk-modal-close-default, .uk-modal-close-outside, .uk-modal-close-full"},events:[{name:"show",self:!0,handler:function(){He(this.panel,"uk-margin-auto-vertical")?Be(this.$el,"uk-flex"):Re(this.$el,"display","block"),cn(this.$el)}},{name:"hidden",self:!0,handler:function(){Re(this.$el,"display",""),De(this.$el,"uk-flex")}}]},Nav:{extends:vi,data:{targets:"> .uk-parent",toggle:"> a",content:"> ul"}},Navbar:o,Offcanvas:l,OverflowAuto:t,Responsive:{props:["width","height"],connected:function(){Be(this.$el,"uk-responsive-width")},update:{read:function(){return!!($t(this.$el)&&this.width&&this.height)&&{width:hn(Tt(this.$el)),height:this.height}},write:function(t){cn(this.$el,nt.contain({height:this.height,width:this.width},t).height)},events:["resize"]}},Scroll:dt,Scrollspy:ft,ScrollspyNav:_t,Sticky:pe,Svg:Hi,Switcher:Zn,Tab:hr,Toggle:Ni,Video:xi,Close:Ji,Spinner:Zi,SlidenavNext:Gi,SlidenavPrevious:Gi,SearchIcon:Ki,Marker:Xi,NavbarToggleIcon:Xi,OverlayIcon:Xi,PaginationNext:Xi,PaginationPrevious:Xi,Totop:Xi}),function(t,e){return Qn.component(e,t)}),Qn.use(function(e){var t,n,i,r;ct&&(n=function(){t||(t=!0,gn.write(function(){return t=!1}),e.update(null,"resize"))},Jt(window,"load resize",n),Jt(document,"loadedmetadata load",n,!0),"ResizeObserver"in window&&new ResizeObserver(n).observe(document.documentElement),Jt(window,"scroll",function(t){i||(i=!0,gn.write(function(){return i=!1}),e.update(null,t.type))},{passive:!0,capture:!0}),r=0,Jt(document,"animationstart",function(t){t=t.target;(Re(t,"animationName")||"").match(/^uk-.*(left|right)/)&&(r++,Re(document.documentElement,"overflowX","hidden"),setTimeout(function(){--r||Re(document.documentElement,"overflowX","")},R(Re(t,"animationDuration"))+100))},!0),Jt(document,mt,function(t){var s,a;ae(t)&&(s=ue(t),a="tagName"in t.target?t.target:Tt(t.target),Qt(document,vt+" "+xt+" scroll",function(t){var e=ue(t),r=e.x,o=e.y;("scroll"!==t.type&&a&&r&&100=Math.abs(e-i)?0
    "}).join("")),e.forEach(function(t,e){return n.children[e].textContent=t}))})}},methods:{start:function(){this.stop(),this.date&&this.units.length&&(this.$update(),this.timer=setInterval(this.$update,1e3))},stop:function(){this.timer&&(clearInterval(this.timer),this.timer=null)}}};var Er="uk-transition-leave",Tr="uk-transition-enter";function Cr(t,s,a,u){void 0===u&&(u=0);var c=_r(s,!0),h={opacity:1},l={opacity:0},e=function(t){return function(){return c===_r(s)?t():he.reject()}},n=e(function(){return Be(s,Er),he.all(Mr(s).map(function(e,n){return new he(function(t){return setTimeout(function(){return Ze.start(e,l,a/2,"ease").then(t)},n*u)})})).then(function(){return De(s,Er)})}),e=e(function(){var o=cn(s);return Be(s,Tr),t(),Re(Ot(s),{opacity:0}),new he(function(r){return requestAnimationFrame(function(){var t=Ot(s),e=cn(s);Re(s,"alignContent","flex-start"),cn(s,o);var n=Mr(s);Re(t,l);var i=n.map(function(e,n){return new he(function(t){return setTimeout(function(){return Ze.start(e,h,a/2,"ease").then(t)},n*u)})});o!==e&&i.push(Ze.start(s,{height:e},a/2+n.length*u,"ease")),he.all(i).then(function(){De(s,Tr),c===_r(s)&&(Re(s,{height:"",alignContent:""}),Re(t,{opacity:""}),delete s.dataset.transition),r()})})})});return(He(s,Er)?Ar(s):He(s,Tr)?Ar(s).then(n):n()).then(e)}function _r(t,e){return e&&(t.dataset.transition=1+_r(t)),H(t.dataset.transition)||0}function Ar(t){return he.all(Ot(t).filter(Ze.inProgress).map(function(e){return new he(function(t){return Qt(e,"transitionend transitioncanceled",t)})}))}function Mr(t){return Ci(Ot(t)).reduce(function(t,e){return t.concat(K(e.filter(function(t){return Ln(t)}),"offsetLeft"))},[])}function zr(t,d,f){return new he(function(l){return requestAnimationFrame(function(){var u=Ot(d),c=u.map(function(t){return Nr(t,!0)}),h=Re(d,["height","padding"]);Ze.cancel(d),u.forEach(Ze.cancel),Br(d),t(),u=u.concat(Ot(d).filter(function(t){return!w(u,t)})),he.resolve().then(function(){gn.flush();var n,i,r,t,e,o=Re(d,["height","padding"]),e=(n=d,r=c,t=(i=u).map(function(t,e){return!!(Tt(t)&&e in r)&&(r[e]?$t(t)?Dr(t):{opacity:0}:{opacity:$t(t)?1:0})}),e=t.map(function(t,e){e=Tt(i[e])===n&&(r[e]||Nr(i[e]));return!!e&&(t?"opacity"in t||(e.opacity%1?t.opacity=1:delete e.opacity):delete e.opacity,e)}),[t,e]),s=e[0],a=e[1];u.forEach(function(t,e){return a[e]&&Re(t,a[e])}),Re(d,Y({display:"block"},h)),requestAnimationFrame(function(){var t=u.map(function(t,e){return Tt(t)===d&&Ze.start(t,s[e],f,"ease")}).concat(Ze.start(d,o,f,"ease"));he.all(t).then(function(){u.forEach(function(t,e){return Tt(t)===d&&Re(t,"display",0===s[e].opacity?"none":"")}),Br(d)},Q).then(l)})})})})}function Nr(t,e){var n=Re(t,"zIndex");return!!$t(t)&&Y({display:"",opacity:e?Re(t,"opacity"):"0",pointerEvents:"none",position:"absolute",zIndex:"auto"===n?Pt(t):n},Dr(t))}function Br(t){Re(t.children,{height:"",left:"",opacity:"",pointerEvents:"",position:"",top:"",marginTop:"",marginLeft:"",transform:"",width:"",zIndex:""}),Re(t,{height:"",display:"",padding:""})}function Dr(t){var e=sn(t),n=e.height,i=e.width,r=an(t),e=r.top,r=r.left,t=Re(t,["marginTop","marginLeft"]);return{top:e,left:r,height:n,width:i,marginLeft:t.marginLeft,marginTop:t.marginTop,transform:""}}hr={props:{duration:Number,animation:Boolean},data:{duration:150,animation:"slide"},methods:{animate:function(t,e){var n=this;void 0===e&&(e=this.$el);var i=this.animation;return("fade"===i?Cr:"delayed-fade"===i?function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];return Cr.apply(void 0,t.concat([40]))}:i?zr:function(){return t(),he.resolve()})(t,e,this.duration).then(function(){return n.$update(e,"resize")},Q)}}},Ni={mixins:[hr],args:"target",props:{target:Boolean,selActive:Boolean},data:{target:null,selActive:!1,attrItem:"uk-filter-control",cls:"uk-active",duration:250},computed:{toggles:{get:function(t,e){t=t.attrItem;return ze("["+t+"],[data-"+t+"]",e)},watch:function(){var e,n=this;this.updateState(),!1!==this.selActive&&(e=ze(this.selActive,this.$el),this.toggles.forEach(function(t){return Le(t,n.cls,w(e,t))}))},immediate:!0},children:{get:function(t,e){return ze(t.target+" > *",e)},watch:function(t,e){var n;e&&(n=e,(t=t).length!==n.length||!t.every(function(t){return~n.indexOf(t)}))&&this.updateState()},immediate:!0}},events:[{name:"click",delegate:function(){return"["+this.attrItem+"],[data-"+this.attrItem+"]"},handler:function(t){t.preventDefault(),this.apply(t.current)}}],methods:{apply:function(t){var e,n,i=this.getState(),t=Pr(t,this.attrItem,this.getState());e=i,n=t,["filter","sort"].every(function(t){return q(e[t],n[t])})||this.setState(t)},getState:function(){var n=this;return this.toggles.filter(function(t){return He(t,n.cls)}).reduce(function(t,e){return Pr(e,n.attrItem,t)},{filter:{"":""},sort:[]})},setState:function(n,i){var r=this;void 0===i&&(i=!0),n=Y({filter:{"":""},sort:[]},n),te(this.$el,"beforeFilter",[this,n]),this.toggles.forEach(function(t){return Le(t,r.cls,!!function(t,e,n){var i=n.filter;void 0===i&&(i={"":""});var r=n.sort,o=r[0],s=r[1],n=Or(t,e),r=n.filter;void 0===r&&(r="");t=n.group;void 0===t&&(t="");e=n.sort,n=n.order;void 0===n&&(n="asc");return O(e)?t in i&&r===i[t]||!r&&t&&!(t in i)&&!i[""]:o===e&&s===n}(t,r.attrItem,n))}),he.all(ze(this.target,this.$el).map(function(t){function e(){!function(t,e,n){var i=function(t){var t=t.filter,e="";return G(t,function(t){return e+=t||""}),e}(t);n.forEach(function(t){return Re(t,"display",i&&!Mt(t,i)?"none":"")});var r=t.sort,t=r[0],r=r[1];t&&(q(r=function(t,n,i){return Y([],t).sort(function(t,e){return ut(t,n).localeCompare(ut(e,n),void 0,{numeric:!0})*("asc"===i||-1)})}(n,t,r),n)||be(e,r))}(n,t,Ot(t)),r.$update(r.$el)}return i?r.animate(e,t):e()})).then(function(){return te(r.$el,"afterFilter",[r])})},updateState:function(){var t=this;gn.write(function(){return t.setState(t.getState(),!1)})}}};function Or(t,e){return Tn(ut(t,e),["filter"])}function Pr(t,e,n){var i=Or(t,e),r=i.filter,t=i.group,e=i.sort,i=i.order;return void 0===i&&(i="asc"),(r||O(e))&&(t?r?(delete n.filter[""],n.filter[t]=r):(delete n.filter[t],(D(n.filter)||""in n.filter)&&(n.filter={"":r||""})):n.filter={"":r||""}),O(e)||(n.sort=[e,i]),n}xi={slide:{show:function(t){return[{transform:Lr(-100*t)},{transform:Lr()}]},percent:Hr,translate:function(t,e){return[{transform:Lr(-100*e*t)},{transform:Lr(100*e*(1-t))}]}}};function Hr(t){return Math.abs(Re(t,"transform").split(",")[4]/t.offsetWidth)||0}function Lr(t,e){return void 0===t&&(t=0),void 0===e&&(e="%"),t+=t?e:"",ht?"translateX("+t+")":"translate3d("+t+", 0, 0)"}function jr(t){return"scale3d("+t+", "+t+", 1)"}var Fr=Y({},xi,{fade:{show:function(){return[{opacity:0},{opacity:1}]},percent:function(t){return 1-Re(t,"opacity")},translate:function(t){return[{opacity:1-t},{opacity:t}]}},scale:{show:function(){return[{opacity:0,transform:jr(.8)},{opacity:1,transform:jr(1)}]},percent:function(t){return 1-Re(t,"opacity")},translate:function(t){return[{opacity:1-t,transform:jr(1-.2*t)},{opacity:t,transform:jr(.8+.2*t)}]}}});function Wr(t,e,n){te(t,ee(e,!1,!1,n))}Ji={mixins:[{props:{autoplay:Boolean,autoplayInterval:Number,pauseOnHover:Boolean},data:{autoplay:!1,autoplayInterval:7e3,pauseOnHover:!0},connected:function(){this.autoplay&&this.startAutoplay()},disconnected:function(){this.stopAutoplay()},update:function(){ot(this.slides,"tabindex","-1")},events:[{name:"visibilitychange",el:function(){return document},filter:function(){return this.autoplay},handler:function(){document.hidden?this.stopAutoplay():this.startAutoplay()}}],methods:{startAutoplay:function(){var t=this;this.stopAutoplay(),this.interval=setInterval(function(){return(!t.draggable||!Me(":focus",t.$el))&&(!t.pauseOnHover||!Mt(t.$el,":hover"))&&!t.stack.length&&t.show("next")},this.autoplayInterval)},stopAutoplay:function(){this.interval&&clearInterval(this.interval)}}},{props:{draggable:Boolean},data:{draggable:!0,threshold:10},created:function(){var i=this;["start","move","end"].forEach(function(t){var n=i[t];i[t]=function(t){var e=ue(t).x*(lt?-1:1);i.prevPos=e!==i.pos?i.pos:i.prevPos,i.pos=e,n(t)}})},events:[{name:mt,delegate:function(){return this.selSlides},handler:function(t){var e;!this.draggable||!ae(t)&&(!(e=t.target).children.length&&e.childNodes.length)||Nt(t.target,St)||0this.pos,this.index=t?this.index:this.prevIndex,t&&(this.percent=1-this.percent),this.show(0
    '}).join("")),this.navItems.concat(this.nav).forEach(function(t){return t&&(t.hidden=!n.maxIndex)}),this.updateNav()},events:["resize"]},events:[{name:"click",delegate:function(){return this.selNavItem},handler:function(t){t.preventDefault(),this.show(ut(t.current,this.attrItem))}},{name:"itemshow",handler:"updateNav"}],methods:{updateNav:function(){var n=this,i=this.getValidIndex();this.navItems.forEach(function(t){var e=ut(t,n.attrItem);Le(t,n.clsActive,H(e)===i),Le(t,"uk-invisible",n.finite&&("previous"===e&&0===i||"next"===e&&i>=n.maxIndex))})}}}],props:{clsActivated:Boolean,easing:String,index:Number,finite:Boolean,velocity:Number,selSlides:String},data:function(){return{easing:"ease",finite:!1,velocity:1,index:0,prevIndex:-1,stack:[],percent:0,clsActive:"uk-active",clsActivated:!1,Transitioner:!1,transitionOptions:{}}},connected:function(){this.prevIndex=-1,this.index=this.getValidIndex(this.$props.index),this.stack=[]},disconnected:function(){De(this.slides,this.clsActive)},computed:{duration:function(t,e){t=t.velocity;return Vr(e.offsetWidth/t)},list:function(t,e){return Me(t.selList,e)},maxIndex:function(){return this.length-1},selSlides:function(t){return t.selList+" "+(t.selSlides||"> *")},slides:{get:function(){return ze(this.selSlides,this.$el)},watch:function(){this.$reset()}},length:function(){return this.slides.length}},events:{itemshown:function(){this.$update(this.list)}},methods:{show:function(t,e){var n=this;if(void 0===e&&(e=!1),!this.dragging&&this.length){var i=this.stack,r=e?0:i.length,o=function(){i.splice(r,1),i.length&&n.show(i.shift(),!0)};if(i[e?"unshift":"push"](t),!e&&1
      '}},created:function(){var t=Me(this.template),e=Me(this.selList,t);this.items.forEach(function(){return be(e,"
    • ")}),this.$mount(be(this.container,t))},computed:{caption:function(t,e){return Me(t.selCaption,e)}},events:[{name:gt+" "+mt+" keydown",handler:"showControls"},{name:"click",self:!0,delegate:function(){return this.selSlides},handler:function(t){t.defaultPrevented||this.hide()}},{name:"shown",self:!0,handler:function(){this.showControls()}},{name:"hide",self:!0,handler:function(){this.hideControls(),De(this.slides,this.clsActive),Ze.stop(this.slides)}},{name:"hidden",self:!0,handler:function(){this.$destroy(!0)}},{name:"keyup",el:function(){return document},handler:function(t){if(this.isToggled(this.$el)&&this.draggable)switch(t.keyCode){case 37:this.show("previous");break;case 39:this.show("next")}}},{name:"beforeitemshow",handler:function(t){this.isToggled()||(this.draggable=!1,t.preventDefault(),this.toggleElement(this.$el,!0,!1),this.animation=Fr.scale,De(t.target,this.clsActive),this.stack.splice(1,0,this.index))}},{name:"itemshow",handler:function(){we(this.caption,this.getItem().caption||"");for(var t=-this.preload;t<=this.preload;t++)this.loadItem(this.index+t)}},{name:"itemshown",handler:function(){this.draggable=this.$props.draggable}},{name:"itemload",handler:function(t,n){var i=this,r=n.source,e=n.type,o=n.alt;void 0===o&&(o="");var s,a,u,c=n.poster,h=n.attrs;void 0===h&&(h={}),this.setItem(n,""),r&&(a={frameborder:"0",allow:"autoplay",allowfullscreen:"",style:"max-width: 100%; box-sizing: border-box;","uk-responsive":"","uk-video":""+this.videoAutoplay},"image"===e||r.match(/\.(avif|jpe?g|a?png|gif|svg|webp)($|\?)/i)?ge(r,h.srcset,h.size).then(function(t){var e=t.width,t=t.height;return i.setItem(n,qr("img",Y({src:r,width:e,height:t,alt:o},h)))},function(){return i.setError(n)}):"video"===e||r.match(/\.(mp4|webm|ogv)($|\?)/i)?(Jt(u=qr("video",Y({src:r,poster:c,controls:"",playsinline:"","uk-video":""+this.videoAutoplay},h)),"loadedmetadata",function(){ot(u,{width:u.videoWidth,height:u.videoHeight}),i.setItem(n,u)}),Jt(u,"error",function(){return i.setError(n)})):"iframe"===e||r.match(/\.(html|php)($|\?)/i)?this.setItem(n,qr("iframe",Y({src:r,frameborder:"0",allowfullscreen:"",class:"uk-lightbox-iframe"},h))):(s=r.match(/\/\/(?:.*?youtube(-nocookie)?\..*?[?&]v=|youtu\.be\/)([\w-]{11})[&?]?(.*)?/))?this.setItem(n,qr("iframe",Y({src:"https://www.youtube"+(s[1]||"")+".com/embed/"+s[2]+(s[3]?"?"+s[3]:""),width:1920,height:1080},a,h))):(s=r.match(/\/\/.*?vimeo\.[a-z]+\/(\d+)[&?]?(.*)?/))&&me("https://vimeo.com/api/oembed.json?maxwidth=1920&url="+encodeURI(r),{responseType:"json",withCredentials:!1}).then(function(t){var e=t.response,t=e.height,e=e.width;return i.setItem(n,qr("iframe",Y({src:"https://player.vimeo.com/video/"+s[1]+(s[2]?"?"+s[2]:""),width:e,height:t},a,h)))},function(){return i.setError(n)}))}}],methods:{loadItem:function(t){void 0===t&&(t=this.index);t=this.getItem(t);this.getSlide(t).childElementCount||te(this.$el,"itemload",[t])},getItem:function(t){return void 0===t&&(t=this.index),this.items[it(t,this.slides)]},setItem:function(t,e){te(this.$el,"itemloaded",[this,we(this.getSlide(t),e)])},getSlide:function(t){return this.slides[this.items.indexOf(t)]},setError:function(t){this.setItem(t,'')},showControls:function(){clearTimeout(this.controlsTimer),this.controlsTimer=setTimeout(this.hideControls,this.delayControls),Be(this.$el,"uk-active","uk-transition-active")},hideControls:function(){De(this.$el,"uk-active","uk-transition-active")}}};function qr(t,e){t=_e("<"+t+">");return ot(t,e),t}Gi={install:function(t,e){t.lightboxPanel||t.component("lightboxPanel",Rr);Y(e.props,t.component("lightboxPanel").options.props)},props:{toggle:String},data:{toggle:"a"},computed:{toggles:{get:function(t,e){return ze(t.toggle,e)},watch:function(){this.hide()}}},disconnected:function(){this.hide()},events:[{name:"click",delegate:function(){return this.toggle+":not(.uk-disabled)"},handler:function(t){t.preventDefault(),this.show(t.current)}}],methods:{show:function(t){var e,n=this,i=J(this.toggles.map(Ur),"source");return _(t)&&(e=Ur(t).source,t=x(i,function(t){t=t.source;return e===t})),this.panel=this.panel||this.$create("lightboxPanel",Y({},this.$props,{items:i})),Jt(this.panel.$el,"hidden",function(){return n.panel=!1}),this.panel.show(t)},hide:function(){return this.panel&&this.panel.hide()}}};function Ur(e){var n={};return["href","caption","type","poster","alt","attrs"].forEach(function(t){n["href"===t?"source":t]=ut(e,t)}),n.attrs=Tn(n.attrs),n}Xi={mixins:[fr],functional:!0,args:["message","status"],data:{message:"",status:"",timeout:5e3,group:null,pos:"top-center",clsContainer:"uk-notification",clsClose:"uk-notification-close",clsMsg:"uk-notification-message"},install:function(i){i.notification.closeAll=function(e,n){Ae(document.body,function(t){t=i.getComponent(t,"notification");!t||e&&e!==t.group||t.close(n)})}},computed:{marginProp:function(t){return"margin"+(g(t.pos,"top")?"Top":"Bottom")},startProps:function(){var t={opacity:0};return t[this.marginProp]=-this.$el.offsetHeight,t}},created:function(){var t=Me("."+this.clsContainer+"-"+this.pos,this.container)||be(this.container,'
      ');this.$mount(be(t,'
      '+this.message+"
      "))},connected:function(){var t,e=this,n=L(Re(this.$el,this.marginProp));Ze.start(Re(this.$el,this.startProps),((t={opacity:1})[this.marginProp]=n,t)).then(function(){e.timeout&&(e.timer=setTimeout(e.close,e.timeout))})},events:((Ki={click:function(t){Nt(t.target,'a[href="#"],a[href=""]')&&t.preventDefault(),this.close()}})[wt]=function(){this.timer&&clearTimeout(this.timer)},Ki[bt]=function(){this.timeout&&(this.timer=setTimeout(this.close,this.timeout))},Ki),methods:{close:function(t){function e(t){var e=Tt(t);te(t,"close",[n]),$e(t),e&&!e.hasChildNodes()&&$e(e)}var n=this;this.timer&&clearTimeout(this.timer),t?e(this.$el):Ze.start(this.$el,this.startProps).then(e)}}};var Yr=["x","y","bgx","bgy","rotate","scale","color","backgroundColor","borderColor","opacity","blur","hue","grayscale","invert","saturate","sepia","fopacity","stroke"],mr={mixins:[lr],props:Yr.reduce(function(t,e){return t[e]="list",t},{}),data:Yr.reduce(function(t,e){return t[e]=void 0,t},{}),computed:{props:function(f,p){var m=this;return Yr.reduce(function(t,e){if(O(f[e]))return t;var n,i,r=e.match(/color/i),o=r||"opacity"===e,s=f[e].slice();o&&Re(p,e,""),s.length<2&&s.unshift(("scale"===e?1:o?Re(p,e):0)||0);var a,u,c,h,l,o=s.reduce(function(t,e){return z(e)&&e.replace(/-|\d/g,"").trim()||t},"");if(r?(r=p.style.color,s=s.map(function(t){return Re(Re(p,"color",t),"color").split(/[(),]/g).slice(1,-1).concat(1).slice(0,4).map(L)}),p.style.color=r):g(e,"bg")?(a="bgy"===e?"height":"width",s=s.map(function(t){return pn(t,a,m.$el)}),Re(p,"background-position-"+e[2],""),i=Re(p,"backgroundPosition").split(" ")["x"===e[2]?0:1],n=m.covers?(u=Math.min.apply(Math,s),c=Math.max.apply(Math,s),h=s.indexOf(u)Zr(c||h))?"in":"out"),{dir:l,percent:r?1-o:i?o:n?1:0})})},percent:function(){return Math.abs((Re(d,"transform").split(",")[4]*(lt?-1:1)+e)/(f-e))},getDistance:function(){return Math.abs(f-e)},getItemIn:function(t){void 0===t&&(t=!1);var e=this.getActives(),n=Qr(d,Gr(h||c,d,i));return t&&(t=e,e=n,n=t),n[x(n,function(t){return!w(e,t)})]},getActives:function(){return Qr(d,Gr(c||h,d,i))}}}},computed:{avgWidth:function(){return Jr(this.list)/this.length},finite:function(t){return t.finite||Math.ceil(Jr(this.list))r.maxIndex?r.maxIndex:n)||(e=r.slides[n+1],r.center&&e&&in.maxIndex||n.sets&&!w(n.sets,e))}),!this.length||this.dragging||this.stack.length||(this.reorder(),this._translate(1));var e=this._getTransitioner(this.index).getActives();this.slides.forEach(function(t){return Le(t,n.clsActive,w(e,t))}),!this.clsActivated||this.sets&&!w(this.sets,L(this.index))||this.slides.forEach(function(t){return Le(t,n.clsActivated||"",w(e,t))})},events:["resize"]},events:{beforeitemshow:function(t){!this.dragging&&this.sets&&this.stack.length<2&&!w(this.sets,this.index)&&(this.index=this.getValidIndex());var e=Math.abs(this.index-this.prevIndex+(0this.prevIndex?(this.maxIndex+1)*this.dir:0));if(!this.dragging&&1=n.index?-1:"")}),this.center)for(var t=this.slides[i],e=on(this.list).width/2-on(t).width/2,r=0;0s[t]-i)&&e}}(i.target,r,n,e,a,i===s&&t.moved!==r))&&(a&&n===a||(i!==s?(s.remove(n),t.moved=r):delete t.moved,i.insert(n,a),this.touched.add(i)))))))},events:["move"]},methods:{init:function(t){var e=t.target,n=t.button,i=t.defaultPrevented,r=this.items.filter(function(t){return Bt(e,t)})[0];!r||i||0$)/g,"$1div$2"));return Re(t,"margin","0","important"),Re(t,Y({boxSizing:"border-box",width:e.offsetWidth,height:e.offsetHeight},Re(e,["paddingLeft","paddingRight","paddingTop","paddingBottom"]))),cn(t.firstElementChild,cn(e.firstElementChild)),t}(this.$container,this.placeholder);var e,n,i=this.placeholder.getBoundingClientRect(),r=i.left,i=i.top;Y(this.origin,{offsetLeft:this.pos.x-r,offsetTop:this.pos.y-i}),Be(this.drag,this.clsDrag,this.clsCustom),Be(this.placeholder,this.clsPlaceholder),Be(this.items,this.clsItem),Be(document.documentElement,this.clsDragState),te(this.$el,"start",[this,this.placeholder]),e=this.pos,n=Date.now(),io=setInterval(function(){var t=e.x,s=e.y;s+=window.pageYOffset;var a=.3*(Date.now()-n);n=Date.now(),Vn(document.elementFromPoint(t,e.y)).reverse().some(function(t){var e=t.scrollTop,n=t.scrollHeight,i=sn(Rn(t)),r=i.top,o=i.bottom,i=i.height;if(rthis.threshold||Math.abs(this.pos.y-this.origin.y)>this.threshold)&&this.start(t)},end:function(){var t,i=this;Zt(document,gt,this.move),Zt(document,vt,this.end),Zt(window,"scroll",this.scroll),this.drag&&(clearInterval(io),t=this.getSortable(this.placeholder),this===t?this.origin.index!==Pt(this.placeholder)&&te(this.$el,"moved",[this,this.placeholder]):(te(t.$el,"added",[t,this.placeholder]),te(this.$el,"removed",[this,this.placeholder])),te(this.$el,"stop",[this,this.placeholder]),$e(this.drag),this.drag=null,this.touched.forEach(function(t){var e=t.clsPlaceholder,n=t.clsItem;return i.touched.forEach(function(t){return De(t.items,e,n)})}),this.touched=null,De(document.documentElement,this.clsDragState))},insert:function(t,e){var n=this;Be(this.items,this.clsItem);this.animate(function(){return e?xe(e,t):be(n.target,t)})},remove:function(t){Bt(t,this.target)&&this.animate(function(){return $e(t)})},getSortable:function(t){do{var e=this.$getComponent(t,"sortable");if(e&&(e===this||!1!==this.group&&e.group===this.group))return e}while(t=Tt(t))}}};function ro(t,e){return t[1]>e[0]&&e[1]>t[0]}bt={mixins:[fr,mi,$i],args:"title",props:{delay:Number,title:String},data:{pos:"top",title:"",delay:0,animation:["uk-animation-scale-up"],duration:100,cls:"uk-active",clsPos:"uk-tooltip"},beforeConnect:function(){var t;this._hasTitle=st(this.$el,"title"),ot(this.$el,"title",""),this.updateAria(!1),Et(t=this.$el)||ot(t,"tabindex","0")},disconnected:function(){this.hide(),ot(this.$el,"title",this._hasTitle?this.title:null)},methods:{show:function(){var e=this;!this.isToggled(this.tooltip||null)&&this.title&&(this._unbind=Qt(document,"show keydown "+mt,this.hide,!1,function(t){return t.type===mt&&!Bt(t.target,e.$el)||"keydown"===t.type&&27===t.keyCode||"show"===t.type&&t.detail[0]!==e&&t.detail[0].$name===e.$name}),clearTimeout(this.showTimer),this.showTimer=setTimeout(this._show,this.delay))},hide:function(){var t=this;Mt(this.$el,"input:focus")||(clearTimeout(this.showTimer),this.isToggled(this.tooltip||null)&&this.toggleElement(this.tooltip,!1,!1).then(function(){t.tooltip=$e(t.tooltip),t._unbind()}))},_show:function(){var n=this;this.tooltip=be(this.container,'
      '+this.title+"
      "),Jt(this.tooltip,"toggled",function(t,e){n.updateAria(e),e&&(n.positionAt(n.tooltip,n.$el),n.origin="y"===n.getAxis()?fn(n.dir)+"-"+n.align:n.align+"-"+fn(n.dir))}),this.toggleElement(this.tooltip,!0)},updateAria:function(t){ot(this.$el,"aria-expanded",t)}},events:(($i={focus:"show",blur:"hide"})[wt+" "+bt]=function(t){ae(t)||this[t.type===wt?"show":"hide"]()},$i[mt]=function(t){ae(t)&&this.show()},$i)};$i={props:{allow:String,clsDragover:String,concurrent:Number,maxSize:Number,method:String,mime:String,msgInvalidMime:String,msgInvalidName:String,msgInvalidSize:String,multiple:Boolean,name:String,params:Object,type:String,url:String},data:{allow:!1,clsDragover:"uk-dragover",concurrent:1,maxSize:0,method:"POST",mime:!1,msgInvalidMime:"Invalid File Type: %s",msgInvalidName:"Invalid File Name: %s",msgInvalidSize:"Invalid File Size: %s Kilobytes Max",multiple:!1,name:"files[]",params:{},type:"",url:"",abort:Q,beforeAll:Q,beforeSend:Q,complete:Q,completeAll:Q,error:Q,fail:Q,load:Q,loadEnd:Q,loadStart:Q,progress:Q},events:{change:function(t){Mt(t.target,'input[type="file"]')&&(t.preventDefault(),t.target.files&&this.upload(t.target.files),t.target.value="")},drop:function(t){so(t);t=t.dataTransfer;t&&t.files&&(De(this.$el,this.clsDragover),this.upload(t.files))},dragenter:function(t){so(t)},dragover:function(t){so(t),Be(this.$el,this.clsDragover)},dragleave:function(t){so(t),De(this.$el,this.clsDragover)}},methods:{upload:function(t){var i=this;if(t.length){te(this.$el,"upload",[t]);for(var e=0;e + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Filesystem\File; +use Joomla\CMS\Filesystem\Folder; +use Joomla\CMS\Installer\Adapter\ComponentAdapter; +JHTML::_('bootstrap.renderModal'); + +/** + * Script File of Extensiondistributor Component + */ +class com_extensiondistributorInstallerScript +{ + /** + * Constructor + * + * @param JAdapterInstance $parent The object responsible for running this script + */ + public function __construct(ComponentAdapter $parent) {} + + /** + * Called on installation + * + * @param ComponentAdapter $parent The object responsible for running this script + * + * @return boolean True on success + */ + public function install(ComponentAdapter $parent) {} + + /** + * Called on uninstallation + * + * @param ComponentAdapter $parent The object responsible for running this script + */ + public function uninstall(ComponentAdapter $parent) + { + // Get Application object + $app = JFactory::getApplication(); + + // Get The Database object + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + // Select ids from fields + $query->select($db->quoteName('id')); + $query->from($db->quoteName('#__fields')); + // Where package context is found + $query->where( $db->quoteName('context') . ' = '. $db->quote('com_extensiondistributor.package') ); + $db->setQuery($query); + // Execute query to see if context is found + $db->execute(); + $package_found = $db->getNumRows(); + // Now check if there were any rows + if ($package_found) + { + // Since there are load the needed package field ids + $package_field_ids = $db->loadColumn(); + // Remove package from the field table + $package_condition = array( $db->quoteName('context') . ' = '. $db->quote('com_extensiondistributor.package') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__fields')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove package items + $package_done = $db->execute(); + if ($package_done) + { + // If successfully remove package add queued success message. + $app->enqueueMessage(JText::_('The fields with type (com_extensiondistributor.package) context was removed from the #__fields table')); + } + // Also Remove package field values + $package_condition = array( $db->quoteName('field_id') . ' IN ('. implode(',', $package_field_ids) .')'); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__fields_values')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove package field values + $package_done = $db->execute(); + if ($package_done) + { + // If successfully remove package add queued success message. + $app->enqueueMessage(JText::_('The fields values for package was removed from the #__fields_values table')); + } + } + + // Create a new query object. + $query = $db->getQuery(true); + // Select ids from field groups + $query->select($db->quoteName('id')); + $query->from($db->quoteName('#__fields_groups')); + // Where package context is found + $query->where( $db->quoteName('context') . ' = '. $db->quote('com_extensiondistributor.package') ); + $db->setQuery($query); + // Execute query to see if context is found + $db->execute(); + $package_found = $db->getNumRows(); + // Now check if there were any rows + if ($package_found) + { + // Remove package from the field groups table + $package_condition = array( $db->quoteName('context') . ' = '. $db->quote('com_extensiondistributor.package') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__fields_groups')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove package items + $package_done = $db->execute(); + if ($package_done) + { + // If successfully remove package add queued success message. + $app->enqueueMessage(JText::_('The field groups with type (com_extensiondistributor.package) context was removed from the #__fields_groups table')); + } + } + + // Create a new query object. + $query = $db->getQuery(true); + // Select id from content type table + $query->select($db->quoteName('type_id')); + $query->from($db->quoteName('#__content_types')); + // Where package alias is found + $query->where( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.package') ); + $db->setQuery($query); + // Execute query to see if alias is found + $db->execute(); + $package_found = $db->getNumRows(); + // Now check if there were any rows + if ($package_found) + { + // Since there are load the needed package type ids + $package_ids = $db->loadColumn(); + // Remove package from the content type table + $package_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.package') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__content_types')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove package items + $package_done = $db->execute(); + if ($package_done) + { + // If successfully remove package add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.package) type alias was removed from the #__content_type table')); + } + + // Remove package items from the contentitem tag map table + $package_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.package') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__contentitem_tag_map')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove package items + $package_done = $db->execute(); + if ($package_done) + { + // If successfully remove package add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.package) type alias was removed from the #__contentitem_tag_map table')); + } + + // Remove package items from the ucm content table + $package_condition = array( $db->quoteName('core_type_alias') . ' = ' . $db->quote('com_extensiondistributor.package') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_content')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove package items + $package_done = $db->execute(); + if ($package_done) + { + // If successfully removed package add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.package) type alias was removed from the #__ucm_content table')); + } + + // Make sure that all the package items are cleared from DB + foreach ($package_ids as $package_id) + { + // Remove package items from the ucm base table + $package_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $package_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_base')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove package items + $db->execute(); + + // Remove package items from the ucm history table + $package_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $package_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_history')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove package items + $db->execute(); + } + } + + // Create a new query object. + $query = $db->getQuery(true); + // Select id from content type table + $query->select($db->quoteName('type_id')); + $query->from($db->quoteName('#__content_types')); + // Where Package alias is found + $query->where( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.package') ); + $db->setQuery($query); + // Execute query to see if alias is found + $db->execute(); + $package_found = $db->getNumRows(); + // Now check if there were any rows + if ($package_found) + { + // Since there are load the needed package type ids + $package_ids = $db->loadColumn(); + // Remove Package from the content type table + $package_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.package') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__content_types')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove Package items + $package_done = $db->execute(); + if ($package_done) + { + // If successfully remove Package add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.package) type alias was removed from the #__content_type table')); + } + + // Remove Package items from the contentitem tag map table + $package_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.package') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__contentitem_tag_map')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove Package items + $package_done = $db->execute(); + if ($package_done) + { + // If successfully remove Package add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.package) type alias was removed from the #__contentitem_tag_map table')); + } + + // Remove Package items from the ucm content table + $package_condition = array( $db->quoteName('core_type_alias') . ' = ' . $db->quote('com_extensiondistributor.package') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_content')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove Package items + $package_done = $db->execute(); + if ($package_done) + { + // If successfully removed Package add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.package) type alias was removed from the #__ucm_content table')); + } + + // Make sure that all the Package items are cleared from DB + foreach ($package_ids as $package_id) + { + // Remove Package items from the ucm base table + $package_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $package_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_base')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove Package items + $db->execute(); + + // Remove Package items from the ucm history table + $package_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $package_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_history')); + $query->where($package_condition); + $db->setQuery($query); + // Execute the query to remove Package items + $db->execute(); + } + } + + // Create a new query object. + $query = $db->getQuery(true); + // Select id from content type table + $query->select($db->quoteName('type_id')); + $query->from($db->quoteName('#__content_types')); + // Where Extension alias is found + $query->where( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.extension') ); + $db->setQuery($query); + // Execute query to see if alias is found + $db->execute(); + $extension_found = $db->getNumRows(); + // Now check if there were any rows + if ($extension_found) + { + // Since there are load the needed extension type ids + $extension_ids = $db->loadColumn(); + // Remove Extension from the content type table + $extension_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.extension') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__content_types')); + $query->where($extension_condition); + $db->setQuery($query); + // Execute the query to remove Extension items + $extension_done = $db->execute(); + if ($extension_done) + { + // If successfully remove Extension add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.extension) type alias was removed from the #__content_type table')); + } + + // Remove Extension items from the contentitem tag map table + $extension_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.extension') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__contentitem_tag_map')); + $query->where($extension_condition); + $db->setQuery($query); + // Execute the query to remove Extension items + $extension_done = $db->execute(); + if ($extension_done) + { + // If successfully remove Extension add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.extension) type alias was removed from the #__contentitem_tag_map table')); + } + + // Remove Extension items from the ucm content table + $extension_condition = array( $db->quoteName('core_type_alias') . ' = ' . $db->quote('com_extensiondistributor.extension') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_content')); + $query->where($extension_condition); + $db->setQuery($query); + // Execute the query to remove Extension items + $extension_done = $db->execute(); + if ($extension_done) + { + // If successfully removed Extension add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.extension) type alias was removed from the #__ucm_content table')); + } + + // Make sure that all the Extension items are cleared from DB + foreach ($extension_ids as $extension_id) + { + // Remove Extension items from the ucm base table + $extension_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $extension_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_base')); + $query->where($extension_condition); + $db->setQuery($query); + // Execute the query to remove Extension items + $db->execute(); + + // Remove Extension items from the ucm history table + $extension_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $extension_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_history')); + $query->where($extension_condition); + $db->setQuery($query); + // Execute the query to remove Extension items + $db->execute(); + } + } + + // Create a new query object. + $query = $db->getQuery(true); + // Select id from content type table + $query->select($db->quoteName('type_id')); + $query->from($db->quoteName('#__content_types')); + // Where Extension catid alias is found + $query->where( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.extension.category') ); + $db->setQuery($query); + // Execute query to see if alias is found + $db->execute(); + $extension_catid_found = $db->getNumRows(); + // Now check if there were any rows + if ($extension_catid_found) + { + // Since there are load the needed extension_catid type ids + $extension_catid_ids = $db->loadColumn(); + // Remove Extension catid from the content type table + $extension_catid_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.extension.category') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__content_types')); + $query->where($extension_catid_condition); + $db->setQuery($query); + // Execute the query to remove Extension catid items + $extension_catid_done = $db->execute(); + if ($extension_catid_done) + { + // If successfully remove Extension catid add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.extension.category) type alias was removed from the #__content_type table')); + } + + // Remove Extension catid items from the contentitem tag map table + $extension_catid_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.extension.category') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__contentitem_tag_map')); + $query->where($extension_catid_condition); + $db->setQuery($query); + // Execute the query to remove Extension catid items + $extension_catid_done = $db->execute(); + if ($extension_catid_done) + { + // If successfully remove Extension catid add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.extension.category) type alias was removed from the #__contentitem_tag_map table')); + } + + // Remove Extension catid items from the ucm content table + $extension_catid_condition = array( $db->quoteName('core_type_alias') . ' = ' . $db->quote('com_extensiondistributor.extension.category') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_content')); + $query->where($extension_catid_condition); + $db->setQuery($query); + // Execute the query to remove Extension catid items + $extension_catid_done = $db->execute(); + if ($extension_catid_done) + { + // If successfully removed Extension catid add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.extension.category) type alias was removed from the #__ucm_content table')); + } + + // Make sure that all the Extension catid items are cleared from DB + foreach ($extension_catid_ids as $extension_catid_id) + { + // Remove Extension catid items from the ucm base table + $extension_catid_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $extension_catid_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_base')); + $query->where($extension_catid_condition); + $db->setQuery($query); + // Execute the query to remove Extension catid items + $db->execute(); + + // Remove Extension catid items from the ucm history table + $extension_catid_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $extension_catid_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_history')); + $query->where($extension_catid_condition); + $db->setQuery($query); + // Execute the query to remove Extension catid items + $db->execute(); + } + } + + // Create a new query object. + $query = $db->getQuery(true); + // Select id from content type table + $query->select($db->quoteName('type_id')); + $query->from($db->quoteName('#__content_types')); + // Where Release alias is found + $query->where( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.release') ); + $db->setQuery($query); + // Execute query to see if alias is found + $db->execute(); + $release_found = $db->getNumRows(); + // Now check if there were any rows + if ($release_found) + { + // Since there are load the needed release type ids + $release_ids = $db->loadColumn(); + // Remove Release from the content type table + $release_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.release') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__content_types')); + $query->where($release_condition); + $db->setQuery($query); + // Execute the query to remove Release items + $release_done = $db->execute(); + if ($release_done) + { + // If successfully remove Release add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.release) type alias was removed from the #__content_type table')); + } + + // Remove Release items from the contentitem tag map table + $release_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.release') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__contentitem_tag_map')); + $query->where($release_condition); + $db->setQuery($query); + // Execute the query to remove Release items + $release_done = $db->execute(); + if ($release_done) + { + // If successfully remove Release add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.release) type alias was removed from the #__contentitem_tag_map table')); + } + + // Remove Release items from the ucm content table + $release_condition = array( $db->quoteName('core_type_alias') . ' = ' . $db->quote('com_extensiondistributor.release') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_content')); + $query->where($release_condition); + $db->setQuery($query); + // Execute the query to remove Release items + $release_done = $db->execute(); + if ($release_done) + { + // If successfully removed Release add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.release) type alias was removed from the #__ucm_content table')); + } + + // Make sure that all the Release items are cleared from DB + foreach ($release_ids as $release_id) + { + // Remove Release items from the ucm base table + $release_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $release_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_base')); + $query->where($release_condition); + $db->setQuery($query); + // Execute the query to remove Release items + $db->execute(); + + // Remove Release items from the ucm history table + $release_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $release_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_history')); + $query->where($release_condition); + $db->setQuery($query); + // Execute the query to remove Release items + $db->execute(); + } + } + + // Create a new query object. + $query = $db->getQuery(true); + // Select id from content type table + $query->select($db->quoteName('type_id')); + $query->from($db->quoteName('#__content_types')); + // Where Key alias is found + $query->where( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.key') ); + $db->setQuery($query); + // Execute query to see if alias is found + $db->execute(); + $key_found = $db->getNumRows(); + // Now check if there were any rows + if ($key_found) + { + // Since there are load the needed key type ids + $key_ids = $db->loadColumn(); + // Remove Key from the content type table + $key_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.key') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__content_types')); + $query->where($key_condition); + $db->setQuery($query); + // Execute the query to remove Key items + $key_done = $db->execute(); + if ($key_done) + { + // If successfully remove Key add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.key) type alias was removed from the #__content_type table')); + } + + // Remove Key items from the contentitem tag map table + $key_condition = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.key') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__contentitem_tag_map')); + $query->where($key_condition); + $db->setQuery($query); + // Execute the query to remove Key items + $key_done = $db->execute(); + if ($key_done) + { + // If successfully remove Key add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.key) type alias was removed from the #__contentitem_tag_map table')); + } + + // Remove Key items from the ucm content table + $key_condition = array( $db->quoteName('core_type_alias') . ' = ' . $db->quote('com_extensiondistributor.key') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_content')); + $query->where($key_condition); + $db->setQuery($query); + // Execute the query to remove Key items + $key_done = $db->execute(); + if ($key_done) + { + // If successfully removed Key add queued success message. + $app->enqueueMessage(JText::_('The (com_extensiondistributor.key) type alias was removed from the #__ucm_content table')); + } + + // Make sure that all the Key items are cleared from DB + foreach ($key_ids as $key_id) + { + // Remove Key items from the ucm base table + $key_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $key_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_base')); + $query->where($key_condition); + $db->setQuery($query); + // Execute the query to remove Key items + $db->execute(); + + // Remove Key items from the ucm history table + $key_condition = array( $db->quoteName('ucm_type_id') . ' = ' . $key_id); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__ucm_history')); + $query->where($key_condition); + $db->setQuery($query); + // Execute the query to remove Key items + $db->execute(); + } + } + + // If All related items was removed queued success message. + $app->enqueueMessage(JText::_('All related items was removed from the #__ucm_base table')); + $app->enqueueMessage(JText::_('All related items was removed from the #__ucm_history table')); + + // Remove extensiondistributor assets from the assets table + $extensiondistributor_condition = array( $db->quoteName('name') . ' LIKE ' . $db->quote('com_extensiondistributor%') ); + + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__assets')); + $query->where($extensiondistributor_condition); + $db->setQuery($query); + $key_done = $db->execute(); + if ($key_done) + { + // If successfully removed extensiondistributor add queued success message. + $app->enqueueMessage(JText::_('All related items was removed from the #__assets table')); + } + + // Get the biggest rule column in the assets table at this point. + $get_rule_length = "SELECT CHAR_LENGTH(`rules`) as rule_size FROM #__assets ORDER BY rule_size DESC LIMIT 1"; + $db->setQuery($get_rule_length); + if ($db->execute()) + { + $rule_length = $db->loadResult(); + // Check the size of the rules column + if ($rule_length < 5120) + { + // Revert the assets table rules column back to the default + $revert_rule = "ALTER TABLE `#__assets` CHANGE `rules` `rules` varchar(5120) NOT NULL COMMENT 'JSON encoded access control.';"; + $db->setQuery($revert_rule); + $db->execute(); + $app->enqueueMessage(JText::_('Reverted the #__assets table rules column back to its default size of varchar(5120)')); + } + else + { + + $app->enqueueMessage(JText::_('Could not revert the #__assets table rules column back to its default size of varchar(5120), since there is still one or more components that still requires the column to be larger.')); + } + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Set app if not set already. + if (!isset($app)) + { + $app = JFactory::getApplication(); + } + // Remove Extensiondistributor from the action_logs_extensions table + $extensiondistributor_action_logs_extensions = array( $db->quoteName('extension') . ' = ' . $db->quote('com_extensiondistributor') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__action_logs_extensions')); + $query->where($extensiondistributor_action_logs_extensions); + $db->setQuery($query); + // Execute the query to remove Extensiondistributor + $extensiondistributor_removed_done = $db->execute(); + if ($extensiondistributor_removed_done) + { + // If successfully remove Extensiondistributor add queued success message. + $app->enqueueMessage(JText::_('The com_extensiondistributor extension was removed from the #__action_logs_extensions table')); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Set app if not set already. + if (!isset($app)) + { + $app = JFactory::getApplication(); + } + // Remove Extensiondistributor Package from the action_log_config table + $package_action_log_config = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.package') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__action_log_config')); + $query->where($package_action_log_config); + $db->setQuery($query); + // Execute the query to remove com_extensiondistributor.package + $package_action_log_config_done = $db->execute(); + if ($package_action_log_config_done) + { + // If successfully removed Extensiondistributor Package add queued success message. + $app->enqueueMessage(JText::_('The com_extensiondistributor.package type alias was removed from the #__action_log_config table')); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Set app if not set already. + if (!isset($app)) + { + $app = JFactory::getApplication(); + } + // Remove Extensiondistributor Extension from the action_log_config table + $extension_action_log_config = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.extension') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__action_log_config')); + $query->where($extension_action_log_config); + $db->setQuery($query); + // Execute the query to remove com_extensiondistributor.extension + $extension_action_log_config_done = $db->execute(); + if ($extension_action_log_config_done) + { + // If successfully removed Extensiondistributor Extension add queued success message. + $app->enqueueMessage(JText::_('The com_extensiondistributor.extension type alias was removed from the #__action_log_config table')); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Set app if not set already. + if (!isset($app)) + { + $app = JFactory::getApplication(); + } + // Remove Extensiondistributor Release from the action_log_config table + $release_action_log_config = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.release') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__action_log_config')); + $query->where($release_action_log_config); + $db->setQuery($query); + // Execute the query to remove com_extensiondistributor.release + $release_action_log_config_done = $db->execute(); + if ($release_action_log_config_done) + { + // If successfully removed Extensiondistributor Release add queued success message. + $app->enqueueMessage(JText::_('The com_extensiondistributor.release type alias was removed from the #__action_log_config table')); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Set app if not set already. + if (!isset($app)) + { + $app = JFactory::getApplication(); + } + // Remove Extensiondistributor Subscription from the action_log_config table + $subscription_action_log_config = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.subscription') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__action_log_config')); + $query->where($subscription_action_log_config); + $db->setQuery($query); + // Execute the query to remove com_extensiondistributor.subscription + $subscription_action_log_config_done = $db->execute(); + if ($subscription_action_log_config_done) + { + // If successfully removed Extensiondistributor Subscription add queued success message. + $app->enqueueMessage(JText::_('The com_extensiondistributor.subscription type alias was removed from the #__action_log_config table')); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Set app if not set already. + if (!isset($app)) + { + $app = JFactory::getApplication(); + } + // Remove Extensiondistributor Key from the action_log_config table + $key_action_log_config = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.key') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__action_log_config')); + $query->where($key_action_log_config); + $db->setQuery($query); + // Execute the query to remove com_extensiondistributor.key + $key_action_log_config_done = $db->execute(); + if ($key_action_log_config_done) + { + // If successfully removed Extensiondistributor Key add queued success message. + $app->enqueueMessage(JText::_('The com_extensiondistributor.key type alias was removed from the #__action_log_config table')); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Set app if not set already. + if (!isset($app)) + { + $app = JFactory::getApplication(); + } + // Remove Extensiondistributor Download from the action_log_config table + $download_action_log_config = array( $db->quoteName('type_alias') . ' = '. $db->quote('com_extensiondistributor.download') ); + // Create a new query object. + $query = $db->getQuery(true); + $query->delete($db->quoteName('#__action_log_config')); + $query->where($download_action_log_config); + $db->setQuery($query); + // Execute the query to remove com_extensiondistributor.download + $download_action_log_config_done = $db->execute(); + if ($download_action_log_config_done) + { + // If successfully removed Extensiondistributor Download add queued success message. + $app->enqueueMessage(JText::_('The com_extensiondistributor.download type alias was removed from the #__action_log_config table')); + } + // little notice as after service, in case of bad experience with component. + echo '

      Did something go wrong? Are you disappointed?

      +

      Please let me know at emmanuel.danan@agerix.fr. +
      We at Agence Agerix are committed to building extensions that performs proficiently! You can help us, really! +
      Send me your thoughts on improvements that is needed, trust me, I will be very grateful! +
      Visit us at https://agerix.fr today!

      '; + } + + /** + * Called on update + * + * @param ComponentAdapter $parent The object responsible for running this script + * + * @return boolean True on success + */ + public function update(ComponentAdapter $parent){} + + /** + * Called before any type of action + * + * @param string $type Which action is happening (install|uninstall|discover_install|update) + * @param ComponentAdapter $parent The object responsible for running this script + * + * @return boolean True on success + */ + public function preflight($type, ComponentAdapter $parent) + { + // get application + $app = JFactory::getApplication(); + // is redundant or so it seems ...hmmm let me know if it works again + if ($type === 'uninstall') + { + return true; + } + // the default for both install and update + $jversion = new JVersion(); + if (!$jversion->isCompatible('3.8.0')) + { + $app->enqueueMessage('Please upgrade to at least Joomla! 3.8.0 before continuing!', 'error'); + return false; + } + // do any updates needed + if ($type === 'update') + { + } + // do any install needed + if ($type === 'install') + { + } + // check if the PHPExcel stuff is still around + if (File::exists(JPATH_ADMINISTRATOR . '/components/com_extensiondistributor/helpers/PHPExcel.php')) + { + // We need to remove this old PHPExcel folder + $this->removeFolder(JPATH_ADMINISTRATOR . '/components/com_extensiondistributor/helpers/PHPExcel'); + // We need to remove this old PHPExcel file + File::delete(JPATH_ADMINISTRATOR . '/components/com_extensiondistributor/helpers/PHPExcel.php'); + } + return true; + } + + /** + * Called after any type of action + * + * @param string $type Which action is happening (install|uninstall|discover_install|update) + * @param ComponentAdapter $parent The object responsible for running this script + * + * @return boolean True on success + */ + public function postflight($type, ComponentAdapter $parent) + { + // get application + $app = JFactory::getApplication(); + // set the default component settings + if ($type === 'install') + { + + // Get The Database object + $db = JFactory::getDbo(); + + // Create the package content type object. + $package = new stdClass(); + $package->type_title = 'Extensiondistributor Package'; + $package->type_alias = 'com_extensiondistributor.package'; + $package->table = '{"special": {"dbtable": "#__extensiondistributor_package","key": "id","type": "Package","prefix": "extensiondistributorTable","config": "array()"},"common": {"dbtable": "#__ucm_content","key": "ucm_id","type": "Corecontent","prefix": "JTable","config": "array()"}}'; + $package->field_mappings = '{"common": {"core_content_item_id": "id","core_title": "name","core_state": "published","core_alias": "alias","core_created_time": "created","core_modified_time": "modified","core_body": "description","core_hits": "hits","core_publish_up": "null","core_publish_down": "null","core_access": "access","core_params": "params","core_featured": "null","core_metadata": "metadata","core_language": "null","core_images": "null","core_urls": "null","core_version": "version","core_ordering": "ordering","core_metakey": "metakey","core_metadesc": "metadesc","core_catid": "catid","core_xreference": "null","asset_id": "asset_id"},"special": {"name":"name","description":"description","release_date":"release_date","version_number":"version_number","update":"update","free":"free","add_php_method_uninstall":"add_php_method_uninstall","add_php_postflight_update":"add_php_postflight_update","add_php_postflight_install":"add_php_postflight_install","add_php_preflight_uninstall":"add_php_preflight_uninstall","infourl":"infourl","useupdate":"useupdate","packager":"packager","file":"file","jed":"jed","newfilename":"newfilename","license":"license","php_minimum":"php_minimum","blockchilduninstall":"blockchilduninstall","packagerurl":"packagerurl","add_php_preflight_install":"add_php_preflight_install","copyright":"copyright","add_php_preflight_update":"add_php_preflight_update","image":"image","php_preflight_install":"php_preflight_install","alias":"alias","php_preflight_update":"php_preflight_update","packagename":"packagename","php_preflight_uninstall":"php_preflight_uninstall","author":"author","php_postflight_install":"php_postflight_install","authorurl":"authorurl","php_postflight_update":"php_postflight_update","authoremail":"authoremail","php_method_uninstall":"php_method_uninstall","targetplatform":"targetplatform"}}'; + $package->router = 'ExtensiondistributorHelperRoute::getPackageRoute'; + $package->content_history_options = '{"formFile": "administrator/components/com_extensiondistributor/models/forms/package.xml","hideFields": ["asset_id","checked_out","checked_out_time","version","release_date","version_number"],"ignoreChanges": ["modified_by","modified","checked_out","checked_out_time","version","hits"],"convertToInt": ["published","ordering","free","add_php_method_uninstall","add_php_postflight_update","add_php_postflight_install","add_php_preflight_uninstall","useupdate","catid","blockchilduninstall","add_php_preflight_install","add_php_preflight_update"],"displayLookup": [{"sourceColumn": "catid","targetTable": "#__categories","targetColumn": "id","displayColumn": "title"},{"sourceColumn": "created_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "access","targetTable": "#__viewlevels","targetColumn": "id","displayColumn": "title"},{"sourceColumn": "modified_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"}]}'; + + // Set the object into the content types table. + $package_Inserted = $db->insertObject('#__content_types', $package); + + // Create the extension content type object. + $extension = new stdClass(); + $extension->type_title = 'Extensiondistributor Extension'; + $extension->type_alias = 'com_extensiondistributor.extension'; + $extension->table = '{"special": {"dbtable": "#__extensiondistributor_extension","key": "id","type": "Extension","prefix": "extensiondistributorTable","config": "array()"},"common": {"dbtable": "#__ucm_content","key": "ucm_id","type": "Corecontent","prefix": "JTable","config": "array()"}}'; + $extension->field_mappings = '{"common": {"core_content_item_id": "id","core_title": "name","core_state": "published","core_alias": "alias","core_created_time": "created","core_modified_time": "modified","core_body": "null","core_hits": "hits","core_publish_up": "null","core_publish_down": "null","core_access": "null","core_params": "params","core_featured": "null","core_metadata": "null","core_language": "null","core_images": "null","core_urls": "null","core_version": "version","core_ordering": "ordering","core_metakey": "null","core_metadesc": "null","core_catid": "catid","core_xreference": "null","asset_id": "asset_id"},"special": {"name":"name","type":"type","version_number":"version_number","release_date":"release_date","update":"update","group":"group","client":"client","alias":"alias","newfilename":"newfilename","keepfilename":"keepfilename","element":"element","tags":"tags","image":"image","description":"description"}}'; + $extension->router = 'ExtensiondistributorHelperRoute::getExtensionRoute'; + $extension->content_history_options = '{"formFile": "administrator/components/com_extensiondistributor/models/forms/extension.xml","hideFields": ["asset_id","checked_out","checked_out_time","version","version_number","release_date"],"ignoreChanges": ["modified_by","modified","checked_out","checked_out_time","version","hits"],"convertToInt": ["published","ordering","keepfilename","catid"],"displayLookup": [{"sourceColumn": "catid","targetTable": "#__categories","targetColumn": "id","displayColumn": "title"},{"sourceColumn": "created_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "modified_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"}]}'; + + // Set the object into the content types table. + $extension_Inserted = $db->insertObject('#__content_types', $extension); + + // Create the extension category content type object. + $extension_category = new stdClass(); + $extension_category->type_title = 'Extensiondistributor Extension Catid'; + $extension_category->type_alias = 'com_extensiondistributor.extension.category'; + $extension_category->table = '{"special":{"dbtable":"#__categories","key":"id","type":"Category","prefix":"JTable","config":"array()"},"common":{"dbtable":"#__ucm_content","key":"ucm_id","type":"Corecontent","prefix":"JTable","config":"array()"}}'; + $extension_category->field_mappings = '{"common":{"core_content_item_id":"id","core_title":"title","core_state":"published","core_alias":"alias","core_created_time":"created_time","core_modified_time":"modified_time","core_body":"description", "core_hits":"hits","core_publish_up":"null","core_publish_down":"null","core_access":"access", "core_params":"params", "core_featured":"null", "core_metadata":"metadata", "core_language":"language", "core_images":"null", "core_urls":"null", "core_version":"version", "core_ordering":"null", "core_metakey":"metakey", "core_metadesc":"metadesc", "core_catid":"parent_id", "core_xreference":"null", "asset_id":"asset_id"}, "special":{"parent_id":"parent_id","lft":"lft","rgt":"rgt","level":"level","path":"path","extension":"extension","note":"note"}}'; + $extension_category->router = 'ExtensiondistributorHelperRoute::getCategoryRoute'; + $extension_category->content_history_options = '{"formFile":"administrator\/components\/com_categories\/models\/forms\/category.xml", "hideFields":["asset_id","checked_out","checked_out_time","version","lft","rgt","level","path","extension"], "ignoreChanges":["modified_user_id", "modified_time", "checked_out", "checked_out_time", "version", "hits", "path"],"convertToInt":["publish_up", "publish_down"], "displayLookup":[{"sourceColumn":"created_user_id","targetTable":"#__users","targetColumn":"id","displayColumn":"name"},{"sourceColumn":"access","targetTable":"#__viewlevels","targetColumn":"id","displayColumn":"title"},{"sourceColumn":"modified_user_id","targetTable":"#__users","targetColumn":"id","displayColumn":"name"},{"sourceColumn":"parent_id","targetTable":"#__categories","targetColumn":"id","displayColumn":"title"}]}'; + + // Set the object into the content types table. + $extension_category_Inserted = $db->insertObject('#__content_types', $extension_category); + + // Create the release content type object. + $release = new stdClass(); + $release->type_title = 'Extensiondistributor Release'; + $release->type_alias = 'com_extensiondistributor.release'; + $release->table = '{"special": {"dbtable": "#__extensiondistributor_release","key": "id","type": "Release","prefix": "extensiondistributorTable","config": "array()"},"common": {"dbtable": "#__ucm_content","key": "ucm_id","type": "Corecontent","prefix": "JTable","config": "array()"}}'; + $release->field_mappings = '{"common": {"core_content_item_id": "id","core_title": "version_number","core_state": "published","core_alias": "null","core_created_time": "created","core_modified_time": "modified","core_body": "null","core_hits": "hits","core_publish_up": "null","core_publish_down": "null","core_access": "null","core_params": "params","core_featured": "null","core_metadata": "null","core_language": "null","core_images": "null","core_urls": "null","core_version": "version","core_ordering": "ordering","core_metakey": "null","core_metadesc": "null","core_catid": "null","core_xreference": "null","asset_id": "asset_id"},"special": {"toolbar":"toolbar","extension":"extension","package":"package","version_number":"version_number","file":"file","release_date":"release_date","reltype":"reltype","description":"description","zip_content":"zip_content","xmlpublished":"xmlpublished","stability":"stability"}}'; + $release->router = 'ExtensiondistributorHelperRoute::getReleaseRoute'; + $release->content_history_options = '{"formFile": "administrator/components/com_extensiondistributor/models/forms/release.xml","hideFields": ["asset_id","checked_out","checked_out_time","version","toolbar","zip_content"],"ignoreChanges": ["modified_by","modified","checked_out","checked_out_time","version","hits"],"convertToInt": ["published","ordering","extension","package","xmlpublished","stability"],"displayLookup": [{"sourceColumn": "created_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "modified_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "extension","targetTable": "#__extensiondistributor_extension","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "package","targetTable": "#__extensiondistributor_package","targetColumn": "id","displayColumn": "name"}]}'; + + // Set the object into the content types table. + $release_Inserted = $db->insertObject('#__content_types', $release); + + // Create the key content type object. + $key = new stdClass(); + $key->type_title = 'Extensiondistributor Key'; + $key->type_alias = 'com_extensiondistributor.key'; + $key->table = '{"special": {"dbtable": "#__extensiondistributor_key","key": "id","type": "Key","prefix": "extensiondistributorTable","config": "array()"},"common": {"dbtable": "#__ucm_content","key": "ucm_id","type": "Corecontent","prefix": "JTable","config": "array()"}}'; + $key->field_mappings = '{"common": {"core_content_item_id": "id","core_title": "key","core_state": "published","core_alias": "null","core_created_time": "created","core_modified_time": "modified","core_body": "null","core_hits": "hits","core_publish_up": "null","core_publish_down": "null","core_access": "null","core_params": "params","core_featured": "null","core_metadata": "null","core_language": "null","core_images": "null","core_urls": "null","core_version": "version","core_ordering": "ordering","core_metakey": "null","core_metadesc": "null","core_catid": "null","core_xreference": "null","asset_id": "asset_id"},"special": {"key":"key","website":"website","user":"user"}}'; + $key->router = 'ExtensiondistributorHelperRoute::getKeyRoute'; + $key->content_history_options = '{"formFile": "administrator/components/com_extensiondistributor/models/forms/key.xml","hideFields": ["asset_id","checked_out","checked_out_time","version"],"ignoreChanges": ["modified_by","modified","checked_out","checked_out_time","version","hits"],"convertToInt": ["published","ordering","user"],"displayLookup": [{"sourceColumn": "created_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "modified_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"}]}'; + + // Set the object into the content types table. + $key_Inserted = $db->insertObject('#__content_types', $key); + + + // Install the global extension params. + $query = $db->getQuery(true); + // Field to update. + $fields = array( + $db->quoteName('params') . ' = ' . $db->quote('{"autorName":"Emmanuel Danan","autorEmail":"emmanuel.danan@agerix.fr","releases_directory":"packages","jcb_directory":"jcb","updates_directory":"updates","import_directory":"importfolder","stability":"4","keepfilename":"1","newfilename":"{type}{_group}_{element}_v{major}_{minor}_{release}_{stability}.zip","targetplatform":"3.(9|10)","php_minimum":"7.0","check_in":"-1 day","save_history":"1","history_limit":"10"}'), + ); + // Condition. + $conditions = array( + $db->quoteName('element') . ' = ' . $db->quote('com_extensiondistributor') + ); + $query->update($db->quoteName('#__extensions'))->set($fields)->where($conditions); + $db->setQuery($query); + $allDone = $db->execute(); + + + // Create a default category + $app = JFactory::getApplication(); + if (!class_exists('ExtensiondistributorHelper')) { + require_once JPATH_ADMINISTRATOR.'/components/com_extensiondistributor/helpers/extensiondistributor.php'; + } + $model = ExtensiondistributorHelper::getModel('Category', JPATH_ADMINISTRATOR . '/components/com_categories', 'Categories'); + $data = $model->getItem(); + $data->parent_id = 1; + $data->level = 1; + $data->path = 'uncategorized'; + $data->extension = 'com_extensiondistributor'; + $data->alias = 'uncategorized'; + $data->title = 'Uncategorized'; + $data->created_user_id = JFactory::getUser()->id; + $data->created_time = JFactory::getDate()->toSql(); + $data->access = 1; + $data->published = 1; + $data->language = '*'; + $model->save((array)$data); + + // Create and secure directories + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $componentid = JComponentHelper::getComponent('com_extensiondistributor')->id; + $directories['releases_directory'] = JPath::clean(JPATH_SITE . '/' . $cparams->get('releases_directory','packages') . '/'); + $directories['jcb_directory'] = JPath::clean(JPATH_SITE . '/' . $cparams->get('jcb_directory','jcb') . '/'); + $directories['updates_directory'] = JPath::clean(JPATH_SITE . '/' . $cparams->get('updates_directory','updates') . '/'); + $directories['import_directory'] = JPath::clean(JPATH_SITE . '/' . $cparams->get('import_directory','importfolder') . '/'); + foreach ($directories as $k => $directory) { + if (!JFolder::exists($directory)) { + JFolder::create($directory); + } + if ($k !== 'updates_directory') + { + ExtensiondistributorHelper::secureFolder($directory,'secure'); + } + } + + // Get the biggest rule column in the assets table at this point. + $get_rule_length = "SELECT CHAR_LENGTH(`rules`) as rule_size FROM #__assets ORDER BY rule_size DESC LIMIT 1"; + $db->setQuery($get_rule_length); + if ($db->execute()) + { + $rule_length = $db->loadResult(); + // Check the size of the rules column + if ($rule_length <= 7200) + { + // Fix the assets table rules column size + $fix_rules_size = "ALTER TABLE `#__assets` CHANGE `rules` `rules` TEXT NOT NULL COMMENT 'JSON encoded access control. Enlarged to TEXT by JCB';"; + $db->setQuery($fix_rules_size); + $db->execute(); + $app->enqueueMessage(JText::_('The #__assets table rules column was resized to the TEXT datatype for the components possible large permission rules.')); + } + } + echo ' + + '; + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the extensiondistributor action logs extensions object. + $extensiondistributor_action_logs_extensions = new stdClass(); + $extensiondistributor_action_logs_extensions->extension = 'com_extensiondistributor'; + + // Set the object into the action logs extensions table. + $extensiondistributor_action_logs_extensions_Inserted = $db->insertObject('#__action_logs_extensions', $extensiondistributor_action_logs_extensions); + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the package action log config object. + $package_action_log_config = new stdClass(); + $package_action_log_config->type_title = 'PACKAGE'; + $package_action_log_config->type_alias = 'com_extensiondistributor.package'; + $package_action_log_config->id_holder = 'id'; + $package_action_log_config->title_holder = 'name'; + $package_action_log_config->table_name = '#__extensiondistributor_package'; + $package_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Set the object into the action log config table. + $package_Inserted = $db->insertObject('#__action_log_config', $package_action_log_config); + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the extension action log config object. + $extension_action_log_config = new stdClass(); + $extension_action_log_config->type_title = 'EXTENSION'; + $extension_action_log_config->type_alias = 'com_extensiondistributor.extension'; + $extension_action_log_config->id_holder = 'id'; + $extension_action_log_config->title_holder = 'name'; + $extension_action_log_config->table_name = '#__extensiondistributor_extension'; + $extension_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Set the object into the action log config table. + $extension_Inserted = $db->insertObject('#__action_log_config', $extension_action_log_config); + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the release action log config object. + $release_action_log_config = new stdClass(); + $release_action_log_config->type_title = 'RELEASE'; + $release_action_log_config->type_alias = 'com_extensiondistributor.release'; + $release_action_log_config->id_holder = 'id'; + $release_action_log_config->title_holder = 'version_number'; + $release_action_log_config->table_name = '#__extensiondistributor_release'; + $release_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Set the object into the action log config table. + $release_Inserted = $db->insertObject('#__action_log_config', $release_action_log_config); + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the subscription action log config object. + $subscription_action_log_config = new stdClass(); + $subscription_action_log_config->type_title = 'SUBSCRIPTION'; + $subscription_action_log_config->type_alias = 'com_extensiondistributor.subscription'; + $subscription_action_log_config->id_holder = 'id'; + $subscription_action_log_config->title_holder = 'date_start'; + $subscription_action_log_config->table_name = '#__extensiondistributor_subscription'; + $subscription_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Set the object into the action log config table. + $subscription_Inserted = $db->insertObject('#__action_log_config', $subscription_action_log_config); + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the key action log config object. + $key_action_log_config = new stdClass(); + $key_action_log_config->type_title = 'KEY'; + $key_action_log_config->type_alias = 'com_extensiondistributor.key'; + $key_action_log_config->id_holder = 'id'; + $key_action_log_config->title_holder = 'key'; + $key_action_log_config->table_name = '#__extensiondistributor_key'; + $key_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Set the object into the action log config table. + $key_Inserted = $db->insertObject('#__action_log_config', $key_action_log_config); + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the download action log config object. + $download_action_log_config = new stdClass(); + $download_action_log_config->type_title = 'DOWNLOAD'; + $download_action_log_config->type_alias = 'com_extensiondistributor.download'; + $download_action_log_config->id_holder = 'id'; + $download_action_log_config->title_holder = 'id'; + $download_action_log_config->table_name = '#__extensiondistributor_download'; + $download_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Set the object into the action log config table. + $download_Inserted = $db->insertObject('#__action_log_config', $download_action_log_config); + } + // do any updates needed + if ($type === 'update') + { + + // Get The Database object + $db = JFactory::getDbo(); + + // Create the package content type object. + $package = new stdClass(); + $package->type_title = 'Extensiondistributor Package'; + $package->type_alias = 'com_extensiondistributor.package'; + $package->table = '{"special": {"dbtable": "#__extensiondistributor_package","key": "id","type": "Package","prefix": "extensiondistributorTable","config": "array()"},"common": {"dbtable": "#__ucm_content","key": "ucm_id","type": "Corecontent","prefix": "JTable","config": "array()"}}'; + $package->field_mappings = '{"common": {"core_content_item_id": "id","core_title": "name","core_state": "published","core_alias": "alias","core_created_time": "created","core_modified_time": "modified","core_body": "description","core_hits": "hits","core_publish_up": "null","core_publish_down": "null","core_access": "access","core_params": "params","core_featured": "null","core_metadata": "metadata","core_language": "null","core_images": "null","core_urls": "null","core_version": "version","core_ordering": "ordering","core_metakey": "metakey","core_metadesc": "metadesc","core_catid": "catid","core_xreference": "null","asset_id": "asset_id"},"special": {"name":"name","description":"description","release_date":"release_date","version_number":"version_number","update":"update","free":"free","add_php_method_uninstall":"add_php_method_uninstall","add_php_postflight_update":"add_php_postflight_update","add_php_postflight_install":"add_php_postflight_install","add_php_preflight_uninstall":"add_php_preflight_uninstall","infourl":"infourl","useupdate":"useupdate","packager":"packager","file":"file","jed":"jed","newfilename":"newfilename","license":"license","php_minimum":"php_minimum","blockchilduninstall":"blockchilduninstall","packagerurl":"packagerurl","add_php_preflight_install":"add_php_preflight_install","copyright":"copyright","add_php_preflight_update":"add_php_preflight_update","image":"image","php_preflight_install":"php_preflight_install","alias":"alias","php_preflight_update":"php_preflight_update","packagename":"packagename","php_preflight_uninstall":"php_preflight_uninstall","author":"author","php_postflight_install":"php_postflight_install","authorurl":"authorurl","php_postflight_update":"php_postflight_update","authoremail":"authoremail","php_method_uninstall":"php_method_uninstall","targetplatform":"targetplatform"}}'; + $package->router = 'ExtensiondistributorHelperRoute::getPackageRoute'; + $package->content_history_options = '{"formFile": "administrator/components/com_extensiondistributor/models/forms/package.xml","hideFields": ["asset_id","checked_out","checked_out_time","version","release_date","version_number"],"ignoreChanges": ["modified_by","modified","checked_out","checked_out_time","version","hits"],"convertToInt": ["published","ordering","free","add_php_method_uninstall","add_php_postflight_update","add_php_postflight_install","add_php_preflight_uninstall","useupdate","catid","blockchilduninstall","add_php_preflight_install","add_php_preflight_update"],"displayLookup": [{"sourceColumn": "catid","targetTable": "#__categories","targetColumn": "id","displayColumn": "title"},{"sourceColumn": "created_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "access","targetTable": "#__viewlevels","targetColumn": "id","displayColumn": "title"},{"sourceColumn": "modified_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"}]}'; + + // Check if package type is already in content_type DB. + $package_id = null; + $query = $db->getQuery(true); + $query->select($db->quoteName(array('type_id'))); + $query->from($db->quoteName('#__content_types')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($package->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $package->type_id = $db->loadResult(); + $package_Updated = $db->updateObject('#__content_types', $package, 'type_id'); + } + else + { + $package_Inserted = $db->insertObject('#__content_types', $package); + } + + // Create the extension content type object. + $extension = new stdClass(); + $extension->type_title = 'Extensiondistributor Extension'; + $extension->type_alias = 'com_extensiondistributor.extension'; + $extension->table = '{"special": {"dbtable": "#__extensiondistributor_extension","key": "id","type": "Extension","prefix": "extensiondistributorTable","config": "array()"},"common": {"dbtable": "#__ucm_content","key": "ucm_id","type": "Corecontent","prefix": "JTable","config": "array()"}}'; + $extension->field_mappings = '{"common": {"core_content_item_id": "id","core_title": "name","core_state": "published","core_alias": "alias","core_created_time": "created","core_modified_time": "modified","core_body": "null","core_hits": "hits","core_publish_up": "null","core_publish_down": "null","core_access": "null","core_params": "params","core_featured": "null","core_metadata": "null","core_language": "null","core_images": "null","core_urls": "null","core_version": "version","core_ordering": "ordering","core_metakey": "null","core_metadesc": "null","core_catid": "catid","core_xreference": "null","asset_id": "asset_id"},"special": {"name":"name","type":"type","version_number":"version_number","release_date":"release_date","update":"update","group":"group","client":"client","alias":"alias","newfilename":"newfilename","keepfilename":"keepfilename","element":"element","tags":"tags","image":"image","description":"description"}}'; + $extension->router = 'ExtensiondistributorHelperRoute::getExtensionRoute'; + $extension->content_history_options = '{"formFile": "administrator/components/com_extensiondistributor/models/forms/extension.xml","hideFields": ["asset_id","checked_out","checked_out_time","version","version_number","release_date"],"ignoreChanges": ["modified_by","modified","checked_out","checked_out_time","version","hits"],"convertToInt": ["published","ordering","keepfilename","catid"],"displayLookup": [{"sourceColumn": "catid","targetTable": "#__categories","targetColumn": "id","displayColumn": "title"},{"sourceColumn": "created_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "modified_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"}]}'; + + // Check if extension type is already in content_type DB. + $extension_id = null; + $query = $db->getQuery(true); + $query->select($db->quoteName(array('type_id'))); + $query->from($db->quoteName('#__content_types')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($extension->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $extension->type_id = $db->loadResult(); + $extension_Updated = $db->updateObject('#__content_types', $extension, 'type_id'); + } + else + { + $extension_Inserted = $db->insertObject('#__content_types', $extension); + } + + // Create the extension category content type object. + $extension_category = new stdClass(); + $extension_category->type_title = 'Extensiondistributor Extension Catid'; + $extension_category->type_alias = 'com_extensiondistributor.extension.category'; + $extension_category->table = '{"special":{"dbtable":"#__categories","key":"id","type":"Category","prefix":"JTable","config":"array()"},"common":{"dbtable":"#__ucm_content","key":"ucm_id","type":"Corecontent","prefix":"JTable","config":"array()"}}'; + $extension_category->field_mappings = '{"common":{"core_content_item_id":"id","core_title":"title","core_state":"published","core_alias":"alias","core_created_time":"created_time","core_modified_time":"modified_time","core_body":"description", "core_hits":"hits","core_publish_up":"null","core_publish_down":"null","core_access":"access", "core_params":"params", "core_featured":"null", "core_metadata":"metadata", "core_language":"language", "core_images":"null", "core_urls":"null", "core_version":"version", "core_ordering":"null", "core_metakey":"metakey", "core_metadesc":"metadesc", "core_catid":"parent_id", "core_xreference":"null", "asset_id":"asset_id"}, "special":{"parent_id":"parent_id","lft":"lft","rgt":"rgt","level":"level","path":"path","extension":"extension","note":"note"}}'; + $extension_category->router = 'ExtensiondistributorHelperRoute::getCategoryRoute'; + $extension_category->content_history_options = '{"formFile":"administrator\/components\/com_categories\/models\/forms\/category.xml", "hideFields":["asset_id","checked_out","checked_out_time","version","lft","rgt","level","path","extension"], "ignoreChanges":["modified_user_id", "modified_time", "checked_out", "checked_out_time", "version", "hits", "path"],"convertToInt":["publish_up", "publish_down"], "displayLookup":[{"sourceColumn":"created_user_id","targetTable":"#__users","targetColumn":"id","displayColumn":"name"},{"sourceColumn":"access","targetTable":"#__viewlevels","targetColumn":"id","displayColumn":"title"},{"sourceColumn":"modified_user_id","targetTable":"#__users","targetColumn":"id","displayColumn":"name"},{"sourceColumn":"parent_id","targetTable":"#__categories","targetColumn":"id","displayColumn":"title"}]}'; + + // Check if extension category type is already in content_type DB. + $extension_category_id = null; + $query = $db->getQuery(true); + $query->select($db->quoteName(array('type_id'))); + $query->from($db->quoteName('#__content_types')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($extension_category->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $extension_category->type_id = $db->loadResult(); + $extension_category_Updated = $db->updateObject('#__content_types', $extension_category, 'type_id'); + } + else + { + $extension_category_Inserted = $db->insertObject('#__content_types', $extension_category); + } + + // Create the release content type object. + $release = new stdClass(); + $release->type_title = 'Extensiondistributor Release'; + $release->type_alias = 'com_extensiondistributor.release'; + $release->table = '{"special": {"dbtable": "#__extensiondistributor_release","key": "id","type": "Release","prefix": "extensiondistributorTable","config": "array()"},"common": {"dbtable": "#__ucm_content","key": "ucm_id","type": "Corecontent","prefix": "JTable","config": "array()"}}'; + $release->field_mappings = '{"common": {"core_content_item_id": "id","core_title": "version_number","core_state": "published","core_alias": "null","core_created_time": "created","core_modified_time": "modified","core_body": "null","core_hits": "hits","core_publish_up": "null","core_publish_down": "null","core_access": "null","core_params": "params","core_featured": "null","core_metadata": "null","core_language": "null","core_images": "null","core_urls": "null","core_version": "version","core_ordering": "ordering","core_metakey": "null","core_metadesc": "null","core_catid": "null","core_xreference": "null","asset_id": "asset_id"},"special": {"toolbar":"toolbar","extension":"extension","package":"package","version_number":"version_number","file":"file","release_date":"release_date","reltype":"reltype","description":"description","zip_content":"zip_content","xmlpublished":"xmlpublished","stability":"stability"}}'; + $release->router = 'ExtensiondistributorHelperRoute::getReleaseRoute'; + $release->content_history_options = '{"formFile": "administrator/components/com_extensiondistributor/models/forms/release.xml","hideFields": ["asset_id","checked_out","checked_out_time","version","toolbar","zip_content"],"ignoreChanges": ["modified_by","modified","checked_out","checked_out_time","version","hits"],"convertToInt": ["published","ordering","extension","package","xmlpublished","stability"],"displayLookup": [{"sourceColumn": "created_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "modified_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "extension","targetTable": "#__extensiondistributor_extension","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "package","targetTable": "#__extensiondistributor_package","targetColumn": "id","displayColumn": "name"}]}'; + + // Check if release type is already in content_type DB. + $release_id = null; + $query = $db->getQuery(true); + $query->select($db->quoteName(array('type_id'))); + $query->from($db->quoteName('#__content_types')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($release->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $release->type_id = $db->loadResult(); + $release_Updated = $db->updateObject('#__content_types', $release, 'type_id'); + } + else + { + $release_Inserted = $db->insertObject('#__content_types', $release); + } + + // Create the key content type object. + $key = new stdClass(); + $key->type_title = 'Extensiondistributor Key'; + $key->type_alias = 'com_extensiondistributor.key'; + $key->table = '{"special": {"dbtable": "#__extensiondistributor_key","key": "id","type": "Key","prefix": "extensiondistributorTable","config": "array()"},"common": {"dbtable": "#__ucm_content","key": "ucm_id","type": "Corecontent","prefix": "JTable","config": "array()"}}'; + $key->field_mappings = '{"common": {"core_content_item_id": "id","core_title": "key","core_state": "published","core_alias": "null","core_created_time": "created","core_modified_time": "modified","core_body": "null","core_hits": "hits","core_publish_up": "null","core_publish_down": "null","core_access": "null","core_params": "params","core_featured": "null","core_metadata": "null","core_language": "null","core_images": "null","core_urls": "null","core_version": "version","core_ordering": "ordering","core_metakey": "null","core_metadesc": "null","core_catid": "null","core_xreference": "null","asset_id": "asset_id"},"special": {"key":"key","website":"website","user":"user"}}'; + $key->router = 'ExtensiondistributorHelperRoute::getKeyRoute'; + $key->content_history_options = '{"formFile": "administrator/components/com_extensiondistributor/models/forms/key.xml","hideFields": ["asset_id","checked_out","checked_out_time","version"],"ignoreChanges": ["modified_by","modified","checked_out","checked_out_time","version","hits"],"convertToInt": ["published","ordering","user"],"displayLookup": [{"sourceColumn": "created_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"},{"sourceColumn": "modified_by","targetTable": "#__users","targetColumn": "id","displayColumn": "name"}]}'; + + // Check if key type is already in content_type DB. + $key_id = null; + $query = $db->getQuery(true); + $query->select($db->quoteName(array('type_id'))); + $query->from($db->quoteName('#__content_types')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($key->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $key->type_id = $db->loadResult(); + $key_Updated = $db->updateObject('#__content_types', $key, 'type_id'); + } + else + { + $key_Inserted = $db->insertObject('#__content_types', $key); + } + + + echo ' + + +

      Upgrade to Version 1.0.7 Was Successful! Let us know if anything is not working as expected.

      '; + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the extensiondistributor action logs extensions object. + $extensiondistributor_action_logs_extensions = new stdClass(); + $extensiondistributor_action_logs_extensions->extension = 'com_extensiondistributor'; + + // Check if extensiondistributor action log extension is already in action logs extensions DB. + $query = $db->getQuery(true); + $query->select($db->quoteName(array('id'))); + $query->from($db->quoteName('#__action_logs_extensions')); + $query->where($db->quoteName('extension') . ' LIKE '. $db->quote($extensiondistributor_action_logs_extensions->extension)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the action logs extensions table if not found. + if (!$db->getNumRows()) + { + $extensiondistributor_action_logs_extensions_Inserted = $db->insertObject('#__action_logs_extensions', $extensiondistributor_action_logs_extensions); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the package action log config object. + $package_action_log_config = new stdClass(); + $package_action_log_config->id = null; + $package_action_log_config->type_title = 'PACKAGE'; + $package_action_log_config->type_alias = 'com_extensiondistributor.package'; + $package_action_log_config->id_holder = 'id'; + $package_action_log_config->title_holder = 'name'; + $package_action_log_config->table_name = '#__extensiondistributor_package'; + $package_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Check if package action log config is already in action_log_config DB. + $query = $db->getQuery(true); + $query->select($db->quoteName(array('id'))); + $query->from($db->quoteName('#__action_log_config')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($package_action_log_config->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $package_action_log_config->id = $db->loadResult(); + $package_action_log_config_Updated = $db->updateObject('#__action_log_config', $package_action_log_config, 'id'); + } + else + { + $package_action_log_config_Inserted = $db->insertObject('#__action_log_config', $package_action_log_config); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the extension action log config object. + $extension_action_log_config = new stdClass(); + $extension_action_log_config->id = null; + $extension_action_log_config->type_title = 'EXTENSION'; + $extension_action_log_config->type_alias = 'com_extensiondistributor.extension'; + $extension_action_log_config->id_holder = 'id'; + $extension_action_log_config->title_holder = 'name'; + $extension_action_log_config->table_name = '#__extensiondistributor_extension'; + $extension_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Check if extension action log config is already in action_log_config DB. + $query = $db->getQuery(true); + $query->select($db->quoteName(array('id'))); + $query->from($db->quoteName('#__action_log_config')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($extension_action_log_config->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $extension_action_log_config->id = $db->loadResult(); + $extension_action_log_config_Updated = $db->updateObject('#__action_log_config', $extension_action_log_config, 'id'); + } + else + { + $extension_action_log_config_Inserted = $db->insertObject('#__action_log_config', $extension_action_log_config); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the release action log config object. + $release_action_log_config = new stdClass(); + $release_action_log_config->id = null; + $release_action_log_config->type_title = 'RELEASE'; + $release_action_log_config->type_alias = 'com_extensiondistributor.release'; + $release_action_log_config->id_holder = 'id'; + $release_action_log_config->title_holder = 'version_number'; + $release_action_log_config->table_name = '#__extensiondistributor_release'; + $release_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Check if release action log config is already in action_log_config DB. + $query = $db->getQuery(true); + $query->select($db->quoteName(array('id'))); + $query->from($db->quoteName('#__action_log_config')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($release_action_log_config->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $release_action_log_config->id = $db->loadResult(); + $release_action_log_config_Updated = $db->updateObject('#__action_log_config', $release_action_log_config, 'id'); + } + else + { + $release_action_log_config_Inserted = $db->insertObject('#__action_log_config', $release_action_log_config); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the subscription action log config object. + $subscription_action_log_config = new stdClass(); + $subscription_action_log_config->id = null; + $subscription_action_log_config->type_title = 'SUBSCRIPTION'; + $subscription_action_log_config->type_alias = 'com_extensiondistributor.subscription'; + $subscription_action_log_config->id_holder = 'id'; + $subscription_action_log_config->title_holder = 'date_start'; + $subscription_action_log_config->table_name = '#__extensiondistributor_subscription'; + $subscription_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Check if subscription action log config is already in action_log_config DB. + $query = $db->getQuery(true); + $query->select($db->quoteName(array('id'))); + $query->from($db->quoteName('#__action_log_config')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($subscription_action_log_config->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $subscription_action_log_config->id = $db->loadResult(); + $subscription_action_log_config_Updated = $db->updateObject('#__action_log_config', $subscription_action_log_config, 'id'); + } + else + { + $subscription_action_log_config_Inserted = $db->insertObject('#__action_log_config', $subscription_action_log_config); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the key action log config object. + $key_action_log_config = new stdClass(); + $key_action_log_config->id = null; + $key_action_log_config->type_title = 'KEY'; + $key_action_log_config->type_alias = 'com_extensiondistributor.key'; + $key_action_log_config->id_holder = 'id'; + $key_action_log_config->title_holder = 'key'; + $key_action_log_config->table_name = '#__extensiondistributor_key'; + $key_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Check if key action log config is already in action_log_config DB. + $query = $db->getQuery(true); + $query->select($db->quoteName(array('id'))); + $query->from($db->quoteName('#__action_log_config')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($key_action_log_config->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $key_action_log_config->id = $db->loadResult(); + $key_action_log_config_Updated = $db->updateObject('#__action_log_config', $key_action_log_config, 'id'); + } + else + { + $key_action_log_config_Inserted = $db->insertObject('#__action_log_config', $key_action_log_config); + } + + // Set db if not set already. + if (!isset($db)) + { + $db = JFactory::getDbo(); + } + // Create the download action log config object. + $download_action_log_config = new stdClass(); + $download_action_log_config->id = null; + $download_action_log_config->type_title = 'DOWNLOAD'; + $download_action_log_config->type_alias = 'com_extensiondistributor.download'; + $download_action_log_config->id_holder = 'id'; + $download_action_log_config->title_holder = 'id'; + $download_action_log_config->table_name = '#__extensiondistributor_download'; + $download_action_log_config->text_prefix = 'COM_EXTENSIONDISTRIBUTOR'; + + // Check if download action log config is already in action_log_config DB. + $query = $db->getQuery(true); + $query->select($db->quoteName(array('id'))); + $query->from($db->quoteName('#__action_log_config')); + $query->where($db->quoteName('type_alias') . ' LIKE '. $db->quote($download_action_log_config->type_alias)); + $db->setQuery($query); + $db->execute(); + + // Set the object into the content types table. + if ($db->getNumRows()) + { + $download_action_log_config->id = $db->loadResult(); + $download_action_log_config_Updated = $db->updateObject('#__action_log_config', $download_action_log_config, 'id'); + } + else + { + $download_action_log_config_Inserted = $db->insertObject('#__action_log_config', $download_action_log_config); + } + } + return true; + } + + /** + * Remove folders with files + * + * @param string $dir The path to folder to remove + * @param boolean $ignore The folders and files to ignore and not remove + * + * @return boolean True in all is removed + * + */ + protected function removeFolder($dir, $ignore = false) + { + if (Folder::exists($dir)) + { + $it = new RecursiveDirectoryIterator($dir); + $it = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST); + // remove ending / + $dir = rtrim($dir, '/'); + // now loop the files & folders + foreach ($it as $file) + { + if ('.' === $file->getBasename() || '..' === $file->getBasename()) continue; + // set file dir + $file_dir = $file->getPathname(); + // check if this is a dir or a file + if ($file->isDir()) + { + $keeper = false; + if ($this->checkArray($ignore)) + { + foreach ($ignore as $keep) + { + if (strpos($file_dir, $dir.'/'.$keep) !== false) + { + $keeper = true; + } + } + } + if ($keeper) + { + continue; + } + Folder::delete($file_dir); + } + else + { + $keeper = false; + if ($this->checkArray($ignore)) + { + foreach ($ignore as $keep) + { + if (strpos($file_dir, $dir.'/'.$keep) !== false) + { + $keeper = true; + } + } + } + if ($keeper) + { + continue; + } + File::delete($file_dir); + } + } + // delete the root folder if not ignore found + if (!$this->checkArray($ignore)) + { + return Folder::delete($dir); + } + return true; + } + return false; + } + + /** + * Check if have an array with a length + * + * @input array The array to check + * + * @returns bool/int number of items in array on success + */ + protected function checkArray($array, $removeEmptyString = false) + { + if (isset($array) && is_array($array) && ($nr = count((array)$array)) > 0) + { + // also make sure the empty strings are removed + if ($removeEmptyString) + { + foreach ($array as $key => $string) + { + if (empty($string)) + { + unset($array[$key]); + } + } + return $this->checkArray($array, false); + } + return $nr; + } + return false; + } +} diff --git a/site/assets/css/index.html b/site/assets/css/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/assets/css/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/assets/css/keys.css b/site/assets/css/keys.css new file mode 100644 index 0000000..1ce441a --- /dev/null +++ b/site/assets/css/keys.css @@ -0,0 +1,22 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage keys.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + diff --git a/site/assets/css/package.css b/site/assets/css/package.css new file mode 100644 index 0000000..3d5c061 --- /dev/null +++ b/site/assets/css/package.css @@ -0,0 +1,22 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage package.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ + + diff --git a/site/assets/css/site.css b/site/assets/css/site.css new file mode 100644 index 0000000..170f65d --- /dev/null +++ b/site/assets/css/site.css @@ -0,0 +1,24 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage site.css + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* CSS Document */ +.no-click { + pointer-events: none; +} + diff --git a/site/assets/images/index.html b/site/assets/images/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/assets/images/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/assets/index.html b/site/assets/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/assets/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/assets/js/index.html b/site/assets/js/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/assets/js/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/assets/js/site.js b/site/assets/js/site.js new file mode 100644 index 0000000..9a503bf --- /dev/null +++ b/site/assets/js/site.js @@ -0,0 +1,20 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage site.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +/* JS Document */ diff --git a/site/controller.php b/site/controller.php new file mode 100644 index 0000000..7214d2b --- /dev/null +++ b/site/controller.php @@ -0,0 +1,132 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Component Base Controller + */ +class ExtensiondistributorController extends BaseController +{ + /** + * Method to display a view. + * + * @param boolean $cachable If true, the view output will be cached. + * @param boolean $urlparams An array of safe URL parameters and their variable types, for valid values see {@link JFilterInput::clean()}. + * + * @return JController This object to support chaining. + * + */ + function display($cachable = false, $urlparams = false) + { + // set default view if not set + $view = $this->input->getCmd('view', 'keys'); + $this->input->set('view', $view); + $isEdit = $this->checkEditView($view); + $layout = $this->input->get('layout', null, 'WORD'); + $id = $this->input->getInt('id'); + // $cachable = true; (TODO) working on a fix [gh-238](https://github.com/vdm-io/Joomla-Component-Builder/issues/238) + + // insure that the view is not cashable if edit view or if user is logged in + $user = JFactory::getUser(); + if ($user->get('id') || $isEdit) + { + $cachable = false; + } + + // Check for edit form. + if($isEdit) + { + if ($layout == 'edit' && !$this->checkEditId('com_extensiondistributor.edit.'.$view, $id)) + { + // Somehow the person just went to the form - we don't allow that. + $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $id)); + $this->setMessage($this->getError(), 'error'); + // check if item was opend from other then its own list view + $ref = $this->input->getCmd('ref', 0); + $refid = $this->input->getInt('refid', 0); + // set redirect + if ($refid > 0 && ExtensiondistributorHelper::checkString($ref)) + { + // redirect to item of ref + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view='.(string)$ref.'&layout=edit&id='.(int)$refid, false)); + } + elseif (ExtensiondistributorHelper::checkString($ref)) + { + // redirect to ref + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view='.(string)$ref, false)); + } + else + { + // normal redirect back to the list default site view + $this->setRedirect(JRoute::_('index.php?option=com_extensiondistributor&view=keys', false)); + } + return false; + } + } + + // we may need to make this more dynamic in the future. (TODO) + $safeurlparams = array( + 'catid' => 'INT', + 'id' => 'INT', + 'cid' => 'ARRAY', + 'year' => 'INT', + 'month' => 'INT', + 'limit' => 'UINT', + 'limitstart' => 'UINT', + 'showall' => 'INT', + 'return' => 'BASE64', + 'filter' => 'STRING', + 'filter_order' => 'CMD', + 'filter_order_Dir' => 'CMD', + 'filter-search' => 'STRING', + 'print' => 'BOOLEAN', + 'lang' => 'CMD', + 'Itemid' => 'INT'); + + // should these not merge? + if (ExtensiondistributorHelper::checkArray($urlparams)) + { + $safeurlparams = ExtensiondistributorHelper::mergeArrays(array($urlparams, $safeurlparams)); + } + + return parent::display($cachable, $safeurlparams); + } + + protected function checkEditView($view) + { + if (ExtensiondistributorHelper::checkString($view)) + { + $views = array( + + ); + // check if this is a edit view + if (in_array($view,$views)) + { + return true; + } + } + return false; + } +} diff --git a/site/controllers/index.html b/site/controllers/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/controllers/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/controllers/package.php b/site/controllers/package.php new file mode 100644 index 0000000..a36f9f6 --- /dev/null +++ b/site/controllers/package.php @@ -0,0 +1,198 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Controller\FormController; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Package Form Controller + */ +class ExtensiondistributorControllerPackage extends FormController +{ + /** + * Current or most recently performed task. + * + * @var string + * @since 12.2 + * @note Replaces _task. + */ + protected $task; + + public function __construct($config = array()) + { + $this->view_list = 'keys'; // safeguard for setting the return view listing to the default site view. + parent::__construct($config); + } + + public function update() + { + $app = JFactory::getApplication(); + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $releaseId = $app->input->get('release', null, 'int'); + $key = $app->input->get('key'); + $domain = $app->input->get('domain', null, 'base64'); + $domain = base64_decode($domain); + $ip = $app->input->get('ip', null, 'base64'); + $ip = base64_decode($ip); + + $this->setRedirect( + JRoute::_( + JUri::root(), false + ) + ); + + // We check if a release ID was provided or we get the latest one + if ($releaseId) { + // We need to identify which type of release it is extension or package + $release = ExtensiondistributorHelper::getReleaseFile($releaseId, $key); + if ($release['file'] === false) { + echo $release['msg']; + return false; + } else { + $filename = $release['msg']; + } + } else { + $app->enqueueMessage('We could not identify your request','error'); + return false; + } + + $folder = $cparams->get('releases_directory'); + $folder = JPath::clean(JPATH_SITE . '/' . $folder . '/'); + + $file = $folder . $filename; + if (!JFile::exists($file)) { + $app->enqueueMessage(sprintf('The file %s is not available', $filename),'error'); + return false; + } + $size = filesize($file); + + // All checks has been done we log the download + $db = JFactory::getDbo(); + $log = new stdClass(); + $log->transaction = 'Joomla'; + $log->package = ExtensiondistributorHelper::getVar('release',$releaseId,'id','package'); + $log->user = ExtensiondistributorHelper::getVar('key',$key,'key','user'); + $log->domain = $domain; + $log->ip = $ip; + $log->key = $key; + $log->created = JFactory::getDate()->toSql(); + $log->created_by = $log->user; + $db->insertObject('#__extensiondistributor_download',$log); + + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header("Content-Transfer-Encoding: Binary"); + header('Content-Disposition: attachment; filename="'.$filename.'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Pragma: public'); + header('Content-Length: ' . $size); + ob_clean(); + flush(); + readfile($file); + + jexit(); + } + + public function check() + { + $app = JFactory::getApplication(); + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $releaseId = $app->input->get('release', null, 'int'); + $key = $app->input->get('key'); + + $release = ExtensiondistributorHelper::getReleaseFile($releaseId, $key); + + header('Content-Type: application/json'); + echo json_encode($release); + $app->close(); + jexit(); + } + + + /** + * Method to check if you can edit an existing record. + * + * Extended classes can override this if necessary. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key; default is id. + * + * @return boolean + * + * @since 12.2 + */ + protected function allowEdit($data = array(), $key = 'id') + { + // to insure no other tampering + return false; + } + + /** + * Method override to check if you can add a new record. + * + * @param array $data An array of input data. + * + * @return boolean + * + * @since 1.6 + */ + protected function allowAdd($data = array()) + { + // to insure no other tampering + return false; + } + + /** + * Method to check if you can save a new or existing record. + * + * Extended classes can override this if necessary. + * + * @param array $data An array of input data. + * @param string $key The name of the key for the primary key. + * + * @return boolean + * + * @since 12.2 + */ + protected function allowSave($data, $key = 'id') + { + // to insure no other tampering + return false; + } + + /** + * Function that allows child controller access to model data + * after the data has been saved. + * + * @param JModelLegacy $model The data model object. + * @param array $validData The validated data. + * + * @return void + * + * @since 12.2 + */ + protected function postSaveHook(JModelLegacy $model, $validData = array()) + { + } +} diff --git a/site/extensiondistributor.php b/site/extensiondistributor.php new file mode 100644 index 0000000..1036e30 --- /dev/null +++ b/site/extensiondistributor.php @@ -0,0 +1,42 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + + + +// Set the component css/js +$document = JFactory::getDocument(); +$document->addStyleSheet('components/com_extensiondistributor/assets/css/site.css'); +$document->addScript('components/com_extensiondistributor/assets/js/site.js'); + +// Require helper files +JLoader::register('ExtensiondistributorHelper', __DIR__ . '/helpers/extensiondistributor.php'); +JLoader::register('ExtensiondistributorHelperRoute', __DIR__ . '/helpers/route.php'); + +// Get an instance of the controller prefixed by Extensiondistributor +$controller = JControllerLegacy::getInstance('Extensiondistributor'); + +// Perform the request task +$controller->execute(JFactory::getApplication()->input->get('task')); + +// Redirect if set by the controller +$controller->redirect(); diff --git a/site/helpers/category.php b/site/helpers/category.php new file mode 100644 index 0000000..46d5347 --- /dev/null +++ b/site/helpers/category.php @@ -0,0 +1,37 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +/** + * Extensiondistributor Component Category Tree + */ + +//Insure this view category file is loaded. +$classname = 'ExtensiondistributorExtensionCategories'; +if (!class_exists($classname)) +{ + $path = JPATH_SITE . '/components/com_extensiondistributor/helpers/categoryextension.php'; + if (is_file($path)) + { + include_once $path; + } +} diff --git a/site/helpers/categoryextension.php b/site/helpers/categoryextension.php new file mode 100644 index 0000000..dc3f959 --- /dev/null +++ b/site/helpers/categoryextension.php @@ -0,0 +1,42 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +/** + * Extensiondistributor Extension Component Category Tree + */ +class ExtensiondistributorExtensionCategories extends JCategories +{ + /** + * Class constructor + * + * @param array $options Array of options + * + */ + public function __construct($options = array()) + { + $options['table'] = '#__extensiondistributor_extension'; + $options['extension'] = 'com_extensiondistributor.extension'; + + parent::__construct($options); + } +} diff --git a/site/helpers/extensiondistributor.php b/site/helpers/extensiondistributor.php new file mode 100644 index 0000000..ec2d619 --- /dev/null +++ b/site/helpers/extensiondistributor.php @@ -0,0 +1,2263 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\Filesystem\File; +use Joomla\CMS\Language\Language; +use Joomla\Registry\Registry; +use Joomla\String\StringHelper; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor component helper + */ +abstract class ExtensiondistributorHelper +{ + /** + * Composer Switch + * + * @var array + */ + protected static $composer = array(); + + /** + * The Main Active Language + * + * @var string + */ + public static $langTag; + + public static function getReleases($id, $view = 'extension', $latest = false) + { + if (!$id) { + return null; + } + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName( + array('id','version_number','release_date','reltype','stability','file','extension','package') + )) + ->from($db->quoteName('#__extensiondistributor_release')) + ->where( + array( + $db->quoteName($view) . ' = ' . (int)$id, + $db->quoteName('published') . ' = 1' + ) + ) + ->order('INET_ATON(version_number) DESC') + ; + $db->setQuery($query); + + return $latest ? $db->loadObject() : $db->loadObjectList(); + } + + public static function getPackageContent($packageId): array + { + $package = self::getPackageObject($packageId); + // We first get the extensions of the package + $extensions = $package->files; + $extensions = json_decode($extensions,true); + + $files = array(); + + foreach ($extensions as &$extension) + { + $data = self::getReleases($extension['extension'],'extension',true); + $details = self::getExtensionObject($extension['extension']); + $extension['details'] = $details; + $extension['release'] = $data; + if (isset($data->file)) { + $files[$data->id] = $data->file; + } + } + + return array('extensions' => $extensions, 'files' => $files, 'package' => $package); + } + + public static function createPackage($packageId,$release) + { + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $content = self::getPackageContent($packageId); + $package = $content['package']; + $extensions = $content['extensions']; + $files = $content['files']; + $lists = self::getApplicationLists(); + $releaseFolder = JPATH_SITE . '/' . $cparams->get('releases_directory'); + $tmpFolder = JPATH_SITE . '/tmp/' . $package->packagename; + $update_path = $cparams->get('updates_directory','updates'); + $update_file = $cparams->get('updateserver_url', JUri::root()).$update_path.'/'.$package->update; + + // We first create the temporary folder + JFolder::create($tmpFolder); + // We create the packages folder and copy the files inside + JFolder::create($tmpFolder . '/packages'); + foreach ($files as $file) { + JFile::copy($releaseFolder . '/' . $file, $tmpFolder . '/packages/' . $file); + } + + $zipFilename = self::createPackageFileName($package, $release->version_number, $release->stability); + + $xmldoc = new DOMDocument(); + + $xmldoc->encoding = 'UTF-8'; + $xmldoc->xmlVersion = '1.0'; + $xmldoc->formatOutput = true; + + $xml_file_name = $tmpFolder .'/pkg_'.$package->packagename.'.xml'; + + // Extension + $root = $xmldoc->createElement('extension'); + $root->setAttributeNode(new DOMAttr('type', 'package')); + $root->setAttributeNode(new DOMAttr('version', '3.8')); + $root->setAttributeNode(new DOMAttr('method', 'upgrade')); + + $root->appendChild($xmldoc->createElement('name', $package->name)); + $root->appendChild($xmldoc->createElement('version', $release->version_number)); // => release + $root->appendChild($xmldoc->createElement('author', $package->author)); + $root->appendChild($xmldoc->createElement('authorUrl', $package->authorurl)); + $root->appendChild($xmldoc->createElement('authorEmail', $package->authoremail)); + $root->appendChild($xmldoc->createElement('creationDate', JHtml::date($release->release_date,'M Y'))); // => release + $root->appendChild($xmldoc->createElement('packagename', $package->packagename)); + $root->appendChild($xmldoc->createElement('packager', $package->packager)); + $root->appendChild($xmldoc->createElement('packagerurl', $package->packagerurl)); + $root->appendChild($xmldoc->createElement('copyright', $package->copyright)); + $root->appendChild($xmldoc->createElement('license', $package->license)); + $root->appendChild($xmldoc->createElement('description'))->appendChild($xmldoc->createCDATASection($package->description)); + $root->appendChild($xmldoc->createElement('scriptfile', 'script.php')); // => dynamic A venir + if ($package->blockchilduninstall) { + $root->appendChild($xmldoc->createElement('blockChildUninstall', 'true')); + } + + $files_node = $xmldoc->createElement('files'); + $files_node->setAttributeNode(new DOMAttr('folder', 'packages')); + + foreach ($extensions as $extension) + { + switch ($extension['details']->type) + { + case 'component' : + case 'library' : + $child_node_file = $xmldoc->createElement('file', $extension['release']->file); + $child_node_file->setAttributeNode(new DOMAttr('type', $extension['details']->type)); + $child_node_file->setAttributeNode(new DOMAttr('id', $extension['details']->element)); + $files_node->appendChild($child_node_file); + break; + case 'module' : + case 'template' : + case 'language' : + $child_node_file = $xmldoc->createElement('file', $extension['release']->file); + $child_node_file->setAttributeNode(new DOMAttr('type', $extension['details']->type)); + $child_node_file->setAttributeNode(new DOMAttr('id', $extension['details']->element)); + $child_node_file->setAttributeNode(new DOMAttr('client', $extension['release']->client)); + $files_node->appendChild($child_node_file); + break; + case 'plugin' : + $child_node_file = $xmldoc->createElement('file', $extension['release']->file); + $child_node_file->setAttributeNode(new DOMAttr('type', $extension['details']->type)); + $child_node_file->setAttributeNode(new DOMAttr('id', $extension['details']->element)); + $child_node_file->setAttributeNode(new DOMAttr('group', $extension['details']->group)); + $files_node->appendChild($child_node_file); + break; + } + } + + $root->appendChild($files_node); + + $updateservers_node = $xmldoc->createElement('updateservers'); + $child_node_server = $xmldoc->createElement('server', $update_file); + $child_node_server->setAttributeNode(new DOMAttr('type', 'extension')); + $child_node_server->setAttributeNode(new DOMAttr('priority', '1')); + $child_node_server->setAttributeNode(new DOMAttr('name', $cparams->get('server_name'))); + $updateservers_node->appendChild($child_node_server); + $root->appendChild($updateservers_node); + + $xmldoc->appendChild($root); + $xmldoc->save($xml_file_name); + + // We create the script file + self::createScriptFile($packageId, $tmpFolder); + + // We zip everything in a package + self::zipPackage($releaseFolder.'/'.$zipFilename,$tmpFolder); + + JFolder::delete($tmpFolder); + + return $zipFilename; + } + + public static function zipPackage($zipFile, $zipDir) + { + $zip = new ZipArchive(); + $zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE); + + $files = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($zipDir), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $name => $file) + { + // Skip directories (they would be added automatically) + if (!$file->isDir()) + { + // Get real and relative path for current file + $filePath = $file->getRealPath(); + $relativePath = substr($filePath, strlen($zipDir) + 1); + + // Add current file to archive + $zip->addFile($filePath, $relativePath); + } + } + + $zip->close(); + } + + protected static $tabSpacerBucket = array(); + + protected static $tabSpacer = "\t"; + + protected static $debugLinenr = false; + + public static function _t($nr) + { + // check if we already have the string + if (!isset(self::$tabSpacerBucket[$nr])) + { + // get the string + self::$tabSpacerBucket[$nr] = str_repeat(self::$tabSpacer, (int) $nr); + } + // return stored string + return self::$tabSpacerBucket[$nr]; + } + + public static function getExtensionInstallClass(&$extension) + { + // yes we are adding it + $script = PHP_EOL . '/**'; + $script .= PHP_EOL . ' * ' . $extension->official_name + . ' script file.'; + $script .= PHP_EOL . ' *'; + $script .= PHP_EOL . ' * @package ' . $extension->class_name; + $script .= PHP_EOL . ' */'; + $script .= PHP_EOL . 'class ' . $extension->installer_class_name; + $script .= PHP_EOL . '{'; + // set constructor + if (isset($extension->add_php_script_construct) + && $extension->add_php_script_construct == 1 + && self::checkString( + $extension->php_script_construct + )) + { + $script .= self::setInstallMethodScript( + 'construct', $extension->php_script_construct + ); + } + // add PHP in extension install + $addScriptMethods = array('php_preflight', 'php_postflight', + 'php_method'); + $addScriptTypes = array('install', 'update', 'uninstall', + 'discover_install'); + // set some buckets for sorting + $function_install = array(); + $function_update = array(); + $function_uninstall = array(); + $has_php_preflight = false; + $function_php_preflight = array('install' => array(), + 'uninstall' => array(), + 'discover_install' => array(), + 'update' => array()); + $has_php_postflight = false; + $function_php_postflight = array('install' => array(), + 'uninstall' => array(), + 'discover_install' => array(), + 'update' => array()); + // the function sorter + foreach ($addScriptMethods as $scriptMethod) + { + foreach ($addScriptTypes as $scriptType) + { + if (isset( + $extension->{'add_' . $scriptMethod . '_' . $scriptType} + ) + && $extension->{'add_' . $scriptMethod . '_' . $scriptType} + == 1 + && self::checkString( + $extension->{$scriptMethod . '_' . $scriptType} + )) + { + // add to the main methods + if ('php_method' === $scriptMethod) + { + ${'function_' . $scriptType}[] + = $extension->{$scriptMethod . '_' . $scriptType}; + } + else + { + ${'function_' . $scriptMethod}[$scriptType][] + = $extension->{$scriptMethod + . '_' . $scriptType}; + ${'has_' . $scriptMethod} = true; + } + } + } + } + // now add the install script. + if (self::checkArray($function_install)) + { + $script .= self::setInstallMethodScript( + 'install', $function_install + ); + } + // now add the update script. + if (self::checkArray($function_update)) + { + $script .= self::setInstallMethodScript( + 'update', $function_update + ); + } + // now add the uninstall script. + if (self::checkArray($function_uninstall)) + { + $script .= self::setInstallMethodScript( + 'uninstall', $function_uninstall + ); + } + // now add the preflight script. + if ($has_php_preflight) + { + $script .= self::setInstallMethodScript( + 'preflight', $function_php_preflight + ); + } + // now add the postflight script. + if ($has_php_postflight) + { + $script .= self::setInstallMethodScript( + 'postflight', $function_php_postflight + ); + } + $script .= PHP_EOL . '}' . PHP_EOL; + + return $script; + } + + public static function setInstallMethodScript($function_name, &$scripts) + { + $script = ''; + // build function + switch ($function_name) + { + case 'install': + case 'update': + case 'uninstall': + // the main function types + $script = PHP_EOL . PHP_EOL . self::_t(1) . '/**'; + $script .= PHP_EOL . self::_t(1) . ' * Called on ' + . $function_name; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @param JAdapterInstance $adapter The object responsible for running this script'; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @return boolean True on success'; + $script .= PHP_EOL . self::_t(1) . ' */'; + $script .= PHP_EOL . self::_t(1) . 'public function ' + . $function_name . '(JAdapterInstance $adapter)'; + $script .= PHP_EOL . self::_t(1) . '{'; + $script .= PHP_EOL . implode(PHP_EOL . PHP_EOL, $scripts); + // Added the HTML message + $script .= PHP_EOL . '// Custom HTML message uninstall'; + // return true + if ('uninstall' !== $function_name) + { + $script .= PHP_EOL . self::_t(2) . 'return true;'; + } + break; + case 'preflight': + case 'postflight': + // the pre/post function types + $script = PHP_EOL . PHP_EOL . self::_t(1) . '/**'; + $script .= PHP_EOL . self::_t(1) + . ' * Called before any type of action'; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @param string $route Which action is happening (install|uninstall|discover_install|update)'; + $script .= PHP_EOL . self::_t(1) + . ' * @param JAdapterInstance $adapter The object responsible for running this script'; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @return boolean True on success'; + $script .= PHP_EOL . self::_t(1) . ' */'; + $script .= PHP_EOL . self::_t(1) . 'public function ' + . $function_name . '($route, JAdapterInstance $adapter)'; + $script .= PHP_EOL . self::_t(1) . '{'; + $script .= PHP_EOL . self::_t(2) . '//' . self::setLine( + __LINE__ + ) . ' get application'; + $script .= PHP_EOL . self::_t(2) + . '$app = JFactory::getApplication();' . PHP_EOL; + // add the default version check (TODO) must make this dynamic + if ('preflight' === $function_name) + { + $script .= PHP_EOL . self::_t(2) . '//' . self::setLine( + __LINE__ + ) . ' the default for both install and update'; + $script .= PHP_EOL . self::_t(2) + . '$jversion = new JVersion();'; + $script .= PHP_EOL . self::_t(2) + . "if (!\$jversion->isCompatible('3.8.0'))"; + $script .= PHP_EOL . self::_t(2) . '{'; + $script .= PHP_EOL . self::_t(3) + . "\$app->enqueueMessage('Please upgrade to at least Joomla! 3.8.0 before continuing!', 'error');"; + $script .= PHP_EOL . self::_t(3) . 'return false;'; + $script .= PHP_EOL . self::_t(2) . '}' . PHP_EOL; + } + // now add the scripts + foreach ($scripts as $route => $_script) + { + if (self::checkArray($_script)) + { + // set the if and script + $script .= PHP_EOL . self::_t(2) . "if ('" . $route + . "' === \$route)"; + $script .= PHP_EOL . self::_t(2) . '{'; + $script .= PHP_EOL . self::_t(3) . implode( + PHP_EOL, $_script + ); + // Ajouté pour la gestion du block custom html + if ($route === 'install') { + $script .= PHP_EOL . '// Custom HTML message install'; + } elseif ($route === 'update') { + $script .= PHP_EOL . '// Custom HTML message update'; + } elseif ($route === 'uninstall') { + $script .= PHP_EOL . '// Custom HTML message uninstall'; + } + // Fin de l'ajout + $script .= PHP_EOL . self::_t(2) . '}' . PHP_EOL; + } + } + // return true + $script .= PHP_EOL . self::_t(2) . 'return true;'; + break; + case 'construct': + // the __construct script + $script = PHP_EOL . PHP_EOL . self::_t(1) . '/**'; + $script .= PHP_EOL . self::_t(1) . ' * Constructor'; + $script .= PHP_EOL . self::_t(1) . ' *'; + $script .= PHP_EOL . self::_t(1) + . ' * @param JAdapterInstance $adapter The object responsible for running this script'; + $script .= PHP_EOL . self::_t(1) . ' */'; + $script .= PHP_EOL . self::_t(1) + . 'public function __construct(JAdapterInstance $adapter)'; + $script .= PHP_EOL . self::_t(1) . '{'; + $script .= PHP_EOL . $scripts; + break; + default: + // oops error + return ''; + } + // close the function + $script .= PHP_EOL . self::_t(1) . '}'; + + return $script; + } + + private static function setLine($nr) + { + if (self::$debugLinenr) + { + return ' [Interpretation ' . $nr . ']'; + } + + return ''; + } + + public static function createScriptFile($packageId, $path) + { + $extension = self::getPackageObject(1); + $extension->official_name = $extension->name; + $extension->class_name = 'Pkg_'.$extension->packagename; + $extension->installer_class_name = $extension->class_name . 'InstallerScript'; + $extension->php_method_uninstall = base64_decode($extension->php_method_uninstall); + $extension->php_postflight_install = base64_decode($extension->php_postflight_install); + $extension->php_postflight_update = base64_decode($extension->php_postflight_update); + $extension->php_preflight_install = base64_decode($extension->php_preflight_install); + $extension->php_preflight_uninstall = base64_decode($extension->php_preflight_uninstall); + $extension->php_preflight_update = base64_decode($extension->php_preflight_update); + $script = self::getExtensionInstallClass($extension); + $file = $path . '/script.php'; + file_put_contents($file, "getQuery(true); + + $query + ->select($db->quoteName( + array( + 'id', + 'alias', + 'catid', + 'client', + 'description', + 'element', + 'group', + 'image', + 'keepfilename', + 'name', + 'newfilename', + 'release_date', + 'type', + 'update', + 'version_number', + 'published' + ) + )) + ->from($db->quoteName('#__extensiondistributor_extension')) + ->where($db->quoteName('id') . ' = ' . (int)$extension) + ; + $db->setQuery($query); + + return $db->loadObject(); + } + + public static function getPackageObject($package) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select('*') + ->from($db->quoteName('#__extensiondistributor_package')) + ->where($db->quoteName('id') . ' = ' . (int)$package) + ; + $db->setQuery($query); + + return $db->loadObject(); + } + + public static function isExtension($type,$element,$group,$client) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName('id')) + ->from($db->quoteName('#__extensiondistributor_extension')) + ->where( + array( + $db->quoteName('type') . ' = ' . $db->quote($type), + $db->quoteName('element') . ' = ' . $db->quote($element), + $db->quoteName('group') . ' = ' . $db->quote($group), + $db->quoteName('client') . ' = ' . $db->quote($client) + ) + ) + ; + $db->setQuery($query); + + return $db->loadResult(); + } + + public static function extensionsLastId() + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName('id')) + ->from($db->quoteName('#__extensiondistributor_extension')) + ->order('id DESC') + ; + $db->setQuery($query,0,1); + + return $db->loadResult(); + } + + public static function isRelease($extensionId,$version) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName('id')) + ->from($db->quoteName('#__extensiondistributor_release')) + ->where( + array( + $db->quoteName('extension') . ' = ' . (int)$extensionId, + $db->quoteName('version_number') . ' = ' . $db->quote($version) + ) + ) + ; + $db->setQuery($query); + + return $db->loadResult(); + } + + public static function getApplicationLists(): array + { + $lists = array(); + + $lists['stability'] = array( + '0' => array( + 'label' => 'Development', + 'color' => 'default' + ), + '1' => array( + 'label' => 'Alpha', + 'color' => 'important' + ), + '2' => array( + 'label' => 'Beta', + 'color' => 'warning' + ), + '3' => array( + 'label' => 'RC', + 'color' => 'warning' + ), + '4' => array( + 'label' => 'Stable', + 'color' => 'success' + ) + ); + + $lists['extensionTypes'] = array( + 'component' => 'com', + 'plugin' => 'plg', + 'module' => 'mod', + 'library' => 'lib', + 'template' => 'tpl', + 'language' => 'lang', + 'file' => 'file' + ); + + return $lists; + } + + public static function createBadge($id, $name, $type, $group = '', $client = ''): string + { + $tooltip = ''; + $tooltip .= ''; + $tooltip .= ucfirst($type); + $tooltip .= ' ' . $group; + $tooltip .= ' ' . $client; + $tooltip .= ''; + $tooltip .= '
      '; + $tooltip .= ""; + $tooltip .= $name; + $return = base64_encode(urlencode(JUri::getInstance()->toString())); + + $bagde = ''; + $bagde .= ''; + $bagde .= strtoupper(substr($type,0,1)); + $bagde .= ''; + if (!empty($group)) { + $bagde .= strtolower(substr($group,0,1)); + } + if (!empty($client)) { + $bagde .= strtolower(substr($client,0,1)); + } + $bagde .= ''; + $bagde .= ''; + $bagde .= ''; + + return $bagde; + } + + public static function getReleaseToFile() + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName(array('id','file'))) + ->from($db->quoteName('#__extensiondistributor_release')) + ; + $db->setQuery($query); + + return $db->loadAssocList('id','file'); + } + + public static function createExtensionFileName($extension, $version, $stability, $file) + { + if ($extension->keepfilename) { // if we keep the filename no need to waste more time + return $file; + } + + $lists = self::getApplicationLists(); + $extensionTypes = $lists['extensionTypes']; + $stabilityList = $lists['stability']; + $pattern = $extension->newfilename; + $version = explode('.',$version); + $replacements = array( + 'type' => $extensionTypes[$extension->type], + 'element' => str_replace(array('com_','mod_'),'',$extension->element), + 'client' => $extension->client, + '_client' => $extension->client ? '_'.$extension->client : '', // Find a cleaner solution after + 'group' => $extension->group, + '_group' => $extension->group ? '_'.$extension->group : '', // Find a cleaner solution after + 'stability' => $stabilityList[$stability]['label'], + 'major' => $version[0], + 'minor' => $version[1], + 'release' => $version[2] + ); + foreach ($replacements as $k => $v) { + $pattern = str_replace('{'.$k.'}',$v,$pattern); + } + + return strtolower($pattern); + } + + public static function createPackageFileName($package, $version, $stability) + { + $lists = self::getApplicationLists(); + $stabilityList = $lists['stability']; + $pattern = $package->newfilename; + $version = explode('.',$version); + $replacements = array( + 'type' => 'pkg', + 'packagename' => $package->packagename, + 'stability' => $stabilityList[$stability]['label'], + 'major' => $version[0], + 'minor' => $version[1], + 'release' => $version[2] + ); + foreach ($replacements as $k => $v) { + $pattern = str_replace('{'.$k.'}',$v,$pattern); + } + + return strtolower($pattern); + } + + public static function getXmlVersions($xml_file) + { + $updates = simplexml_load_file($xml_file); + $versions = array(); + foreach ($updates->children() as $update) { + $versions[] = $update->version; + } + return $versions; + } + + public static function getReleaseDetails($releaseId) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName( + array('r.id','r.version_number','r.release_date','r.reltype','r.stability','r.file','r.extension','r.package'), + array('r_id','r_version_number','r_release_date','r_reltype','r_stability','r_file','r_extension','r_package') + )) + ->select('p.*') + ->join('LEFT','#__extensiondistributor_package AS p ON p.id = r.package') + ->from($db->quoteName('#__extensiondistributor_release','r')) + ->where($db->quoteName('r.id') . ' = ' . (int)$releaseId) + ; + $db->setQuery($query); + + return $db->loadObject(); + } + + public static function getReleaseFile($releaseId, $key = null): array + { + $file = true; + $msg = ''; + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + $cparams = JComponentHelper::getParams('com_extensiondistributor'); + $folder = $cparams->get('releases_directory'); + $folder = JPath::clean(JPATH_SITE . '/' . $folder . '/'); + + $query + ->clear() + ->select($db->quoteName( + array('id','file','extension','package','published') + )) + ->from($db->quoteName('#__extensiondistributor_release')) + ->where($db->quoteName('id') . ' = ' . (int)$releaseId) + ; + $db->setQuery($query); + $release = $db->loadObject(); + + if (is_null($release) || !$release) { + $file = false; + $msg = 'The requested file does not exist'; + return array('file' => $file, 'msg' => $msg); + } + if ($release->published != 1) { + $file = false; + $msg = 'The requested file is not published'; + return array('file' => $file, 'msg' => $msg); + } + + if ($release->extension > 0) { + // For extensions we do not check the key + $file = false; + $msg = 'The individual extension download is not authorized'; + return array('file' => $file, 'msg' => $msg); + } + + if ($release->package > 0) { + $latest = self::getPackageObject($release->package); + if (is_null($latest) || !$latest) { + $file = false; + $msg = 'The requested package is not available anymore'; + return array('file' => $file, 'msg' => $msg); + } + if (!$latest->free) { + if (is_null($key)) { + $file = false; + $msg = 'This is a commercial extension you need to provide a download key'; + return array('file' => $file, 'msg' => $msg); + } + $userId = self::getVar('key',$key,'key','user'); + if (!$userId) { + $file = false; + $msg = 'The download key you provided does not exists'; + return array('file' => $file, 'msg' => $msg); + } + $isLegitimate = self::isLegitimate($userId,$release->package); + if (!$isLegitimate) { + $file = false; + $msg = 'Your subscription is not active anymore'; + return array('file' => $file, 'msg' => $msg); + } + } + if (!JFile::exists($folder . $release->file)) { + $file = false; + $msg = sprintf('The file %s is not available', $release->file); + return array('file' => $file, 'msg' => $msg); + } + $file = true; + $msg = $release->file; + return array('file' => $file, 'msg' => $msg); + } + + return array('file' => $file, 'msg' => $msg); + } + + public static function isLegitimate($userId,$packageId): bool + { + $now = JFactory::getDate()->toSql(); + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->clear() + ->select($db->quoteName('id')) + ->from($db->quoteName('#__extensiondistributor_subscription')) + ->where( + array( + $db->quoteName('user') . ' = ' . (int)$userId, + $db->quoteName('active') . ' = 1', + $db->quoteName('date_start') . ' <= ' . $db->quote($now), + $db->quoteName('date_expire') . ' > ' . $db->quote($now) + ) + ) + ->extendWhere('AND', array( + $db->quoteName('package') . ' = ' . (int)$packageId, + $db->quoteName('package') . ' = ' . $db->quote('All') + ),'OR') + ; + $db->setQuery($query); + $result = $db->loadResult(); + + return $db->loadResult() ? true : false; + } + + public static function getRealIpAddr() + { + if (!empty($_SERVER['HTTP_CLIENT_IP'])) { + // Check IP from internet. + $ip = $_SERVER['HTTP_CLIENT_IP']; + } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) ) { + // Check IP is passed from proxy. + $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; + } else { + // Get IP address from remote address. + $ip = $_SERVER['REMOTE_ADDR']; + } + return $ip; + } + + public static function getFileUsage($filename) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + + $query + ->select($db->quoteName( + array('r.id','r.version_number','r.release_date','r.reltype','r.stability','r.file','r.extension','r.package') + )) + ->from($db->quoteName('#__extensiondistributor_release','r')) + ->select($db->quoteName( + array('e.id','e.name','e.element','e.type','e.group','e.client'), + array('e_id','e_name','e_element','e_type','e_group','e_client') + )) + ->join('LEFT','#__extensiondistributor_extension AS e ON r.extension = e.id') + ->select($db->quoteName( + array('p.id','p.packagename','p.name'), + array('p_id','p_packagename','p_name') + )) + ->join('LEFT','#__extensiondistributor_package AS p ON r.package = p.id') + ->where($db->quoteName('r.file') . ' = ' . $db->quote($filename)) + ; + $db->setQuery($query); + + return $db->loadObjectList('id'); + } + + public static function secureFolder($path, $action = 'secure') + { + $destPath = JPath::clean($path); + $srcPath = JPATH_ADMINISTRATOR.'/components/com_extensiondistributor/assets/security/'; + $files = array( + 'htaccess.txt' => '.htaccess', + 'index.htm' => 'index.htm', + 'index.html' => 'index.html', + 'index.php' => 'index.php', + 'web.config' => 'web.config' + ); + foreach ($files as $src => $dest) { + if ($action === 'secure') { + if (JFile::exists($srcPath.$src)) { + JFile::copy($srcPath.$src, $destPath.$dest); + } + } else { + if (JFile::exists($path.$dest)) { + JFile::delete($path.$dest); + } + } + } + } + + + /** + * Load the Composer Vendors + */ + public static function composerAutoload($target) + { + // insure we load the composer vendor only once + if (!isset(self::$composer[$target])) + { + // get the function name + $functionName = self::safeString('compose' . $target); + // check if method exist + if (method_exists(__CLASS__, $functionName)) + { + return self::{$functionName}(); + } + return false; + } + return self::$composer[$target]; + } + + /** + * Convert it into a string + */ + public static function jsonToString($value, $sperator = ", ", $table = null, $id = 'id', $name = 'name') + { + // do some table foot work + $external = false; + if (strpos($table, '#__') !== false) + { + $external = true; + $table = str_replace('#__', '', $table); + } + // check if string is JSON + $result = json_decode($value, true); + if (json_last_error() === JSON_ERROR_NONE) + { + // is JSON + if (self::checkArray($result)) + { + if (self::checkString($table)) + { + $names = array(); + foreach ($result as $val) + { + if ($external) + { + if ($_name = self::getVar(null, $val, $id, $name, '=', $table)) + { + $names[] = $_name; + } + } + else + { + if ($_name = self::getVar($table, $val, $id, $name)) + { + $names[] = $_name; + } + } + } + if (self::checkArray($names)) + { + return (string) implode($sperator,$names); + } + } + return (string) implode($sperator,$result); + } + return (string) json_decode($value); + } + return $value; + } + + /** + * Load the Component xml manifest. + */ + public static function manifest() + { + $manifestUrl = JPATH_ADMINISTRATOR."/components/com_extensiondistributor/extensiondistributor.xml"; + return simplexml_load_file($manifestUrl); + } + + /** + * Joomla version object + */ + protected static $JVersion; + + /** + * set/get Joomla version + */ + public static function jVersion() + { + // check if set + if (!self::checkObject(self::$JVersion)) + { + self::$JVersion = new JVersion(); + } + return self::$JVersion; + } + + /** + * Load the Contributors details. + */ + public static function getContributors() + { + // get params + $params = JComponentHelper::getParams('com_extensiondistributor'); + // start contributors array + $contributors = array(); + // get all Contributors (max 20) + $searchArray = range('0','20'); + foreach($searchArray as $nr) + { + if ((NULL !== $params->get("showContributor".$nr)) && ($params->get("showContributor".$nr) == 2 || $params->get("showContributor".$nr) == 3)) + { + // set link based of selected option + if($params->get("useContributor".$nr) == 1) + { + $link_front = ''; + $link_back = ''; + } + elseif($params->get("useContributor".$nr) == 2) + { + $link_front = ''; + $link_back = ''; + } + else + { + $link_front = ''; + $link_back = ''; + } + $contributors[$nr]['title'] = self::htmlEscape($params->get("titleContributor".$nr)); + $contributors[$nr]['name'] = $link_front.self::htmlEscape($params->get("nameContributor".$nr)).$link_back; + } + } + return $contributors; + } + + /** + * Can be used to build help urls. + **/ + public static function getHelpUrl($view) + { + return false; + } + + /** + * Get any component's model + */ + public static function getModel($name, $path = JPATH_COMPONENT_SITE, $Component = 'Extensiondistributor', $config = array()) + { + // fix the name + $name = self::safeString($name); + // full path to models + $fullPathModels = $path . '/models'; + // load the model file + JModelLegacy::addIncludePath($fullPathModels, $Component . 'Model'); + // make sure the table path is loaded + if (!isset($config['table_path']) || !self::checkString($config['table_path'])) + { + // This is the JCB default path to tables in Joomla 3.x + $config['table_path'] = JPATH_ADMINISTRATOR . '/components/com_' . strtolower($Component) . '/tables'; + } + // get instance + $model = JModelLegacy::getInstance($name, $Component . 'Model', $config); + // if model not found (strange) + if ($model == false) + { + jimport('joomla.filesystem.file'); + // get file path + $filePath = $path . '/' . $name . '.php'; + $fullPathModel = $fullPathModels . '/' . $name . '.php'; + // check if it exists + if (File::exists($filePath)) + { + // get the file + require_once $filePath; + } + elseif (File::exists($fullPathModel)) + { + // get the file + require_once $fullPathModel; + } + // build class names + $modelClass = $Component . 'Model' . $name; + if (class_exists($modelClass)) + { + // initialize the model + return new $modelClass($config); + } + } + return $model; + } + + /** + * Add to asset Table + */ + public static function setAsset($id, $table, $inherit = true) + { + $parent = JTable::getInstance('Asset'); + $parent->loadByName('com_extensiondistributor'); + + $parentId = $parent->id; + $name = 'com_extensiondistributor.'.$table.'.'.$id; + $title = ''; + + $asset = JTable::getInstance('Asset'); + $asset->loadByName($name); + + // Check for an error. + $error = $asset->getError(); + + if ($error) + { + return false; + } + else + { + // Specify how a new or moved node asset is inserted into the tree. + if ($asset->parent_id != $parentId) + { + $asset->setLocation($parentId, 'last-child'); + } + + // Prepare the asset to be stored. + $asset->parent_id = $parentId; + $asset->name = $name; + $asset->title = $title; + // get the default asset rules + $rules = self::getDefaultAssetRules('com_extensiondistributor', $table, $inherit); + if ($rules instanceof JAccessRules) + { + $asset->rules = (string) $rules; + } + + if (!$asset->check() || !$asset->store()) + { + JFactory::getApplication()->enqueueMessage($asset->getError(), 'warning'); + return false; + } + else + { + // Create an asset_id or heal one that is corrupted. + $object = new stdClass(); + + // Must be a valid primary key value. + $object->id = $id; + $object->asset_id = (int) $asset->id; + + // Update their asset_id to link to the asset table. + return JFactory::getDbo()->updateObject('#__extensiondistributor_'.$table, $object, 'id'); + } + } + return false; + } + + /** + * Gets the default asset Rules for a component/view. + */ + protected static function getDefaultAssetRules($component, $view, $inherit = true) + { + // if new or inherited + $assetId = 0; + // Only get the actual item rules if not inheriting + if (!$inherit) + { + // Need to find the asset id by the name of the component. + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__assets')) + ->where($db->quoteName('name') . ' = ' . $db->quote($component)); + $db->setQuery($query); + $db->execute(); + // check that there is a value + if ($db->getNumRows()) + { + // asset already set so use saved rules + $assetId = (int) $db->loadResult(); + } + } + // get asset rules + $result = JAccess::getAssetRules($assetId); + if ($result instanceof JAccessRules) + { + $_result = (string) $result; + $_result = json_decode($_result); + foreach ($_result as $name => &$rule) + { + $v = explode('.', $name); + if ($view !== $v[0]) + { + // remove since it is not part of this view + unset($_result->$name); + } + elseif ($inherit) + { + // clear the value since we inherit + $rule = array(); + } + } + // check if there are any view values remaining + if (count((array) $_result)) + { + $_result = json_encode($_result); + $_result = array($_result); + // Instantiate and return the JAccessRules object for the asset rules. + $rules = new JAccessRules($_result); + // return filtered rules + return $rules; + } + } + return $result; + } + + /** + * xmlAppend + * + * @param SimpleXMLElement $xml The XML element reference in which to inject a comment + * @param mixed $node A SimpleXMLElement node to append to the XML element reference, or a stdClass object containing a comment attribute to be injected before the XML node and a fieldXML attribute containing a SimpleXMLElement + * + * @return null + * + */ + public static function xmlAppend(&$xml, $node) + { + if (!$node) + { + // element was not returned + return; + } + switch (get_class($node)) + { + case 'stdClass': + if (property_exists($node, 'comment')) + { + self::xmlComment($xml, $node->comment); + } + if (property_exists($node, 'fieldXML')) + { + self::xmlAppend($xml, $node->fieldXML); + } + break; + case 'SimpleXMLElement': + $domXML = dom_import_simplexml($xml); + $domNode = dom_import_simplexml($node); + $domXML->appendChild($domXML->ownerDocument->importNode($domNode, true)); + $xml = simplexml_import_dom($domXML); + break; + } + } + + /** + * xmlComment + * + * @param SimpleXMLElement $xml The XML element reference in which to inject a comment + * @param string $comment The comment to inject + * + * @return null + * + */ + public static function xmlComment(&$xml, $comment) + { + $domXML = dom_import_simplexml($xml); + $domComment = new DOMComment($comment); + $nodeTarget = $domXML->ownerDocument->importNode($domComment, true); + $domXML->appendChild($nodeTarget); + $xml = simplexml_import_dom($domXML); + } + + /** + * xmlAddAttributes + * + * @param SimpleXMLElement $xml The XML element reference in which to inject a comment + * @param array $attributes The attributes to apply to the XML element + * + * @return null + * + */ + public static function xmlAddAttributes(&$xml, $attributes = array()) + { + foreach ($attributes as $key => $value) + { + $xml->addAttribute($key, $value); + } + } + + /** + * xmlAddOptions + * + * @param SimpleXMLElement $xml The XML element reference in which to inject a comment + * @param array $options The options to apply to the XML element + * + * @return void + * + */ + public static function xmlAddOptions(&$xml, $options = array()) + { + foreach ($options as $key => $value) + { + $addOption = $xml->addChild('option'); + $addOption->addAttribute('value', $key); + $addOption[] = $value; + } + } + + /** + * get the field object + * + * @param array $attributes The array of attributes + * @param string $default The default of the field + * @param array $options The options to apply to the XML element + * + * @return object + * + */ + public static function getFieldObject(&$attributes, $default = '', $options = null) + { + // make sure we have attributes and a type value + if (self::checkArray($attributes) && isset($attributes['type'])) + { + // make sure the form helper class is loaded + if (!method_exists('JFormHelper', 'loadFieldType')) + { + jimport('joomla.form.form'); + } + // get field type + $field = JFormHelper::loadFieldType($attributes['type'], true); + // get field xml + $XML = self::getFieldXML($attributes, $options); + // setup the field + $field->setup($XML, $default); + // return the field object + return $field; + } + return false; + } + + /** + * get the field xml + * + * @param array $attributes The array of attributes + * @param array $options The options to apply to the XML element + * + * @return object + * + */ + public static function getFieldXML(&$attributes, $options = null) + { + // make sure we have attributes and a type value + if (self::checkArray($attributes)) + { + // start field xml + $XML = new SimpleXMLElement(''); + // load the attributes + self::xmlAddAttributes($XML, $attributes); + // check if we have options + if (self::checkArray($options)) + { + // load the options + self::xmlAddOptions($XML, $options); + } + // return the field xml + return $XML; + } + return false; + } + + /** + * Render Bool Button + * + * @param array $args All the args for the button + * 0) name + * 1) additional (options class) // not used at this time + * 2) default + * 3) yes (name) + * 4) no (name) + * + * @return string The input html of the button + * + */ + public static function renderBoolButton() + { + $args = func_get_args(); + // check if there is additional button class + $additional = isset($args[1]) ? (string) $args[1] : ''; // not used at this time + // button attributes + $buttonAttributes = array( + 'type' => 'radio', + 'name' => isset($args[0]) ? self::htmlEscape($args[0]) : 'bool_button', + 'label' => isset($args[0]) ? self::safeString(self::htmlEscape($args[0]), 'Ww') : 'Bool Button', // not seen anyway + 'class' => 'btn-group', + 'filter' => 'INT', + 'default' => isset($args[2]) ? (int) $args[2] : 0); + // set the button options + $buttonOptions = array( + '1' => isset($args[3]) ? self::htmlEscape($args[3]) : 'JYES', + '0' => isset($args[4]) ? self::htmlEscape($args[4]) : 'JNO'); + // return the input + return self::getFieldObject($buttonAttributes, $buttonAttributes['default'], $buttonOptions)->input; + } + + /** + * Get a variable + * + * @param string $table The table from which to get the variable + * @param string $where The value where + * @param string $whereString The target/field string where/name + * @param string $what The return field + * @param string $operator The operator between $whereString/field and $where/value + * @param string $main The component in which the table is found + * + * @return mix string/int/float + * + */ + public static function getVar($table, $where = null, $whereString = 'user', $what = 'id', $operator = '=', $main = 'extensiondistributor') + { + if(!$where) + { + $where = JFactory::getUser()->id; + } + // Get a db connection. + $db = JFactory::getDbo(); + // Create a new query object. + $query = $db->getQuery(true); + $query->select($db->quoteName(array($what))); + if (empty($table)) + { + $query->from($db->quoteName('#__'.$main)); + } + else + { + $query->from($db->quoteName('#__'.$main.'_'.$table)); + } + if (is_numeric($where)) + { + $query->where($db->quoteName($whereString) . ' '.$operator.' '.(int) $where); + } + elseif (is_string($where)) + { + $query->where($db->quoteName($whereString) . ' '.$operator.' '. $db->quote((string)$where)); + } + else + { + return false; + } + $db->setQuery($query); + $db->execute(); + if ($db->getNumRows()) + { + return $db->loadResult(); + } + return false; + } + + /** + * Get array of variables + * + * @param string $table The table from which to get the variables + * @param string $where The value where + * @param string $whereString The target/field string where/name + * @param string $what The return field + * @param string $operator The operator between $whereString/field and $where/value + * @param string $main The component in which the table is found + * @param bool $unique The switch to return a unique array + * + * @return array + * + */ + public static function getVars($table, $where = null, $whereString = 'user', $what = 'id', $operator = 'IN', $main = 'extensiondistributor', $unique = true) + { + if(!$where) + { + $where = JFactory::getUser()->id; + } + + if (!self::checkArray($where) && $where > 0) + { + $where = array($where); + } + + if (self::checkArray($where)) + { + // prep main <-- why? well if $main='' is empty then $table can be categories or users + if (self::checkString($main)) + { + $main = '_'.ltrim($main, '_'); + } + // Get a db connection. + $db = JFactory::getDbo(); + // Create a new query object. + $query = $db->getQuery(true); + + $query->select($db->quoteName(array($what))); + if (empty($table)) + { + $query->from($db->quoteName('#__'.$main)); + } + else + { + $query->from($db->quoteName('#_'.$main.'_'.$table)); + } + // add strings to array search + if ('IN_STRINGS' === $operator || 'NOT IN_STRINGS' === $operator) + { + $query->where($db->quoteName($whereString) . ' ' . str_replace('_STRINGS', '', $operator) . ' ("' . implode('","',$where) . '")'); + } + else + { + $query->where($db->quoteName($whereString) . ' ' . $operator . ' (' . implode(',',$where) . ')'); + } + $db->setQuery($query); + $db->execute(); + if ($db->getNumRows()) + { + if ($unique) + { + return array_unique($db->loadColumn()); + } + return $db->loadColumn(); + } + } + return false; + } + + public static function isPublished($id,$type) + { + if ($type == 'raw') + { + $type = 'item'; + } + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + $query->select(array('a.published')); + $query->from('#__extensiondistributor_'.$type.' AS a'); + $query->where('a.id = '. (int) $id); + $query->where('a.published = 1'); + $db->setQuery($query); + $db->execute(); + $found = $db->getNumRows(); + if($found) + { + return true; + } + return false; + } + + public static function getGroupName($id) + { + $db = JFactory::getDBO(); + $query = $db->getQuery(true); + $query->select(array('a.title')); + $query->from('#__usergroups AS a'); + $query->where('a.id = '. (int) $id); + $db->setQuery($query); + $db->execute(); + $found = $db->getNumRows(); + if($found) + { + return $db->loadResult(); + } + return $id; + } + + /** + * Get the action permissions + * + * @param string $view The related view name + * @param int $record The item to act upon + * @param string $views The related list view name + * @param mixed $target Only get this permission (like edit, create, delete) + * @param string $component The target component + * @param object $user The user whose permissions we are loading + * + * @return object The JObject of permission/authorised actions + * + */ + public static function getActions($view, &$record = null, $views = null, $target = null, $component = 'extensiondistributor', $user = 'null') + { + // load the user if not given + if (!self::checkObject($user)) + { + // get the user object + $user = JFactory::getUser(); + } + // load the JObject + $result = new JObject; + // make view name safe (just incase) + $view = self::safeString($view); + if (self::checkString($views)) + { + $views = self::safeString($views); + } + // get all actions from component + $actions = JAccess::getActionsFromFile( + JPATH_ADMINISTRATOR . '/components/com_' . $component . '/access.xml', + "/access/section[@name='component']/" + ); + // if non found then return empty JObject + if (empty($actions)) + { + return $result; + } + // get created by if not found + if (self::checkObject($record) && !isset($record->created_by) && isset($record->id)) + { + $record->created_by = self::getVar($view, $record->id, 'id', 'created_by', '=', $component); + } + // set actions only set in component settings + $componentActions = array('core.admin', 'core.manage', 'core.options', 'core.export'); + // check if we have a target + $checkTarget = false; + if ($target) + { + // convert to an array + if (self::checkString($target)) + { + $target = array($target); + } + // check if we are good to go + if (self::checkArray($target)) + { + $checkTarget = true; + } + } + // loop the actions and set the permissions + foreach ($actions as $action) + { + // check target action filter + if ($checkTarget && self::filterActions($view, $action->name, $target)) + { + continue; + } + // set to use component default + $fallback = true; + // reset permission per/action + $permission = false; + $catpermission = false; + // set area + $area = 'comp'; + // check if the record has an ID and the action is item related (not a component action) + if (self::checkObject($record) && isset($record->id) && $record->id > 0 && !in_array($action->name, $componentActions) && + (strpos($action->name, 'core.') !== false || strpos($action->name, $view . '.') !== false)) + { + // we are in item + $area = 'item'; + // The record has been set. Check the record permissions. + $permission = $user->authorise($action->name, 'com_' . $component . '.' . $view . '.' . (int) $record->id); + // if no permission found, check edit own + if (!$permission) + { + // With edit, if the created_by matches current user then dig deeper. + if (($action->name === 'core.edit' || $action->name === $view . '.edit') && $record->created_by > 0 && ($record->created_by == $user->id)) + { + // the correct target + $coreCheck = (array) explode('.', $action->name); + // check that we have both local and global access + if ($user->authorise($coreCheck[0] . '.edit.own', 'com_' . $component . '.' . $view . '.' . (int) $record->id) && + $user->authorise($coreCheck[0] . '.edit.own', 'com_' . $component)) + { + // allow edit + $result->set($action->name, true); + // set not to use global default + // because we already validated it + $fallback = false; + } + else + { + // do not allow edit + $result->set($action->name, false); + $fallback = false; + } + } + } + elseif (self::checkString($views) && isset($record->catid) && $record->catid > 0) + { + // we are in item + $area = 'category'; + // set the core check + $coreCheck = explode('.', $action->name); + $core = $coreCheck[0]; + // make sure we use the core. action check for the categories + if (strpos($action->name, $view) !== false && strpos($action->name, 'core.') === false ) + { + $coreCheck[0] = 'core'; + $categoryCheck = implode('.', $coreCheck); + } + else + { + $categoryCheck = $action->name; + } + // The record has a category. Check the category permissions. + $catpermission = $user->authorise($categoryCheck, 'com_' . $component . '.' . $views . '.category.' . (int) $record->catid); + if (!$catpermission && !is_null($catpermission)) + { + // With edit, if the created_by matches current user then dig deeper. + if (($action->name === 'core.edit' || $action->name === $view . '.edit') && $record->created_by > 0 && ($record->created_by == $user->id)) + { + // check that we have both local and global access + if ($user->authorise('core.edit.own', 'com_' . $component . '.' . $views . '.category.' . (int) $record->catid) && + $user->authorise($core . '.edit.own', 'com_' . $component)) + { + // allow edit + $result->set($action->name, true); + // set not to use global default + // because we already validated it + $fallback = false; + } + else + { + // do not allow edit + $result->set($action->name, false); + $fallback = false; + } + } + } + } + } + // if allowed then fallback on component global settings + if ($fallback) + { + // if item/category blocks access then don't fall back on global + if ((($area === 'item') && !$permission) || (($area === 'category') && !$catpermission)) + { + // do not allow + $result->set($action->name, false); + } + // Finally remember the global settings have the final say. (even if item allow) + // The local item permissions can block, but it can't open and override of global permissions. + // Since items are created by users and global permissions is set by system admin. + else + { + $result->set($action->name, $user->authorise($action->name, 'com_' . $component)); + } + } + } + return $result; + } + + /** + * Filter the action permissions + * + * @param string $action The action to check + * @param array $targets The array of target actions + * + * @return boolean true if action should be filtered out + * + */ + protected static function filterActions(&$view, &$action, &$targets) + { + foreach ($targets as $target) + { + if (strpos($action, $view . '.' . $target) !== false || + strpos($action, 'core.' . $target) !== false) + { + return false; + break; + } + } + return true; + } + + /** + * Check if have an json string + * + * @input string The json string to check + * + * @returns bool true on success + */ + public static function checkJson($string) + { + if (self::checkString($string)) + { + json_decode($string); + return (json_last_error() === JSON_ERROR_NONE); + } + return false; + } + + /** + * Check if have an object with a length + * + * @input object The object to check + * + * @returns bool true on success + */ + public static function checkObject($object) + { + if (isset($object) && is_object($object)) + { + return count((array)$object) > 0; + } + return false; + } + + /** + * Check if have an array with a length + * + * @input array The array to check + * + * @returns bool/int number of items in array on success + */ + public static function checkArray($array, $removeEmptyString = false) + { + if (isset($array) && is_array($array) && ($nr = count((array)$array)) > 0) + { + // also make sure the empty strings are removed + if ($removeEmptyString) + { + foreach ($array as $key => $string) + { + if (empty($string)) + { + unset($array[$key]); + } + } + return self::checkArray($array, false); + } + return $nr; + } + return false; + } + + /** + * Check if have a string with a length + * + * @input string The string to check + * + * @returns bool true on success + */ + public static function checkString($string) + { + if (isset($string) && is_string($string) && strlen($string) > 0) + { + return true; + } + return false; + } + + /** + * Check if we are connected + * Thanks https://stackoverflow.com/a/4860432/1429677 + * + * @returns bool true on success + */ + public static function isConnected() + { + // If example.com is down, then probably the whole internet is down, since IANA maintains the domain. Right? + $connected = @fsockopen("www.example.com", 80); + // website, port (try 80 or 443) + if ($connected) + { + //action when connected + $is_conn = true; + fclose($connected); + } + else + { + //action in connection failure + $is_conn = false; + } + return $is_conn; + } + + /** + * Merge an array of array's + * + * @input array The arrays you would like to merge + * + * @returns array on success + */ + public static function mergeArrays($arrays) + { + if(self::checkArray($arrays)) + { + $arrayBuket = array(); + foreach ($arrays as $array) + { + if (self::checkArray($array)) + { + $arrayBuket = array_merge($arrayBuket, $array); + } + } + return $arrayBuket; + } + return false; + } + + // typo sorry! + public static function sorten($string, $length = 40, $addTip = true) + { + return self::shorten($string, $length, $addTip); + } + + /** + * Shorten a string + * + * @input string The you would like to shorten + * + * @returns string on success + */ + public static function shorten($string, $length = 40, $addTip = true) + { + if (self::checkString($string)) + { + $initial = strlen($string); + $words = preg_split('/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE); + $words_count = count((array)$words); + + $word_length = 0; + $last_word = 0; + for (; $last_word < $words_count; ++$last_word) + { + $word_length += strlen($words[$last_word]); + if ($word_length > $length) + { + break; + } + } + + $newString = implode(array_slice($words, 0, $last_word)); + $final = strlen($newString); + if ($initial != $final && $addTip) + { + $title = self::shorten($string, 400 , false); + return ''.trim($newString).'...'; + } + elseif ($initial != $final && !$addTip) + { + return trim($newString).'...'; + } + } + return $string; + } + + /** + * Making strings safe (various ways) + * + * @input string The you would like to make safe + * + * @returns string on success + */ + public static function safeString($string, $type = 'L', $spacer = '_', $replaceNumbers = true, $keepOnlyCharacters = true) + { + if ($replaceNumbers === true) + { + // remove all numbers and replace with english text version (works well only up to millions) + $string = self::replaceNumbers($string); + } + // 0nly continue if we have a string + if (self::checkString($string)) + { + // create file name without the extention that is safe + if ($type === 'filename') + { + // make sure VDM is not in the string + $string = str_replace('VDM', 'vDm', $string); + // Remove anything which isn't a word, whitespace, number + // or any of the following caracters -_() + // If you don't need to handle multi-byte characters + // you can use preg_replace rather than mb_ereg_replace + // Thanks @Łukasz Rysiak! + // $string = mb_ereg_replace("([^\w\s\d\-_\(\)])", '', $string); + $string = preg_replace("([^\w\s\d\-_\(\)])", '', $string); + // http://stackoverflow.com/a/2021729/1429677 + return preg_replace('/\s+/', ' ', $string); + } + // remove all other characters + $string = trim($string); + $string = preg_replace('/'.$spacer.'+/', ' ', $string); + $string = preg_replace('/\s+/', ' ', $string); + // Transliterate string + $string = self::transliterate($string); + // remove all and keep only characters + if ($keepOnlyCharacters) + { + $string = preg_replace("/[^A-Za-z ]/", '', $string); + } + // keep both numbers and characters + else + { + $string = preg_replace("/[^A-Za-z0-9 ]/", '', $string); + } + // select final adaptations + if ($type === 'L' || $type === 'strtolower') + { + // replace white space with underscore + $string = preg_replace('/\s+/', $spacer, $string); + // default is to return lower + return strtolower($string); + } + elseif ($type === 'W') + { + // return a string with all first letter of each word uppercase(no undersocre) + return ucwords(strtolower($string)); + } + elseif ($type === 'w' || $type === 'word') + { + // return a string with all lowercase(no undersocre) + return strtolower($string); + } + elseif ($type === 'Ww' || $type === 'Word') + { + // return a string with first letter of the first word uppercase and all the rest lowercase(no undersocre) + return ucfirst(strtolower($string)); + } + elseif ($type === 'WW' || $type === 'WORD') + { + // return a string with all the uppercase(no undersocre) + return strtoupper($string); + } + elseif ($type === 'U' || $type === 'strtoupper') + { + // replace white space with underscore + $string = preg_replace('/\s+/', $spacer, $string); + // return all upper + return strtoupper($string); + } + elseif ($type === 'F' || $type === 'ucfirst') + { + // replace white space with underscore + $string = preg_replace('/\s+/', $spacer, $string); + // return with first caracter to upper + return ucfirst(strtolower($string)); + } + elseif ($type === 'cA' || $type === 'cAmel' || $type === 'camelcase') + { + // convert all words to first letter uppercase + $string = ucwords(strtolower($string)); + // remove white space + $string = preg_replace('/\s+/', '', $string); + // now return first letter lowercase + return lcfirst($string); + } + // return string + return $string; + } + // not a string + return ''; + } + + public static function transliterate($string) + { + // set tag only once + if (!self::checkString(self::$langTag)) + { + // get global value + self::$langTag = JComponentHelper::getParams('com_extensiondistributor')->get('language', 'en-GB'); + } + // Transliterate on the language requested + $lang = Language::getInstance(self::$langTag); + return $lang->transliterate($string); + } + + public static function htmlEscape($var, $charset = 'UTF-8', $shorten = false, $length = 40) + { + if (self::checkString($var)) + { + $filter = new JFilterInput(); + $string = $filter->clean(html_entity_decode(htmlentities($var, ENT_COMPAT, $charset)), 'HTML'); + if ($shorten) + { + return self::shorten($string,$length); + } + return $string; + } + else + { + return ''; + } + } + + public static function replaceNumbers($string) + { + // set numbers array + $numbers = array(); + // first get all numbers + preg_match_all('!\d+!', $string, $numbers); + // check if we have any numbers + if (isset($numbers[0]) && self::checkArray($numbers[0])) + { + foreach ($numbers[0] as $number) + { + $searchReplace[$number] = self::numberToString((int)$number); + } + // now replace numbers in string + $string = str_replace(array_keys($searchReplace), array_values($searchReplace),$string); + // check if we missed any, strange if we did. + return self::replaceNumbers($string); + } + // return the string with no numbers remaining. + return $string; + } + + /** + * Convert an integer into an English word string + * Thanks to Tom Nicholson + * + * @input an int + * @returns a string + */ + public static function numberToString($x) + { + $nwords = array( "zero", "one", "two", "three", "four", "five", "six", "seven", + "eight", "nine", "ten", "eleven", "twelve", "thirteen", + "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", + "nineteen", "twenty", 30 => "thirty", 40 => "forty", + 50 => "fifty", 60 => "sixty", 70 => "seventy", 80 => "eighty", + 90 => "ninety" ); + + if(!is_numeric($x)) + { + $w = $x; + } + elseif(fmod($x, 1) != 0) + { + $w = $x; + } + else + { + if($x < 0) + { + $w = 'minus '; + $x = -$x; + } + else + { + $w = ''; + // ... now $x is a non-negative integer. + } + + if($x < 21) // 0 to 20 + { + $w .= $nwords[$x]; + } + elseif($x < 100) // 21 to 99 + { + $w .= $nwords[10 * floor($x/10)]; + $r = fmod($x, 10); + if($r > 0) + { + $w .= ' '. $nwords[$r]; + } + } + elseif($x < 1000) // 100 to 999 + { + $w .= $nwords[floor($x/100)] .' hundred'; + $r = fmod($x, 100); + if($r > 0) + { + $w .= ' and '. self::numberToString($r); + } + } + elseif($x < 1000000) // 1000 to 999999 + { + $w .= self::numberToString(floor($x/1000)) .' thousand'; + $r = fmod($x, 1000); + if($r > 0) + { + $w .= ' '; + if($r < 100) + { + $w .= 'and '; + } + $w .= self::numberToString($r); + } + } + else // millions + { + $w .= self::numberToString(floor($x/1000000)) .' million'; + $r = fmod($x, 1000000); + if($r > 0) + { + $w .= ' '; + if($r < 100) + { + $w .= 'and '; + } + $w .= self::numberToString($r); + } + } + } + return $w; + } + + /** + * Random Key + * + * @returns a string + */ + public static function randomkey($size) + { + $bag = "abcefghijknopqrstuwxyzABCDDEFGHIJKLLMMNOPQRSTUVVWXYZabcddefghijkllmmnopqrstuvvwxyzABCEFGHIJKNOPQRSTUWXYZ"; + $key = array(); + $bagsize = strlen($bag) - 1; + for ($i = 0; $i < $size; $i++) + { + $get = rand(0, $bagsize); + $key[] = $bag[$get]; + } + return implode($key); + } +} + diff --git a/site/helpers/headercheck.php b/site/helpers/headercheck.php new file mode 100644 index 0000000..cc0bfa1 --- /dev/null +++ b/site/helpers/headercheck.php @@ -0,0 +1,80 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +class extensiondistributorHeaderCheck +{ + function js_loaded($script_name) + { + // UIkit check point + if (strpos($script_name,'uikit') !== false) + { + $app = JFactory::getApplication(); + $getTemplateName = $app->getTemplate('template')->template; + + if (strpos($getTemplateName,'yoo') !== false) + { + return true; + } + } + + $document = JFactory::getDocument(); + $head_data = $document->getHeadData(); + foreach (array_keys($head_data['scripts']) as $script) + { + if (stristr($script, $script_name)) + { + return true; + } + } + + return false; + } + + function css_loaded($script_name) + { + // UIkit check point + if (strpos($script_name,'uikit') !== false) + { + $app = JFactory::getApplication(); + $getTemplateName = $app->getTemplate('template')->template; + + if (strpos($getTemplateName,'yoo') !== false) + { + return true; + } + } + + $document = JFactory::getDocument(); + $head_data = $document->getHeadData(); + + foreach (array_keys($head_data['styleSheets']) as $script) + { + if (stristr($script, $script_name)) + { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/site/helpers/index.html b/site/helpers/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/helpers/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/helpers/route.php b/site/helpers/route.php new file mode 100644 index 0000000..695a267 --- /dev/null +++ b/site/helpers/route.php @@ -0,0 +1,352 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +/** + * Extensiondistributor Route Helper + **/ +abstract class ExtensiondistributorHelperRoute +{ + protected static $lookup; + + /** + * @param int The route of the Extension + */ + public static function getExtensionRoute($id = 0, $catid = 0) + { + if ($id > 0) + { + // Initialize the needel array. + $needles = array( + 'extension' => array((int) $id) + ); + // Create the link + $link = 'index.php?option=com_extensiondistributor&view=extension&id='. $id; + } + else + { + // Initialize the needel array. + $needles = array( + 'extension' => array() + ); + // Create the link but don't add the id. + $link = 'index.php?option=com_extensiondistributor&view=extension'; + } + if ($catid > 1) + { + $categories = JCategories::getInstance('extensiondistributor.extensions'); + $category = $categories->get($catid); + if ($category) + { + $needles['category'] = array_reverse($category->getPath()); + $needles['categories'] = $needles['category']; + $link .= '&catid='.$catid; + } + } + + if ($item = self::_findItem($needles)) + { + $link .= '&Itemid='.$item; + } + + return $link; + } + + /** + * @param int The route of the Package + */ + public static function getPackageRoute($id = 0, $catid = 0) + { + if ($id > 0) + { + // Initialize the needel array. + $needles = array( + 'package' => array((int) $id) + ); + // Create the link + $link = 'index.php?option=com_extensiondistributor&view=package&id='. $id; + } + else + { + // Initialize the needel array. + $needles = array( + 'package' => array() + ); + // Create the link but don't add the id. + $link = 'index.php?option=com_extensiondistributor&view=package'; + } + if ($catid > 1) + { + $categories = JCategories::getInstance('extensiondistributor.package'); + $category = $categories->get($catid); + if ($category) + { + $needles['category'] = array_reverse($category->getPath()); + $needles['categories'] = $needles['category']; + $link .= '&catid='.$catid; + } + } + + if ($item = self::_findItem($needles)) + { + $link .= '&Itemid='.$item; + } + + return $link; + } + + /** + * @param int The route of the Keys + */ + public static function getKeysRoute($id = 0, $catid = 0) + { + if ($id > 0) + { + // Initialize the needel array. + $needles = array( + 'keys' => array((int) $id) + ); + // Create the link + $link = 'index.php?option=com_extensiondistributor&view=keys&id='. $id; + } + else + { + // Initialize the needel array. + $needles = array( + 'keys' => array() + ); + // Create the link but don't add the id. + $link = 'index.php?option=com_extensiondistributor&view=keys'; + } + if ($catid > 1) + { + $categories = JCategories::getInstance('extensiondistributor.keys'); + $category = $categories->get($catid); + if ($category) + { + $needles['category'] = array_reverse($category->getPath()); + $needles['categories'] = $needles['category']; + $link .= '&catid='.$catid; + } + } + + if ($item = self::_findItem($needles)) + { + $link .= '&Itemid='.$item; + } + + return $link; + } + + /** + * Get the URL route for extensiondistributor category from a category ID and language + * + * @param mixed $catid The id of the items's category either an integer id or a instance of JCategoryNode + * @param mixed $language The id of the language being used. + * + * @return string The link to the contact + * + * @since 1.5 + */ + public static function getCategoryRoute_keep_for_later($catid, $language = 0) + { + if ($catid instanceof JCategoryNode) + { + $id = $catid->id; + $category = $catid; + } + else + { + throw new Exception('First parameter must be JCategoryNode'); + } + + $views = array( + "com_extensiondistributor" => "extension", + "com_extensiondistributor" => "extension"); + $view = $views[$category->extension]; + + if ($id < 1 || !($category instanceof JCategoryNode)) + { + $link = ''; + } + else + { + //Create the link + $link = 'index.php?option=com_extensiondistributor&view='.$view.'&category='.$category->slug; + + $needles = array( + $view => array($id), + 'category' => array($id) + ); + + if ($language && $language != "*" && JLanguageMultilang::isEnabled()) + { + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select('a.sef AS sef') + ->select('a.lang_code AS lang_code') + ->from('#__languages AS a'); + + $db->setQuery($query); + $langs = $db->loadObjectList(); + foreach ($langs as $lang) + { + if ($language == $lang->lang_code) + { + $link .= '&lang='.$lang->sef; + $needles['language'] = $language; + } + } + } + + if ($item = self::_findItem($needles,'category')) + { + + $link .= '&Itemid='.$item; + } + else + { + if ($category) + { + $catids = array_reverse($category->getPath()); + $needles = array( + 'category' => $catids + ); + if ($item = self::_findItem($needles,'category')) + { + $link .= '&Itemid='.$item; + } + elseif ($item = self::_findItem(null, 'category')) + { + $link .= '&Itemid='.$item; + } + } + } + } + return $link; + } + + protected static function _findItem($needles = null,$type = null) + { + $app = JFactory::getApplication(); + $menus = $app->getMenu('site'); + $language = isset($needles['language']) ? $needles['language'] : '*'; + + // Prepare the reverse lookup array. + if (!isset(self::$lookup[$language])) + { + self::$lookup[$language] = array(); + + $component = JComponentHelper::getComponent('com_extensiondistributor'); + + $attributes = array('component_id'); + $values = array($component->id); + + if ($language != '*') + { + $attributes[] = 'language'; + $values[] = array($needles['language'], '*'); + } + + $items = $menus->getItems($attributes, $values); + + foreach ($items as $item) + { + if (isset($item->query) && isset($item->query['view'])) + { + $view = $item->query['view']; + + if (!isset(self::$lookup[$language][$view])) + { + self::$lookup[$language][$view] = array(); + } + + if (isset($item->query['id'])) + { + /** + * Here it will become a bit tricky + * language != * can override existing entries + * language == * cannot override existing entries + */ + if (!isset(self::$lookup[$language][$view][$item->query['id']]) || $item->language != '*') + { + self::$lookup[$language][$view][$item->query['id']] = $item->id; + } + } + else + { + self::$lookup[$language][$view][0] = $item->id; + } + } + } + } + + if ($needles) + { + foreach ($needles as $view => $ids) + { + if (isset(self::$lookup[$language][$view])) + { + if (ExtensiondistributorHelper::checkArray($ids)) + { + foreach ($ids as $id) + { + if (isset(self::$lookup[$language][$view][(int) $id])) + { + return self::$lookup[$language][$view][(int) $id]; + } + } + } + elseif (isset(self::$lookup[$language][$view][0])) + { + return self::$lookup[$language][$view][0]; + } + } + } + } + + if ($type) + { + // Check if the global menu item has been set. + $params = JComponentHelper::getParams('com_extensiondistributor'); + if ($item = $params->get($type.'_menu', 0)) + { + return $item; + } + } + + // Check if the active menuitem matches the requested language + $active = $menus->getActive(); + + if ($active + && $active->component == 'com_extensiondistributor' + && ($language == '*' || in_array($active->language, array('*', $language)) || !JLanguageMultilang::isEnabled())) + { + return $active->id; + } + + // If not found, return language specific home link + $default = $menus->getDefault($language); + + return !empty($default->id) ? $default->id : null; + } +} diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/language/en-GB/en-GB.com_extensiondistributor.ini b/site/language/en-GB/en-GB.com_extensiondistributor.ini new file mode 100644 index 0000000..3e812bc --- /dev/null +++ b/site/language/en-GB/en-GB.com_extensiondistributor.ini @@ -0,0 +1,24 @@ +COM_CONTENT_FIELD_MODIFIED_DESC="The last date this item was modified." +COM_EXTENSIONDISTRIBUTOR="Extension Distributor" +COM_EXTENSIONDISTRIBUTOR_CREATE_NEW_S="Create New %s" +COM_EXTENSIONDISTRIBUTOR_EDIT_S="Edit %s" +COM_EXTENSIONDISTRIBUTOR_KEYS="Keys" +COM_EXTENSIONDISTRIBUTOR_KEYS_DESC="User Keys" +COM_EXTENSIONDISTRIBUTOR_NEW="New" +COM_EXTENSIONDISTRIBUTOR_NOT_AUTHORISED_TO_VIEW_KEYS="Not authorised to view keys!" +COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED="Not found or access denied!" +COM_EXTENSIONDISTRIBUTOR_NO_ACCESS_GRANTED="No Access Granted!" +COM_EXTENSIONDISTRIBUTOR_PACKAGE="package" +COM_EXTENSIONDISTRIBUTOR_PACKAGE_DESC="Package - extensionDistributor" +JGLOBAL_FIELD_ID_DESC="Record number in the database." +JGLOBAL_FIELD_ID_LABEL="ID" +JGLOBAL_FIELD_MODIFIED_BY_DESC="The user who did the last modification." +JGLOBAL_FIELD_MODIFIED_BY_LABEL="Modified By" +JGLOBAL_FIELD_MODIFIED_LABEL="Modified Date" +JTOOLBAR_APPLY="Save" +JTOOLBAR_CANCEL="Cancel" +JTOOLBAR_CLOSE="Close" +JTOOLBAR_HELP="Help" +JTOOLBAR_SAVE="Save & Close" +JTOOLBAR_SAVE_AND_NEW="Save & New" +JTOOLBAR_SAVE_AS_COPY="Save as Copy" \ No newline at end of file diff --git a/site/language/en-GB/en-GB.com_extensiondistributor.sys.ini b/site/language/en-GB/en-GB.com_extensiondistributor.sys.ini new file mode 100644 index 0000000..6d3d345 --- /dev/null +++ b/site/language/en-GB/en-GB.com_extensiondistributor.sys.ini @@ -0,0 +1,3 @@ +COM_EXTENSIONDISTRIBUTOR="Extension Distributor" +COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED="Not found or access denied!" +COM_EXTENSIONDISTRIBUTOR_NO_ACCESS_GRANTED="No Access Granted!" \ No newline at end of file diff --git a/site/language/en-GB/index.html b/site/language/en-GB/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/language/en-GB/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/language/fr-FR/fr-FR.com_extensiondistributor.ini b/site/language/fr-FR/fr-FR.com_extensiondistributor.ini new file mode 100644 index 0000000..ba22371 --- /dev/null +++ b/site/language/fr-FR/fr-FR.com_extensiondistributor.ini @@ -0,0 +1,16 @@ +COM_CONTENT_FIELD_MODIFIED_DESC="La date de la dernière modification de cet élément." +COM_EXTENSIONDISTRIBUTOR_NEW="Nouveau" +COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED="Non trouvé ou accès refusé !" +COM_EXTENSIONDISTRIBUTOR_NO_ACCESS_GRANTED="Accès interdit !" +JGLOBAL_FIELD_ID_DESC="Le numéro d'enregistrement dans la base de données." +JGLOBAL_FIELD_ID_LABEL="ID" +JGLOBAL_FIELD_MODIFIED_BY_DESC="Le membre qui a fait la dernière modification" +JGLOBAL_FIELD_MODIFIED_BY_LABEL="Modifié par" +JGLOBAL_FIELD_MODIFIED_LABEL="Date modifiée" +JTOOLBAR_APPLY="Sauvegarder" +JTOOLBAR_CANCEL="Annuler" +JTOOLBAR_CLOSE="Fermer" +JTOOLBAR_HELP="Aide" +JTOOLBAR_SAVE="Sauvegarder & Fermer" +JTOOLBAR_SAVE_AND_NEW="Sauver & Nouveau" +JTOOLBAR_SAVE_AS_COPY="Enregistrer comme copie" \ No newline at end of file diff --git a/site/language/fr-FR/fr-FR.com_extensiondistributor.sys.ini b/site/language/fr-FR/fr-FR.com_extensiondistributor.sys.ini new file mode 100644 index 0000000..2e81580 --- /dev/null +++ b/site/language/fr-FR/fr-FR.com_extensiondistributor.sys.ini @@ -0,0 +1,2 @@ +COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED="Non trouvé ou accès refusé !" +COM_EXTENSIONDISTRIBUTOR_NO_ACCESS_GRANTED="Accès interdit !" \ No newline at end of file diff --git a/site/language/index.html b/site/language/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/language/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/layouts/index.html b/site/layouts/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/layouts/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/models/index.html b/site/models/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/models/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/models/keys.php b/site/models/keys.php new file mode 100644 index 0000000..e2fc6dd --- /dev/null +++ b/site/models/keys.php @@ -0,0 +1,218 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ItemModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Keys Item Model + */ +class ExtensiondistributorModelKeys extends ItemModel +{ + /** + * Model context string. + * + * @var string + */ + protected $_context = 'com_extensiondistributor.keys'; + + /** + * Model user data. + * + * @var strings + */ + protected $user; + protected $userId; + protected $guest; + protected $groups; + protected $levels; + protected $app; + protected $input; + protected $uikitComp; + + /** + * @var object item + */ + protected $item; + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @since 1.6 + * + * @return void + */ + protected function populateState() + { + $this->app = JFactory::getApplication(); + $this->input = $this->app->input; + // Get the itme main id + $id = $this->input->getInt('id', null); + $this->setState('keys.id', $id); + + // Load the parameters. + $params = $this->app->getParams(); + $this->setState('params', $params); + parent::populateState(); + } + + /** + * Method to get article data. + * + * @param integer $pk The id of the article. + * + * @return mixed Menu item data object on success, false on failure. + */ + public function getItem($pk = null) + { + $this->user = JFactory::getUser(); + // check if this user has permission to access item + if (!$this->user->authorise('site.keys.access', 'com_extensiondistributor')) + { + $app = JFactory::getApplication(); + $app->enqueueMessage(JText::_('COM_EXTENSIONDISTRIBUTOR_NOT_AUTHORISED_TO_VIEW_KEYS'), 'error'); + // redirect away to the home page if no access allowed. + $app->redirect(JURI::root()); + return false; + } + $this->userId = $this->user->get('id'); + $this->guest = $this->user->get('guest'); + $this->groups = $this->user->get('groups'); + $this->authorisedGroups = $this->user->getAuthorisedGroups(); + $this->levels = $this->user->getAuthorisedViewLevels(); + $this->initSet = true; + + $pk = (!empty($pk)) ? $pk : (int) $this->getState('keys.id'); + + if ($this->_item === null) + { + $this->_item = array(); + } + + if (!isset($this->_item[$pk])) + { + try + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Get data +return false; + + // Reset the query using our newly populated query object. + $db->setQuery($query); + // Load the results as a stdClass object. + $data = $db->loadObject(); + + if (empty($data)) + { + $app = JFactory::getApplication(); + // If no data is found redirect to default page and show warning. + $app->enqueueMessage(JText::_('COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED'), 'warning'); + $app->redirect(JURI::root()); + return false; + } + + // set data object to item. + $this->_item[$pk] = $data; + } + catch (Exception $e) + { + if ($e->getCode() == 404) + { + // Need to go thru the error handler to allow Redirect to work. + JError::raiseWarning(404, $e->getMessage()); + } + else + { + $this->setError($e); + $this->_item[$pk] = false; + } + } + } + + return $this->_item[$pk]; + } + + /** + * Custom Method + * + * @return mixed An array of objects on success, false on failure. + * + */ + public function getUserKeys() + { + + if (!isset($this->initSet) || !$this->initSet) + { + $this->user = JFactory::getUser(); + $this->userId = $this->user->get('id'); + $this->guest = $this->user->get('guest'); + $this->groups = $this->user->get('groups'); + $this->authorisedGroups = $this->user->getAuthorisedGroups(); + $this->levels = $this->user->getAuthorisedViewLevels(); + $this->initSet = true; + } + + // Get the global params + $globalParams = JComponentHelper::getParams('com_extensiondistributor', true); + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Get from #__extensiondistributor_key as a + $query->select($db->quoteName( + array('a.id','a.user','a.website','a.key','a.published','a.created_by','a.modified_by','a.created','a.modified','a.version','a.hits','a.ordering'), + array('id','user','website','key','published','created_by','modified_by','created','modified','version','hits','ordering'))); + $query->from($db->quoteName('#__extensiondistributor_key', 'a')); + $query->where('a.user = ' . (int) $this->userId); + + // Reset the query using our newly populated query object. + $db->setQuery($query); + $items = $db->loadObjectList(); + + if (empty($items)) + { + return false; + } + + // Insure all item fields are adapted where needed. + if (ExtensiondistributorHelper::checkArray($items)) + { + foreach ($items as $nr => &$item) + { + // Always create a slug for sef URL's + $item->slug = (isset($item->alias) && isset($item->id)) ? $item->id.':'.$item->alias : $item->id; + } + } + // return items + return $items; + } +} diff --git a/site/models/package.php b/site/models/package.php new file mode 100644 index 0000000..3e30b38 --- /dev/null +++ b/site/models/package.php @@ -0,0 +1,152 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\Model\ItemModel; +use Joomla\Utilities\ArrayHelper; + +/** + * Extensiondistributor Package Item Model + */ +class ExtensiondistributorModelPackage extends ItemModel +{ + /** + * Model context string. + * + * @var string + */ + protected $_context = 'com_extensiondistributor.package'; + + /** + * Model user data. + * + * @var strings + */ + protected $user; + protected $userId; + protected $guest; + protected $groups; + protected $levels; + protected $app; + protected $input; + protected $uikitComp; + + /** + * @var object item + */ + protected $item; + + /** + * Method to auto-populate the model state. + * + * Note. Calling getState in this method will result in recursion. + * + * @since 1.6 + * + * @return void + */ + protected function populateState() + { + $this->app = JFactory::getApplication(); + $this->input = $this->app->input; + // Get the itme main id + $id = $this->input->getInt('id', null); + $this->setState('package.id', $id); + + // Load the parameters. + $params = $this->app->getParams(); + $this->setState('params', $params); + parent::populateState(); + } + + /** + * Method to get article data. + * + * @param integer $pk The id of the article. + * + * @return mixed Menu item data object on success, false on failure. + */ + public function getItem($pk = null) + { + $this->user = JFactory::getUser(); + $this->userId = $this->user->get('id'); + $this->guest = $this->user->get('guest'); + $this->groups = $this->user->get('groups'); + $this->authorisedGroups = $this->user->getAuthorisedGroups(); + $this->levels = $this->user->getAuthorisedViewLevels(); + $this->initSet = true; + + $pk = (!empty($pk)) ? $pk : (int) $this->getState('package.id'); + + if ($this->_item === null) + { + $this->_item = array(); + } + + if (!isset($this->_item[$pk])) + { + try + { + // Get a db connection. + $db = JFactory::getDbo(); + + // Create a new query object. + $query = $db->getQuery(true); + + // Get data +return false; + + // Reset the query using our newly populated query object. + $db->setQuery($query); + // Load the results as a stdClass object. + $data = $db->loadObject(); + + if (empty($data)) + { + $app = JFactory::getApplication(); + // If no data is found redirect to default page and show warning. + $app->enqueueMessage(JText::_('COM_EXTENSIONDISTRIBUTOR_NOT_FOUND_OR_ACCESS_DENIED'), 'warning'); + $app->redirect(JURI::root()); + return false; + } + + // set data object to item. + $this->_item[$pk] = $data; + } + catch (Exception $e) + { + if ($e->getCode() == 404) + { + // Need to go thru the error handler to allow Redirect to work. + JError::raiseWarning(404, $e->getMessage()); + } + else + { + $this->setError($e); + $this->_item[$pk] = false; + } + } + } + + return $this->_item[$pk]; + } +} diff --git a/site/router.php b/site/router.php new file mode 100644 index 0000000..9e6efe5 --- /dev/null +++ b/site/router.php @@ -0,0 +1,252 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +/** + * Routing class from com_extensiondistributor + * + * @since 3.3 + */ +class ExtensiondistributorRouter extends JComponentRouterBase +{ + /** + * Build the route for the com_extensiondistributor component + * + * @param array &$query An array of URL arguments + * + * @return array The URL arguments to use to assemble the subsequent URL. + * + * @since 3.3 + */ + public function build(&$query) + { + $segments = array(); + + // Get a menu item based on Itemid or currently active + $params = JComponentHelper::getParams('com_extensiondistributor'); + + if (empty($query['Itemid'])) + { + $menuItem = $this->menu->getActive(); + } + else + { + $menuItem = $this->menu->getItem($query['Itemid']); + } + + $mView = (empty($menuItem->query['view'])) ? null : $menuItem->query['view']; + $mId = (empty($menuItem->query['id'])) ? null : $menuItem->query['id']; + + if (isset($query['view'])) + { + $view = $query['view']; + + if (empty($query['Itemid']) && !(isset($view) && isset($query['id']) && ($view === 'package' || $view === 'keys'))) + { + $segments[] = $query['view']; + } + + unset($query['view']); + } + + // Are we dealing with a item that is attached to a menu item? + if (isset($view) && ($mView == $view) and (isset($query['id'])) and ($mId == (int) $query['id'])) + { + unset($query['view']); + unset($query['catid']); + unset($query['id']); + return $segments; + } + + if (isset($view) && isset($query['id']) && ($view === 'package' || $view === 'keys')) + { + if ($mId != (int) $query['id'] || $mView != $view) + { + if (($view === 'package' || $view === 'keys')) + { + $segments[] = $view; + $id = explode(':', $query['id']); + if (count($id) == 2) + { + $segments[] = $id[1]; + } + else + { + $segments[] = $id[0]; + } + } + } + unset($query['id']); + } + + $total = count($segments); + + for ($i = 0; $i < $total; $i++) + { + $segments[$i] = str_replace(':', '-', $segments[$i]); + } + + return $segments; + + } + + /** + * Parse the segments of a URL. + * + * @param array &$segments The segments of the URL to parse. + * + * @return array The URL attributes to be used by the application. + * + * @since 3.3 + */ + public function parse(&$segments) + { + $count = count($segments); + $vars = array(); + + // Handle View and Identifier + switch($segments[0]) + { + case 'package': + $vars['view'] = 'package'; + if (is_numeric($segments[$count-1])) + { + $vars['id'] = (int) $segments[$count-1]; + } + elseif ($segments[$count-1]) + { + $id = $this->getVar('package', $segments[$count-1], 'alias', 'id'); + if($id) + { + $vars['id'] = $id; + } + } + break; + case 'keys': + $vars['view'] = 'keys'; + if (is_numeric($segments[$count-1])) + { + $vars['id'] = (int) $segments[$count-1]; + } + elseif ($segments[$count-1]) + { + $id = $this->getVar('keys', $segments[$count-1], 'alias', 'id'); + if($id) + { + $vars['id'] = $id; + } + } + break; + } + + return $vars; + } + + protected function getVar($table, $where = null, $whereString = null, $what = null, $category = false, $operator = '=', $main = 'extensiondistributor') + { + if(!$where || !$what || !$whereString) + { + return false; + } + // Get a db connection. + $db = JFactory::getDbo(); + // Create a new query object. + $query = $db->getQuery(true); + + $query->select($db->quoteName(array($what))); + if ('categories' == $table || 'category' == $table || $category) + { + $getTable = '#__categories'; + $query->from($db->quoteName($getTable)); + // we need this to target the components categories (TODO will keep an eye on this) + $query->where($db->quoteName('extension') . ' LIKE '. $db->quote((string)'com_' . $main . '%')); + } + else + { + // we must check if the table exist (TODO not ideal) + $tables = $db->getTableList(); + $app = JFactory::getApplication(); + $prefix = $app->get('dbprefix'); + $check = $prefix.$main.'_'.$table; + if (in_array($check, $tables)) + { + $getTable = '#__'.$main.'_'.$table; + $query->from($db->quoteName($getTable)); + } + else + { + return false; + } + } + if (is_numeric($where)) + { + return false; + } + elseif ($this->checkString($where)) + { + // we must first check if this table has the column + $columns = $db->getTableColumns($getTable); + if (isset($columns[$whereString])) + { + $query->where($db->quoteName($whereString) . ' '.$operator.' '. $db->quote((string)$where)); + } + else + { + return false; + } + } + else + { + return false; + } + $db->setQuery($query); + $db->execute(); + if ($db->getNumRows()) + { + return $db->loadResult(); + } + return false; + } + + protected function checkString($string) + { + if (isset($string) && is_string($string) && strlen($string) > 0) + { + return true; + } + return false; + } +} + +function ExtensiondistributorBuildRoute(&$query) +{ + $router = new ExtensiondistributorRouter; + + return $router->build($query); +} + +function ExtensiondistributorParseRoute($segments) +{ + $router = new ExtensiondistributorRouter; + + return $router->parse($segments); +} \ No newline at end of file diff --git a/site/views/index.html b/site/views/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/views/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/views/keys/index.html b/site/views/keys/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/views/keys/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/views/keys/tmpl/default.php b/site/views/keys/tmpl/default.php new file mode 100644 index 0000000..b8976ab --- /dev/null +++ b/site/views/keys/tmpl/default.php @@ -0,0 +1,42 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + + +?> +toolbar->render(); ?> +

      My download keys

      +userkeys as $item): ?> +
      +
      +
      key; ?>
      +
      website; ?>
      +
      + + + +
      +
      +
      + +Add key + diff --git a/site/views/keys/tmpl/default.xml b/site/views/keys/tmpl/default.xml new file mode 100644 index 0000000..158cf61 --- /dev/null +++ b/site/views/keys/tmpl/default.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/site/views/keys/tmpl/index.html b/site/views/keys/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/views/keys/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/views/keys/view.html.php b/site/views/keys/view.html.php new file mode 100644 index 0000000..0b215f9 --- /dev/null +++ b/site/views/keys/view.html.php @@ -0,0 +1,108 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Keys + */ +class ExtensiondistributorViewKeys extends HtmlView +{ + // Overwriting JView display method + function display($tpl = null) + { + // get combined params of both component and menu + $this->app = JFactory::getApplication(); + $this->params = $this->app->getParams(); + $this->menu = $this->app->getMenu()->getActive(); + // get the user object + $this->user = JFactory::getUser(); + // Initialise variables. + $this->item = $this->get('Item'); + $this->userkeys = $this->get('UserKeys'); + + // Set the toolbar + $this->addToolBar(); + + // set the document + $this->_prepareDocument(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode(PHP_EOL, $errors), 500); + } + + parent::display($tpl); + } + + /** + * Prepares the document + */ + protected function _prepareDocument() + { + + // always make sure jquery is loaded. + JHtml::_('jquery.framework'); + // Load the header checker class. + require_once( JPATH_COMPONENT_SITE.'/helpers/headercheck.php' ); + // Initialize the header checker. + $HeaderCheck = new extensiondistributorHeaderCheck; + + // always load these files. + $this->document->addStyleSheet(JURI::root(true) . "/media/com_extensiondistributor/uikit-v3/css/uikit.min.css", (ExtensiondistributorHelper::jVersion()->isCompatible("3.8.0")) ? array("version" => "auto") : "text/css"); + $this->document->addScript(JURI::root(true) . "/media/com_extensiondistributor/uikit-v3/js/uikit.min.js", (ExtensiondistributorHelper::jVersion()->isCompatible("3.8.0")) ? array("version" => "auto") : "text/javascript"); + $this->document->addScript(JURI::root(true) . "/media/com_extensiondistributor/uikit-v3/js/uikit-icons.min.js", (ExtensiondistributorHelper::jVersion()->isCompatible("3.8.0")) ? array("version" => "auto") : "text/javascript"); + // add the document default css file + $this->document->addStyleSheet(JURI::root(true) .'/components/com_extensiondistributor/assets/css/keys.css', (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('keys'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + // now initiate the toolbar + $this->toolbar = JToolbar::getInstance(); + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var, $sorten = false, $length = 40) + { + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, $sorten, $length); + } +} diff --git a/site/views/package/index.html b/site/views/package/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/views/package/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/views/package/submitbutton.js b/site/views/package/submitbutton.js new file mode 100644 index 0000000..20cf18f --- /dev/null +++ b/site/views/package/submitbutton.js @@ -0,0 +1,34 @@ +/*----------------------------------------------------------------------------------| www.vdm.io |----/ + Agence Agerix +/-------------------------------------------------------------------------------------------------------/ + + @version 1.x.x + @build 2nd June, 2022 + @created 12th December, 2020 + @package Extension Distributor + @subpackage submitbutton.js + @author Emmanuel Danan + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +Joomla.submitbutton = function(task) +{ + if (task == ''){ + return false; + } else { + var action = task.split('.'); + if (action[1] == 'cancel' || action[1] == 'close' || document.formvalidator.isValid(document.getElementById("adminForm"))){ + Joomla.submitform(task, document.getElementById("adminForm")); + return true; + } else { + alert(Joomla.JText._('package, some values are not acceptable.','Some values are unacceptable')); + return false; + } + } +} \ No newline at end of file diff --git a/site/views/package/tmpl/default.php b/site/views/package/tmpl/default.php new file mode 100644 index 0000000..0392c82 --- /dev/null +++ b/site/views/package/tmpl/default.php @@ -0,0 +1,33 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + + +?> +toolbar->render(); ?> +

      + Download area +

      +
      + There's nothing special do do here. + Back to homepage +
      diff --git a/site/views/package/tmpl/index.html b/site/views/package/tmpl/index.html new file mode 100644 index 0000000..fa6d84e --- /dev/null +++ b/site/views/package/tmpl/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/views/package/view.html.php b/site/views/package/view.html.php new file mode 100644 index 0000000..9c4c0f4 --- /dev/null +++ b/site/views/package/view.html.php @@ -0,0 +1,102 @@ + + @copyright Copyright (C) 2021. All Rights Reserved. + @license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html + ____ _____ _____ __ __ __ __ ___ _____ __ __ ____ _____ _ _ ____ _ _ ____ + (_ _)( _ )( _ )( \/ )( ) /__\ / __)( _ )( \/ )( _ \( _ )( \( )( ___)( \( )(_ _) +.-_)( )(_)( )(_)( ) ( )(__ /(__)\ ( (__ )(_)( ) ( )___/ )(_)( ) ( )__) ) ( )( +\____) (_____)(_____)(_/\/\_)(____)(__)(__) \___)(_____)(_/\/\_)(__) (_____)(_)\_)(____)(_)\_) (__) + +/------------------------------------------------------------------------------------------------------*/ + +// No direct access to this file +defined('_JEXEC') or die('Restricted access'); + +use Joomla\CMS\MVC\View\HtmlView; + +/** + * Extensiondistributor Html View class for the Package + */ +class ExtensiondistributorViewPackage extends HtmlView +{ + // Overwriting JView display method + function display($tpl = null) + { + // get combined params of both component and menu + $this->app = JFactory::getApplication(); + $this->params = $this->app->getParams(); + $this->menu = $this->app->getMenu()->getActive(); + // get the user object + $this->user = JFactory::getUser(); + // Initialise variables. + $this->item = $this->get('Item'); + + // Set the toolbar + $this->addToolBar(); + + // set the document + $this->_prepareDocument(); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new Exception(implode(PHP_EOL, $errors), 500); + } + + parent::display($tpl); + } + + /** + * Prepares the document + */ + protected function _prepareDocument() + { + + // always make sure jquery is loaded. + JHtml::_('jquery.framework'); + // Load the header checker class. + require_once( JPATH_COMPONENT_SITE.'/helpers/headercheck.php' ); + // Initialize the header checker. + $HeaderCheck = new extensiondistributorHeaderCheck; + // add the document default css file + $this->document->addStyleSheet(JURI::root(true) .'/components/com_extensiondistributor/assets/css/package.css', (ExtensiondistributorHelper::jVersion()->isCompatible('3.8.0')) ? array('version' => 'auto') : 'text/css'); + } + + /** + * Setting the toolbar + */ + protected function addToolBar() + { + + // set help url for this view if found + $this->help_url = ExtensiondistributorHelper::getHelpUrl('package'); + if (ExtensiondistributorHelper::checkString($this->help_url)) + { + JToolbarHelper::help('COM_EXTENSIONDISTRIBUTOR_HELP_MANAGER', false, $this->help_url); + } + // now initiate the toolbar + $this->toolbar = JToolbar::getInstance(); + } + + /** + * Escapes a value for output in a view script. + * + * @param mixed $var The output to escape. + * + * @return mixed The escaped value. + */ + public function escape($var, $sorten = false, $length = 40) + { + // use the helper htmlEscape method instead. + return ExtensiondistributorHelper::htmlEscape($var, $this->_charset, $sorten, $length); + } +}
    • ").text(this.emptyString))},draw:function(){var a=this,c=a.ft.$el.children("tbody"),d=!0;a.array.length>0?(a.$empty.detach(),b.arr.each(a.array,function(b){a.expandFirst&&d&&(b.expanded=!0,d=!1),b.draw(c)})):(a.$empty.children("td").attr("colspan",a.ft.columns.visibleColspan),c.append(a.$empty))}}),b.components.internal.register("rows",b.Rows,0)}(jQuery,FooTable),function(a){a.Defaults.prototype.rows=[],a.Defaults.prototype.empty="No results",a.Defaults.prototype.showToggle=!0,a.Defaults.prototype.toggleColumn="first",a.Defaults.prototype.expandFirst=!1}(FooTable),function(a){a.Filter=a.Class.extend({construct:function(b,c,d,e,f){this.name=b,this.space=!a.is.string(e)||"OR"!=e&&"AND"!=e?"AND":e,this.connectors=a.is["boolean"](f)?f:!0,this.query=new a.Query(c,this.space,this.connectors),this.columns=d},match:function(b){return a.is.string(b)?(a.is.string(this.query)&&(this.query=new a.Query(this.query,this.space,this.connectors)),this.query instanceof a.Query?this.query.match(b):!1):!1},matchRow:function(b){var c=this,d=a.arr.map(b.cells,function(b){return a.arr.contains(c.columns,b.column)?b.filterValue:null}).join(" ");return c.match(d)}})}(FooTable),function(a,b){b.Filtering=b.Component.extend({construct:function(a){this._super(a,a.o.filtering.enabled),this.filters=a.o.filtering.filters,this.delay=a.o.filtering.delay,this.min=a.o.filtering.min,this.space=a.o.filtering.space,this.connectors=a.o.filtering.connectors,this.placeholder=a.o.filtering.placeholder,this.position=a.o.filtering.position,this.$row=null,this.$cell=null,this.$dropdown=null,this.$input=null,this.$button=null,this._filterTimeout=null},preinit:function(a){var c=this;this.ft.raise("preinit.ft.filtering").then(function(){c.ft.$el.hasClass("footable-filtering")&&(c.enabled=!0),c.enabled=b.is["boolean"](a.filtering)?a.filtering:c.enabled,c.enabled&&(c.space=b.is.string(a.filterSpace)?a.filterSpace:c.space,c.min=b.is.number(a.filterMin)?a.filterMin:c.min,c.connectors=b.is.number(a.filterConnectors)?a.filterConnectors:c.connectors,c.delay=b.is.number(a.filterDelay)?a.filterDelay:c.delay,c.placeholder=b.is.number(a.filterPlaceholder)?a.filterPlaceholder:c.placeholder,c.filters=b.is.array(a.filterFilters)?c.ensure(a.filterFilters):c.ensure(c.filters),c.ft.$el.hasClass("footable-filtering-left")&&(c.position="left"),c.ft.$el.hasClass("footable-filtering-center")&&(c.position="center"),c.ft.$el.hasClass("footable-filtering-right")&&(c.position="right"),c.position=b.is.string(a.filterPosition)?a.filterPosition:c.position)},function(){c.enabled=!1})},init:function(){var a=this;this.ft.raise("init.ft.filtering").then(function(){a.$create()},function(){a.enabled=!1})},destroy:function(){var a=this;this.ft.raise("destroy.ft.filtering").then(function(){a.ft.$el.removeClass("footable-filtering").find("thead > tr.footable-filtering").remove()})},$create:function(){var c,d=this,e=a("
      ",{"class":"form-group"}).append(a("
      ").attr("colspan",d.ft.columns.visibleColspan).appendTo(d.$row),d.$form=a("",{"class":"form-inline"}).append(e).appendTo(d.$cell),d.$input=a("",{type:"text","class":"form-control",placeholder:d.placeholder}),d.$button=a("").attr("colspan",c.ft.columns.visibleColspan);var f=c.ft.$el.children("tfoot");0==f.length&&(f=a("
      ').attr('colspan', this.ft.columns.visibleColspan); + var $tfoot = this.ft.$el.children('tfoot'); + if ($tfoot.length == 0){ + $tfoot = $('
      ").attr("colspan",this.ft.columns.visibleColspan);var c=this.ft.$el.children("tfoot");0==c.length&&(c=a("
      This example shows using pseudo code what a sort function would look like.