From 027aa99b3217227a318024db0cd331f4797aae04 Mon Sep 17 00:00:00 2001 From: Bachir Soussi Chiadmi Date: Tue, 6 Sep 2016 16:05:07 +0200 Subject: [PATCH] a few more base modules --- base-profile.md | 19 + .../contrib/admin/filter_perms/LICENSE.txt | 339 +++++ .../admin/filter_perms/filter_perms.info.yml | 13 + .../filter_perms/filter_perms.services.yml | 5 + .../filter_perms/src/Form/PermissionsForm.php | 261 ++++ .../src/Form/PermissionsRoleSpecificForm.php | 24 + .../src/Routing/RouteSubscriber.php | 25 + .../all/modules/contrib/dev/devel/LICENSE.txt | 339 +++++ .../all/modules/contrib/dev/devel/README.txt | 51 + .../modules/contrib/dev/devel/composer.json | 25 + .../devel/config/install/devel.settings.yml | 8 + .../config/install/system.menu.devel.yml | 9 + .../dev/devel/config/schema/devel.schema.yml | 43 + .../contrib/dev/devel/css/devel-rtl.css | 0 .../modules/contrib/dev/devel/css/devel.css | 42 + .../modules/contrib/dev/devel/devel.api.php | 25 + .../modules/contrib/dev/devel/devel.info.yml | 14 + .../modules/contrib/dev/devel/devel.install | 30 + .../contrib/dev/devel/devel.libraries.yml | 5 + .../contrib/dev/devel/devel.links.menu.yml | 75 + .../contrib/dev/devel/devel.links.task.yml | 8 + .../modules/contrib/dev/devel/devel.module | 626 ++++++++ .../contrib/dev/devel/devel.permissions.yml | 14 + .../contrib/dev/devel/devel.routing.yml | 172 +++ .../modules/contrib/dev/devel/devel.rules.inc | 24 + .../contrib/dev/devel/devel.services.yml | 26 + .../dev/devel/devel_generate/README.txt | 42 + .../devel_generate/devel_generate.batch.inc | 25 + .../devel_generate/devel_generate.info.yml | 13 + .../devel_generate/devel_generate.module | 145 ++ .../devel_generate.permissions.yml | 5 + .../devel_generate/devel_generate.routing.yml | 2 + .../devel_generate.services.yml | 4 + .../drush/DevelGenerateUnishTest.php | 151 ++ .../drush/devel_generate.drush.inc | 196 +++ .../src/Annotation/DevelGenerate.php | 86 ++ .../devel_generate/src/DevelGenerateBase.php | 168 +++ .../src/DevelGenerateBaseInterface.php | 77 + .../src/DevelGenerateException.php | 12 + .../src/DevelGeneratePermissions.php | 62 + .../src/DevelGeneratePluginManager.php | 31 + .../src/Form/DevelGenerateForm.php | 114 ++ .../DevelGenerate/ContentDevelGenerate.php | 506 +++++++ .../DevelGenerate/MenuDevelGenerate.php | 407 ++++++ .../DevelGenerate/TermDevelGenerate.php | 267 ++++ .../DevelGenerate/UserDevelGenerate.php | 191 +++ .../DevelGenerate/VocabularyDevelGenerate.php | 175 +++ .../src/Routing/DevelGenerateRoutes.php | 57 + .../src/Tests/DevelGenerateTest.php | 175 +++ .../devel_generate_example.info.yml | 14 + .../devel_generate_example.module | 1 + .../DevelGenerate/ExampleDevelGenerate.php | 88 ++ .../tests/src/DevelGenerateManagerTest.php | 93 ++ .../dev/devel/devel_node_access/README.txt | 42 + .../install/devel_node_access.settings.yml | 3 + .../schema/devel_node_access.schema.yml | 15 + .../css/devel_node_access.module.css | 5 + .../devel_node_access.api.php | 81 ++ .../devel_node_access.info.yml | 16 + .../devel_node_access.install | 7 + .../devel_node_access/devel_node_access.js | 64 + .../devel_node_access.libraries.yml | 7 + .../devel_node_access.module | 269 ++++ .../devel_node_access.permissions.yml | 4 + .../devel_node_access/src/Form/DnaForm.php | 135 ++ .../src/Plugin/Block/DnaBlock.php | 1263 +++++++++++++++++ .../contrib/dev/devel/drush/devel.drush.inc | 274 ++++ .../dev/devel/drush/develDrushTest.php | 38 + .../dev/devel/drush/phpstorm.drush.inc | 105 ++ .../modules/contrib/dev/devel/kint/README.txt | 37 + .../contrib/dev/devel/kint/kint.info.yml | 13 + .../contrib/dev/devel/kint/kint.module | 65 + .../dev/devel/kint/kint.permissions.yml | 4 + .../contrib/dev/devel/kint/kint.services.yml | 5 + .../contrib/dev/devel/kint/kint/.gitignore | 3 + .../dev/devel/kint/kint/Kint.class.php | 836 +++++++++++ .../contrib/dev/devel/kint/kint/LICENCE | 20 + .../contrib/dev/devel/kint/kint/README.md | 141 ++ .../contrib/dev/devel/kint/kint/composer.json | 29 + .../dev/devel/kint/kint/config.default.php | 94 ++ .../dev/devel/kint/kint/decorators/plain.php | 335 +++++ .../dev/devel/kint/kint/decorators/rich.php | 319 +++++ .../dev/devel/kint/kint/examples/overview.php | 176 +++ .../devel/kint/kint/inc/kintObject.class.php | 19 + .../devel/kint/kint/inc/kintParser.class.php | 608 ++++++++ .../kint/kint/inc/kintVariableData.class.php | 102 ++ .../kint/kint/parsers/custom/classmethods.php | 150 ++ .../kint/kint/parsers/custom/classstatics.php | 49 + .../devel/kint/kint/parsers/custom/color.php | 400 ++++++ .../devel/kint/kint/parsers/custom/fspath.php | 69 + .../devel/kint/kint/parsers/custom/json.php | 19 + .../kint/kint/parsers/custom/microtime.php | 60 + .../kint/parsers/custom/objectiterateable.php | 22 + .../kint/parsers/custom/splobjectstorage.php | 24 + .../kint/kint/parsers/custom/timestamp.php | 29 + .../devel/kint/kint/parsers/custom/xml.php | 25 + .../kint/kint/parsers/objects/closure.php | 47 + .../kint/kint/parsers/objects/smarty.php | 35 + .../kint/kint/parsers/objects/splfileinfo.php | 70 + .../contrib/dev/devel/kint/kint/view/base.js | 437 ++++++ .../dev/devel/kint/kint/view/base.less | 413 ++++++ .../kint/kint/view/compiled/aante-light.css | 397 ++++++ .../dev/devel/kint/kint/view/compiled/kint.js | 10 + .../kint/kint/view/compiled/original.css | 1 + .../kint/view/compiled/solarized-dark.css | 1 + .../kint/kint/view/compiled/solarized.css | 1 + .../kint/kint/view/themes/aante-light.less | 154 ++ .../devel/kint/kint/view/themes/original.less | 43 + .../kint/kint/view/themes/solarized-dark.less | 39 + .../kint/kint/view/themes/solarized.less | 29 + .../kint/src/Plugin/Devel/Dumper/Kint.php | 58 + .../dev/devel/kint/src/Twig/KintExtension.php | 154 ++ .../contrib/dev/devel/run-tests-drush.sh | 21 + .../dev/devel/src/Annotation/DevelDumper.php | 44 + .../devel/src/Controller/DevelController.php | 235 +++ .../src/Controller/EntityDebugController.php | 122 ++ .../src/Controller/SwitchUserController.php | 120 ++ .../contrib/dev/devel/src/DevelDumperBase.php | 38 + .../dev/devel/src/DevelDumperInterface.php | 59 + .../dev/devel/src/DevelDumperManager.php | 142 ++ .../devel/src/DevelDumperManagerInterface.php | 102 ++ .../devel/src/DevelDumperPluginManager.php | 71 + .../src/DevelDumperPluginManagerInterface.php | 24 + .../EventSubscriber/DevelEventSubscriber.php | 108 ++ .../dev/devel/src/Form/ConfigEditor.php | 146 ++ .../dev/devel/src/Form/ConfigsList.php | 85 ++ .../dev/devel/src/Form/DevelRebuildMenus.php | 86 ++ .../dev/devel/src/Form/DevelReinstall.php | 154 ++ .../contrib/dev/devel/src/Form/ExecutePHP.php | 55 + .../dev/devel/src/Form/SettingsForm.php | 192 +++ .../dev/devel/src/Form/SwitchUserForm.php | 108 ++ .../dev/devel/src/Form/SystemStateEdit.php | 189 +++ .../src/Plugin/Block/DevelExecutePHP.php | 33 + .../src/Plugin/Block/SwitchUserBlock.php | 286 ++++ .../src/Plugin/Derivative/DevelLocalTask.php | 99 ++ .../src/Plugin/Devel/Dumper/ChromePhp.php | 40 + .../src/Plugin/Devel/Dumper/DoctrineDebug.php | 74 + .../Plugin/Devel/Dumper/DrupalVariable.php | 65 + .../devel/src/Plugin/Devel/Dumper/FirePhp.php | 41 + .../src/Plugin/Devel/Dumper/VarDumper.php | 50 + .../devel/src/Plugin/Mail/DevelMailLog.php | 98 ++ .../src/Plugin/Menu/DestinationMenuLink.php | 32 + .../src/Plugin/Menu/MenuItemMenuLink.php | 29 + .../dev/devel/src/Routing/RouteSubscriber.php | 122 ++ .../devel/src/Tests/DevelControllerTest.php | 109 ++ .../dev/devel/src/Tests/DevelDumperTest.php | 159 +++ .../devel/src/Tests/DevelErrorHandlerTest.php | 213 +++ .../dev/devel/src/Tests/DevelMailTest.php | 48 + .../devel/src/Tests/DevelMenuLinksTest.php | 147 ++ .../devel/src/Tests/DevelRebuildMenusTest.php | 41 + .../devel/src/Tests/DevelReinstallTest.php | 60 + .../devel/src/Tests/DevelStateEditorTest.php | 210 +++ .../devel/src/Tests/DevelSwitchUserTest.php | 313 ++++ .../dev/devel/src/Twig/Extension/Debug.php | 201 +++ .../css/devel_dumper_test.css | 0 .../devel_dumper_test.info.yml | 12 + .../devel_dumper_test.libraries.yml | 7 + .../devel_dumper_test.routing.yml | 39 + .../devel_dumper_test/js/devel_dumper_test.js | 0 .../src/Controller/DumperTestController.php | 89 ++ .../Devel/Dumper/AvailableTestDumper.php | 60 + .../Devel/Dumper/NotAvailableTestDumper.php | 41 + .../devel_entity_test.info.yml | 16 + .../devel_entity_test.links.task.yml | 2 + .../devel_entity_test.module | 29 + .../src/Entity/DevelEntityTestCanonical.php | 44 + .../src/Entity/DevelEntityTestEdit.php | 44 + .../src/Entity/DevelEntityTestNoLinks.php | 41 + .../Derivative/DevelEntityTestLocalTasks.php | 31 + .../modules/devel_test/devel_test.info.yml | 12 + .../modules/devel_test/devel_test.routing.yml | 7 + .../src/Controller/DevelTestController.php | 24 + .../tests/src/Kernel/DevelDumperTestTrait.php | 127 ++ .../Kernel/DevelEnforcedDependenciesTest.php | 80 ++ .../src/Kernel/DevelTwigExtensionTest.php | 203 +++ .../contrib/dev/devel/webprofiler/README.md | 28 + .../dev/devel/webprofiler/composer.json | 18 + .../config/install/webprofiler.config.yml | 28 + .../config/schema/webprofiler.schema.yml | 35 + .../config/translations/console.en.yml | 54 + .../config/translations/console.es.yml | 54 + .../devel/webprofiler/css/app/dashboard.css | 582 ++++++++ .../dev/devel/webprofiler/css/timeline.css | 106 ++ .../devel/webprofiler/images/caret-down.svg | 1 + .../js/app/collections/collectors.js | 35 + .../dev/devel/webprofiler/js/app/helpers.js | 104 ++ .../dev/devel/webprofiler/js/app/main.js | 105 ++ .../webprofiler/js/app/models/collector.js | 15 + .../webprofiler/js/app/routers/collectors.js | 48 + .../webprofiler/js/app/views/collector.js | 29 + .../js/app/views/collectorsList.js | 21 + .../devel/webprofiler/js/app/views/details.js | 20 + .../devel/webprofiler/js/app/views/layout.js | 83 ++ .../dev/devel/webprofiler/js/database.js | 68 + .../dev/devel/webprofiler/js/timeline.js | 223 +++ .../src/Access/AccessManagerWrapper.php | 83 ++ .../Asset/CssCollectionRendererWrapper.php | 40 + .../src/Asset/JsCollectionRendererWrapper.php | 40 + .../src/Cache/CacheBackendWrapper.php | 179 +++ .../src/Cache/CacheFactoryWrapper.php | 62 + .../src/Command/BenchmarkCommand.php | 302 ++++ .../webprofiler/src/Command/BenchmarkData.php | 57 + .../webprofiler/src/Command/ExportCommand.php | 151 ++ .../webprofiler/src/Command/ListCommand.php | 73 + .../src/Compiler/DecoratorPass.php | 33 + .../webprofiler/src/Compiler/EventPass.php | 30 + .../webprofiler/src/Compiler/ProfilerPass.php | 64 + .../webprofiler/src/Compiler/ServicePass.php | 110 ++ .../webprofiler/src/Compiler/StoragePass.php | 32 + .../src/Config/ConfigFactoryWrapper.php | 44 + .../src/Controller/DashboardController.php | 243 ++++ .../src/Controller/DatabaseController.php | 95 ++ .../src/Controller/ToolbarController.php | 108 ++ .../src/DataCollector/AssetsDataCollector.php | 107 ++ .../src/DataCollector/BlocksDataCollector.php | 149 ++ .../src/DataCollector/CacheDataCollector.php | 172 +++ .../src/DataCollector/ConfigDataCollector.php | 68 + .../DataCollector/DatabaseDataCollector.php | 253 ++++ .../src/DataCollector/DevelDataCollector.php | 110 ++ .../src/DataCollector/DrupalDataCollector.php | 134 ++ .../DrupalDataCollectorTrait.php | 115 ++ .../src/DataCollector/EventsDataCollector.php | 90 ++ .../DataCollector/ExtensionDataCollector.php | 156 ++ .../src/DataCollector/FormsDataCollector.php | 81 ++ .../src/DataCollector/HttpDataCollector.php | 168 +++ .../src/DataCollector/MailDataCollector.php | 99 ++ .../PerformanceTimingDataCollector.php | 83 ++ .../DataCollector/PhpConfigDataCollector.php | 163 +++ .../DataCollector/RequestDataCollector.php | 83 ++ .../DataCollector/RoutingDataCollector.php | 91 ++ .../DataCollector/ServicesDataCollector.php | 162 +++ .../src/DataCollector/StateDataCollector.php | 65 + .../src/DataCollector/ThemeDataCollector.php | 257 ++++ .../src/DataCollector/TimeDataCollector.php | 157 ++ .../TranslationsDataCollector.php | 99 ++ .../src/DataCollector/UserDataCollector.php | 141 ++ .../src/DataCollector/ViewsDataCollector.php | 118 ++ .../dev/devel/webprofiler/src/Decorator.php | 94 ++ .../TraceableContainer.php | 66 + .../src/DrupalDataCollectorInterface.php | 67 + .../Config/ConfigEntityStorageDecorator.php | 214 +++ .../Config/RoleStorageDecorator.php | 19 + .../Config/ShortcutSetStorageDecorator.php | 56 + .../Config/VocabularyStorageDecorator.php | 19 + .../src/Entity/EntityDecorator.php | 24 + .../src/Entity/EntityManagerWrapper.php | 239 ++++ .../src/Entity/EntityViewBuilderDecorator.php | 92 ++ .../WebprofilerEventSubscriber.php | 97 ++ .../devel/webprofiler/src/Form/ConfigForm.php | 237 ++++ .../src/Form/FormBuilderWrapper.php | 61 + .../src/Form/ProfilesFilterForm.php | 86 ++ .../src/Frontend/PerformanceTimingData.php | 81 ++ .../webprofiler/src/Helper/ClassShortener.php | 34 + .../src/Helper/ClassShortenerInterface.php | 17 + .../src/Helper/IdeLinkGenerator.php | 40 + .../src/Helper/IdeLinkGeneratorInterface.php | 17 + .../src/Http/HttpClientMiddleware.php | 84 ++ .../src/Mail/MailManagerWrapper.php | 94 ++ .../src/Profiler/DatabaseProfilerStorage.php | 158 +++ .../src/Profiler/FileProfilerStorage.php | 24 + .../webprofiler/src/Profiler/Profiler.php | 77 + .../src/Profiler/ProfilerStorageFactory.php | 26 + .../src/Profiler/ProfilerStorageManager.php | 52 + .../src/Profiler/TemplateManager.php | 130 ++ .../WebprofilerRequestMatcher.php | 50 + .../src/Routing/TokenConverter.php | 54 + .../StackMiddleware/WebprofilerMiddleware.php | 41 + .../webprofiler/src/State/StateWrapper.php | 86 ++ .../dev/devel/webprofiler/src/Stopwatch.php | 543 +++++++ .../TranslationManagerWrapper.php | 69 + .../src/Theme/ThemeNegotiatorWrapper.php | 39 + .../src/TraceableEventDispatcher.php | 70 + .../src/Twig/Dumper/HtmlDumper.php | 35 + .../src/Twig/Extension/ProfilerExtension.php | 93 ++ .../src/Views/TraceableViewExecutable.php | 156 ++ .../Views/ViewExecutableFactoryWrapper.php | 47 + .../src/WebprofilerServiceProvider.php | 109 ++ .../templates/Collector/assets.html.twig | 133 ++ .../templates/Collector/blocks.html.twig | 113 ++ .../templates/Collector/cache.html.twig | 63 + .../templates/Collector/config.html.twig | 37 + .../templates/Collector/database.html.twig | 168 +++ .../templates/Collector/devel.html.twig | 24 + .../templates/Collector/drupal.html.twig | 66 + .../templates/Collector/events.html.twig | 79 ++ .../templates/Collector/extensions.html.twig | 95 ++ .../templates/Collector/forms.html.twig | 67 + .../templates/Collector/http.html.twig | 77 + .../templates/Collector/mail.html.twig | 76 + .../Collector/performance_timing.html.twig | 76 + .../templates/Collector/php_config.html.twig | 52 + .../templates/Collector/request.html.twig | 206 +++ .../templates/Collector/routing.html.twig | 50 + .../templates/Collector/services.html.twig | 149 ++ .../templates/Collector/state.html.twig | 36 + .../templates/Collector/theme.html.twig | 147 ++ .../templates/Collector/time.html.twig | 59 + .../Collector/translations.html.twig | 85 ++ .../templates/Collector/user.html.twig | 63 + .../templates/Collector/views.html.twig | 58 + .../Profiler/webprofiler_dashboard.html.twig | 57 + .../Profiler/webprofiler_loader.html.twig | 73 + .../Profiler/webprofiler_panel.html.twig | 1 + .../Profiler/webprofiler_toolbar.css.twig | 311 ++++ .../Profiler/webprofiler_toolbar.html.twig | 35 + .../src/Unit/Cache/CacheDataCollectorTest.php | 69 + .../devel/webprofiler/webprofiler.info.yml | 16 + .../dev/devel/webprofiler/webprofiler.install | 190 +++ .../webprofiler/webprofiler.libraries.yml | 63 + .../webprofiler/webprofiler.links.menu.yml | 5 + .../webprofiler/webprofiler.links.task.yml | 9 + .../devel/webprofiler/webprofiler.make.yml | 12 + .../dev/devel/webprofiler/webprofiler.module | 51 + .../webprofiler/webprofiler.permissions.yml | 7 + .../devel/webprofiler/webprofiler.routing.yml | 81 ++ .../webprofiler/webprofiler.services.yml | 267 ++++ .../modules/contrib/dev/entity/.travis.yml | 58 + .../modules/contrib/dev/entity/LICENSE.txt | 339 +++++ .../all/modules/contrib/dev/entity/README.txt | 11 + .../modules/contrib/dev/entity/composer.json | 10 + .../entity/config/schema/entity.schema.yml | 11 + .../contrib/dev/entity/entity.info.yml | 12 + .../contrib/dev/entity/entity.links.task.yml | 3 + .../modules/contrib/dev/entity/entity.module | 6 + .../contrib/dev/entity/entity.services.yml | 6 + .../contrib/dev/entity/entity.views.inc | 32 + .../EntityRevisionRouteAccessChecker.php | 159 +++ .../Controller/RevisionControllerTrait.php | 200 +++ .../Controller/RevisionOverviewController.php | 161 +++ .../RevisionableEntityBundleInterface.php | 17 + .../dev/entity/src/EntityViewBuilder.php | 37 + .../dev/entity/src/Form/DeleteMultiple.php | 226 +++ .../entity/src/Form/RevisionRevertForm.php | 167 +++ .../Form/RevisionableContentEntityForm.php | 159 +++ .../entity/src/Plugin/Action/DeleteAction.php | 97 ++ .../Action/Derivative/DeleteActionDeriver.php | 78 + .../Derivative/RevisionsOverviewDeriver.php | 67 + .../RevisionableContentEntityBase.php | 30 + .../Routing/DeleteMultipleRouteProvider.php | 47 + .../src/Routing/RevisionRouteProvider.php | 127 ++ .../RevisionOverviewIntegrationTest.php | 51 + .../schema/entity_module_test.schema.yml | 16 + .../entity_module_test.info.yml | 9 + .../entity_module_test.links.task.yml | 9 + .../entity_module_test.permissions.yml | 7 + .../entity_module_test.routing.yml | 7 + .../src/Entity/EnhancedEntity.php | 76 + .../src/Entity/EnhancedEntityBundle.php | 81 ++ .../src/Functional/DeleteMultipleFormTest.php | 81 ++ .../Functional/RevisionRouteAccessTest.php | 97 ++ .../tests/src/Kernel/DeleteActionTest.php | 83 ++ .../tests/src/Kernel/RevisionBasicUITest.php | 164 +++ .../modules/contrib/mail/maillog/LICENSE.txt | 339 +++++ .../modules/contrib/mail/maillog/README.txt | 4 + .../config/install/maillog.settings.yml | 3 + .../install/views.view.maillog_overview.yml | 441 ++++++ .../maillog/config/schema/maillog.schema.yml | 12 + .../contrib/mail/maillog/maillog.info.yml | 16 + .../contrib/mail/maillog/maillog.install | 93 ++ .../mail/maillog/maillog.links.menu.yml | 5 + .../contrib/mail/maillog/maillog.module | 28 + .../mail/maillog/maillog.permissions.yml | 10 + .../contrib/mail/maillog/maillog.routing.yml | 30 + .../contrib/mail/maillog/maillog.views.inc | 192 +++ .../src/Controller/MaillogController.php | 132 ++ .../src/Form/MaillogClearConfirmForm.php | 64 + .../maillog/src/Form/MaillogSettingsForm.php | 108 ++ .../mail/maillog/src/Plugin/Mail/Maillog.php | 104 ++ .../Plugin/views/field/MaillogFieldDelete.php | 38 + .../maillog/src/Tests/Mail/MailUiTest.php | 149 ++ .../maillog_test/maillog_test.info.yml | 12 + .../modules/maillog_test/maillog_test.module | 34 + .../maillog/tests/src/Kernel/MailTest.php | 98 ++ .../modules/contrib/theme/extlink/LICENSE.txt | 339 +++++ .../modules/contrib/theme/extlink/README.txt | 37 + .../theme/extlink/config/extlink.settings.yml | 8 + .../extlink/config/schema/extlink.schema.yml | 40 + .../modules/contrib/theme/extlink/extlink.css | 22 + .../contrib/theme/extlink/extlink.info.yml | 13 + .../contrib/theme/extlink/extlink.install | 21 + .../modules/contrib/theme/extlink/extlink.js | 171 +++ .../theme/extlink/extlink.libraries.yml | 12 + .../theme/extlink/extlink.links.menu.yml | 5 + .../contrib/theme/extlink/extlink.min.js | 1 + .../contrib/theme/extlink/extlink.module | 32 + .../contrib/theme/extlink/extlink.routing.yml | 7 + .../contrib/theme/extlink/extlink_s.png | Bin 0 -> 153 bytes .../src/Form/ExtlinkAdminSettingsForm.php | 200 +++ .../extlink/src/Tests/ExtlinkAdminTest.php | 26 + .../theme/extlink/src/Tests/ExtlinkTest.php | 18 + .../extlink/src/Tests/ExtlinkTestBase.php | 67 + .../contrib/users/profile/.travis-phpcs.sh | 9 + .../modules/contrib/users/profile/.travis.yml | 119 ++ .../modules/contrib/users/profile/LICENSE.txt | 339 +++++ .../modules/contrib/users/profile/README.md | 23 + .../contrib/users/profile/composer.json | 10 + .../system.action.profile_delete_action.yml | 9 + .../system.action.profile_publish_action.yml | 9 + ...system.action.profile_unpublish_action.yml | 9 + .../config/install/views.view.profiles.yml | 379 +++++ .../profile/config/schema/profile.schema.yml | 53 + .../users/profile/css/profile.base.css | 11 + .../contrib/users/profile/profile.api.php | 51 + .../contrib/users/profile/profile.info.yml | 17 + .../users/profile/profile.libraries.yml | 5 + .../users/profile/profile.links.action.yml | 5 + .../users/profile/profile.links.menu.yml | 5 + .../users/profile/profile.links.task.yml | 14 + .../contrib/users/profile/profile.module | 345 +++++ .../users/profile/profile.permissions.yml | 13 + .../contrib/users/profile/profile.routing.yml | 60 + .../users/profile/profile.services.yml | 7 + .../profile/src/Access/ProfileAccessCheck.php | 72 + .../src/Controller/ProfileController.php | 177 +++ .../src/Controller/ProfileViewController.php | 61 + .../users/profile/src/Entity/Profile.php | 275 ++++ .../profile/src/Entity/ProfileInterface.php | 126 ++ .../users/profile/src/Entity/ProfileType.php | 174 +++ .../src/Entity/ProfileTypeInterface.php | 73 + .../users/profile/src/Form/DeleteMultiple.php | 124 ++ .../profile/src/Form/ProfileDeleteForm.php | 29 + .../users/profile/src/Form/ProfileForm.php | 89 ++ .../src/Form/ProfileTypeDeleteForm.php | 60 + .../profile/src/Form/ProfileTypeForm.php | 126 ++ .../src/Plugin/Action/DeleteProfile.php | 77 + .../src/Plugin/Action/PublishProfile.php | 39 + .../src/Plugin/Action/UnpublishProfile.php | 39 + .../Plugin/Derivative/ProfileLocalTask.php | 66 + .../ProfileSelection.php | 29 + .../Plugin/views/field/ProfileBulkForm.php | 21 + .../src/ProfileAccessControlHandler.php | 112 ++ .../profile/src/ProfileHtmlRouteProvider.php | 59 + .../users/profile/src/ProfileListBuilder.php | 140 ++ .../users/profile/src/ProfilePermissions.php | 72 + .../users/profile/src/ProfileStorage.php | 51 + .../profile/src/ProfileStorageInterface.php | 56 + .../users/profile/src/ProfileTestTrait.php | 67 + .../profile/src/ProfileTypeListBuilder.php | 63 + .../users/profile/src/ProfileViewBuilder.php | 22 + .../users/profile/src/ProfileViewsData.php | 29 + .../profile/src/Tests/ProfileDefaultTest.php | 411 ++++++ .../src/Tests/ProfileFieldAccessTest.php | 91 ++ .../src/Tests/ProfileRoleAccessTest.php | 165 +++ .../profile/src/Tests/ProfileTabTest.php | 135 ++ .../profile/src/Tests/ProfileTestBase.php | 139 ++ .../profile/src/Tests/ProfileTypeCRUDTest.php | 103 ++ .../src/Tests/ProfileTypeMultipleTest.php | 95 ++ .../users/profile/templates/profile.html.twig | 24 + .../tests/profile_test/profile_test.info.yml | 12 + .../test_views/views.view.users.yml | 243 ++++ .../Functional/ProfileRegisterFormTest.php | 66 + .../tests/src/Functional/ProfileTestBase.php | 140 ++ .../tests/src/Kernel/ProfileAccessTest.php | 210 +++ .../profile/tests/src/Kernel/ProfileTest.php | 149 ++ .../tests/src/Kernel/ProfileViewTest.php | 106 ++ 455 files changed, 43606 insertions(+) create mode 100644 sites/all/modules/contrib/admin/filter_perms/LICENSE.txt create mode 100644 sites/all/modules/contrib/admin/filter_perms/filter_perms.info.yml create mode 100644 sites/all/modules/contrib/admin/filter_perms/filter_perms.services.yml create mode 100644 sites/all/modules/contrib/admin/filter_perms/src/Form/PermissionsForm.php create mode 100644 sites/all/modules/contrib/admin/filter_perms/src/Form/PermissionsRoleSpecificForm.php create mode 100644 sites/all/modules/contrib/admin/filter_perms/src/Routing/RouteSubscriber.php create mode 100644 sites/all/modules/contrib/dev/devel/LICENSE.txt create mode 100644 sites/all/modules/contrib/dev/devel/README.txt create mode 100644 sites/all/modules/contrib/dev/devel/composer.json create mode 100644 sites/all/modules/contrib/dev/devel/config/install/devel.settings.yml create mode 100644 sites/all/modules/contrib/dev/devel/config/install/system.menu.devel.yml create mode 100644 sites/all/modules/contrib/dev/devel/config/schema/devel.schema.yml create mode 100644 sites/all/modules/contrib/dev/devel/css/devel-rtl.css create mode 100644 sites/all/modules/contrib/dev/devel/css/devel.css create mode 100644 sites/all/modules/contrib/dev/devel/devel.api.php create mode 100644 sites/all/modules/contrib/dev/devel/devel.info.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel.install create mode 100644 sites/all/modules/contrib/dev/devel/devel.libraries.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel.links.menu.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel.links.task.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel.module create mode 100644 sites/all/modules/contrib/dev/devel/devel.permissions.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel.routing.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel.rules.inc create mode 100644 sites/all/modules/contrib/dev/devel/devel.services.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/README.txt create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.batch.inc create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.info.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.module create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.permissions.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.routing.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.services.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/drush/DevelGenerateUnishTest.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/drush/devel_generate.drush.inc create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/Annotation/DevelGenerate.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGenerateBase.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGenerateBaseInterface.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGenerateException.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGeneratePermissions.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGeneratePluginManager.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/Form/DevelGenerateForm.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/ContentDevelGenerate.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/MenuDevelGenerate.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/UserDevelGenerate.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/VocabularyDevelGenerate.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/Routing/DevelGenerateRoutes.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/src/Tests/DevelGenerateTest.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.info.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.module create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/src/Plugin/DevelGenerate/ExampleDevelGenerate.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_generate/tests/src/DevelGenerateManagerTest.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/README.txt create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/config/install/devel_node_access.settings.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/config/schema/devel_node_access.schema.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/css/devel_node_access.module.css create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.api.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.info.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.install create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.js create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.libraries.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.module create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.permissions.yml create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/src/Form/DnaForm.php create mode 100644 sites/all/modules/contrib/dev/devel/devel_node_access/src/Plugin/Block/DnaBlock.php create mode 100644 sites/all/modules/contrib/dev/devel/drush/devel.drush.inc create mode 100644 sites/all/modules/contrib/dev/devel/drush/develDrushTest.php create mode 100644 sites/all/modules/contrib/dev/devel/drush/phpstorm.drush.inc create mode 100644 sites/all/modules/contrib/dev/devel/kint/README.txt create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint.info.yml create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint.module create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint.permissions.yml create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint.services.yml create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/.gitignore create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/Kint.class.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/LICENCE create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/README.md create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/composer.json create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/config.default.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/decorators/plain.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/decorators/rich.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/examples/overview.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/inc/kintObject.class.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/inc/kintParser.class.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/inc/kintVariableData.class.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/classmethods.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/classstatics.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/color.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/fspath.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/json.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/microtime.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/objectiterateable.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/splobjectstorage.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/timestamp.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/xml.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/closure.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/smarty.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/splfileinfo.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/base.js create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/base.less create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/aante-light.css create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/kint.js create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/original.css create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/solarized-dark.css create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/solarized.css create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/themes/aante-light.less create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/themes/original.less create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/themes/solarized-dark.less create mode 100644 sites/all/modules/contrib/dev/devel/kint/kint/view/themes/solarized.less create mode 100644 sites/all/modules/contrib/dev/devel/kint/src/Plugin/Devel/Dumper/Kint.php create mode 100644 sites/all/modules/contrib/dev/devel/kint/src/Twig/KintExtension.php create mode 100755 sites/all/modules/contrib/dev/devel/run-tests-drush.sh create mode 100644 sites/all/modules/contrib/dev/devel/src/Annotation/DevelDumper.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Controller/DevelController.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Controller/EntityDebugController.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Controller/SwitchUserController.php create mode 100644 sites/all/modules/contrib/dev/devel/src/DevelDumperBase.php create mode 100644 sites/all/modules/contrib/dev/devel/src/DevelDumperInterface.php create mode 100644 sites/all/modules/contrib/dev/devel/src/DevelDumperManager.php create mode 100644 sites/all/modules/contrib/dev/devel/src/DevelDumperManagerInterface.php create mode 100644 sites/all/modules/contrib/dev/devel/src/DevelDumperPluginManager.php create mode 100644 sites/all/modules/contrib/dev/devel/src/DevelDumperPluginManagerInterface.php create mode 100644 sites/all/modules/contrib/dev/devel/src/EventSubscriber/DevelEventSubscriber.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Form/ConfigEditor.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Form/ConfigsList.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Form/DevelRebuildMenus.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Form/DevelReinstall.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Form/ExecutePHP.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Form/SettingsForm.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Form/SwitchUserForm.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Form/SystemStateEdit.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Block/DevelExecutePHP.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Block/SwitchUserBlock.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Derivative/DevelLocalTask.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/ChromePhp.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/DoctrineDebug.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/DrupalVariable.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/FirePhp.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/VarDumper.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Mail/DevelMailLog.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Menu/DestinationMenuLink.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Plugin/Menu/MenuItemMenuLink.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Routing/RouteSubscriber.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Tests/DevelControllerTest.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Tests/DevelDumperTest.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Tests/DevelErrorHandlerTest.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Tests/DevelMailTest.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Tests/DevelMenuLinksTest.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Tests/DevelRebuildMenusTest.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Tests/DevelReinstallTest.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Tests/DevelStateEditorTest.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Tests/DevelSwitchUserTest.php create mode 100644 sites/all/modules/contrib/dev/devel/src/Twig/Extension/Debug.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/css/devel_dumper_test.css create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.info.yml create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.libraries.yml create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.routing.yml create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/js/devel_dumper_test.js create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Controller/DumperTestController.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Plugin/Devel/Dumper/AvailableTestDumper.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Plugin/Devel/Dumper/NotAvailableTestDumper.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.info.yml create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.links.task.yml create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.module create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/src/Entity/DevelEntityTestCanonical.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/src/Entity/DevelEntityTestEdit.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/src/Entity/DevelEntityTestNoLinks.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/src/Plugin/Derivative/DevelEntityTestLocalTasks.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.info.yml create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.routing.yml create mode 100644 sites/all/modules/contrib/dev/devel/tests/modules/devel_test/src/Controller/DevelTestController.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelDumperTestTrait.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelEnforcedDependenciesTest.php create mode 100644 sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelTwigExtensionTest.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/README.md create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/composer.json create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/config/install/webprofiler.config.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/config/schema/webprofiler.schema.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/config/translations/console.en.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/config/translations/console.es.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/css/app/dashboard.css create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/css/timeline.css create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/images/caret-down.svg create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/app/collections/collectors.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/app/helpers.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/app/main.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/app/models/collector.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/app/routers/collectors.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/collector.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/collectorsList.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/details.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/layout.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/database.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/js/timeline.js create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Access/AccessManagerWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Asset/CssCollectionRendererWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Asset/JsCollectionRendererWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Cache/CacheBackendWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Cache/CacheFactoryWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Command/BenchmarkCommand.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Command/BenchmarkData.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Command/ExportCommand.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Command/ListCommand.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/DecoratorPass.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/EventPass.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/ProfilerPass.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/ServicePass.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/StoragePass.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Config/ConfigFactoryWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/DashboardController.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/DatabaseController.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/ToolbarController.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/AssetsDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/BlocksDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/CacheDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ConfigDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DatabaseDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DevelDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DrupalDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DrupalDataCollectorTrait.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/EventsDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ExtensionDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/FormsDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/HttpDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/MailDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/PerformanceTimingDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/PhpConfigDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/RequestDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/RoutingDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ServicesDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/StateDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ThemeDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/TimeDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/TranslationsDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/UserDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ViewsDataCollector.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Decorator.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DependencyInjection/TraceableContainer.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/DrupalDataCollectorInterface.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/ConfigEntityStorageDecorator.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/RoleStorageDecorator.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/ShortcutSetStorageDecorator.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/VocabularyStorageDecorator.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityDecorator.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityManagerWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityViewBuilderDecorator.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/EventSubscriber/WebprofilerEventSubscriber.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ConfigForm.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Form/FormBuilderWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ProfilesFilterForm.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Frontend/PerformanceTimingData.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/ClassShortener.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/ClassShortenerInterface.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/IdeLinkGenerator.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/IdeLinkGeneratorInterface.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Http/HttpClientMiddleware.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Mail/MailManagerWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/DatabaseProfilerStorage.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/FileProfilerStorage.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/Profiler.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/ProfilerStorageFactory.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/ProfilerStorageManager.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/TemplateManager.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/RequestMatcher/WebprofilerRequestMatcher.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Routing/TokenConverter.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/StackMiddleware/WebprofilerMiddleware.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/State/StateWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Stopwatch.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/StringTranslation/TranslationManagerWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Theme/ThemeNegotiatorWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/TraceableEventDispatcher.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Twig/Dumper/HtmlDumper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Twig/Extension/ProfilerExtension.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Views/TraceableViewExecutable.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/Views/ViewExecutableFactoryWrapper.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/src/WebprofilerServiceProvider.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/assets.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/blocks.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/cache.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/config.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/database.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/devel.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/drupal.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/events.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/extensions.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/forms.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/http.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/mail.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/performance_timing.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/php_config.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/request.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/routing.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/services.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/state.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/theme.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/time.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/translations.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/user.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/views.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_dashboard.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_loader.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_panel.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_toolbar.css.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_toolbar.html.twig create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/tests/src/Unit/Cache/CacheDataCollectorTest.php create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.info.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.install create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.libraries.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.links.menu.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.links.task.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.make.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.module create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.permissions.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.routing.yml create mode 100644 sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.services.yml create mode 100644 sites/all/modules/contrib/dev/entity/.travis.yml create mode 100644 sites/all/modules/contrib/dev/entity/LICENSE.txt create mode 100644 sites/all/modules/contrib/dev/entity/README.txt create mode 100644 sites/all/modules/contrib/dev/entity/composer.json create mode 100644 sites/all/modules/contrib/dev/entity/config/schema/entity.schema.yml create mode 100644 sites/all/modules/contrib/dev/entity/entity.info.yml create mode 100644 sites/all/modules/contrib/dev/entity/entity.links.task.yml create mode 100644 sites/all/modules/contrib/dev/entity/entity.module create mode 100644 sites/all/modules/contrib/dev/entity/entity.services.yml create mode 100644 sites/all/modules/contrib/dev/entity/entity.views.inc create mode 100644 sites/all/modules/contrib/dev/entity/src/Access/EntityRevisionRouteAccessChecker.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Controller/RevisionControllerTrait.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Controller/RevisionOverviewController.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Entity/RevisionableEntityBundleInterface.php create mode 100644 sites/all/modules/contrib/dev/entity/src/EntityViewBuilder.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Form/DeleteMultiple.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Form/RevisionRevertForm.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Form/RevisionableContentEntityForm.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Plugin/Action/DeleteAction.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Plugin/Action/Derivative/DeleteActionDeriver.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Plugin/Derivative/RevisionsOverviewDeriver.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Revision/RevisionableContentEntityBase.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Routing/DeleteMultipleRouteProvider.php create mode 100644 sites/all/modules/contrib/dev/entity/src/Routing/RevisionRouteProvider.php create mode 100644 sites/all/modules/contrib/dev/entity/tests/Kernel/RevisionOverviewIntegrationTest.php create mode 100644 sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/config/schema/entity_module_test.schema.yml create mode 100644 sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.info.yml create mode 100644 sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.links.task.yml create mode 100644 sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.permissions.yml create mode 100644 sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.routing.yml create mode 100644 sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php create mode 100644 sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/src/Entity/EnhancedEntityBundle.php create mode 100644 sites/all/modules/contrib/dev/entity/tests/src/Functional/DeleteMultipleFormTest.php create mode 100644 sites/all/modules/contrib/dev/entity/tests/src/Functional/RevisionRouteAccessTest.php create mode 100644 sites/all/modules/contrib/dev/entity/tests/src/Kernel/DeleteActionTest.php create mode 100644 sites/all/modules/contrib/dev/entity/tests/src/Kernel/RevisionBasicUITest.php create mode 100644 sites/all/modules/contrib/mail/maillog/LICENSE.txt create mode 100644 sites/all/modules/contrib/mail/maillog/README.txt create mode 100644 sites/all/modules/contrib/mail/maillog/config/install/maillog.settings.yml create mode 100644 sites/all/modules/contrib/mail/maillog/config/install/views.view.maillog_overview.yml create mode 100644 sites/all/modules/contrib/mail/maillog/config/schema/maillog.schema.yml create mode 100644 sites/all/modules/contrib/mail/maillog/maillog.info.yml create mode 100644 sites/all/modules/contrib/mail/maillog/maillog.install create mode 100644 sites/all/modules/contrib/mail/maillog/maillog.links.menu.yml create mode 100644 sites/all/modules/contrib/mail/maillog/maillog.module create mode 100644 sites/all/modules/contrib/mail/maillog/maillog.permissions.yml create mode 100644 sites/all/modules/contrib/mail/maillog/maillog.routing.yml create mode 100644 sites/all/modules/contrib/mail/maillog/maillog.views.inc create mode 100644 sites/all/modules/contrib/mail/maillog/src/Controller/MaillogController.php create mode 100644 sites/all/modules/contrib/mail/maillog/src/Form/MaillogClearConfirmForm.php create mode 100644 sites/all/modules/contrib/mail/maillog/src/Form/MaillogSettingsForm.php create mode 100755 sites/all/modules/contrib/mail/maillog/src/Plugin/Mail/Maillog.php create mode 100644 sites/all/modules/contrib/mail/maillog/src/Plugin/views/field/MaillogFieldDelete.php create mode 100644 sites/all/modules/contrib/mail/maillog/src/Tests/Mail/MailUiTest.php create mode 100644 sites/all/modules/contrib/mail/maillog/tests/modules/maillog_test/maillog_test.info.yml create mode 100644 sites/all/modules/contrib/mail/maillog/tests/modules/maillog_test/maillog_test.module create mode 100644 sites/all/modules/contrib/mail/maillog/tests/src/Kernel/MailTest.php create mode 100644 sites/all/modules/contrib/theme/extlink/LICENSE.txt create mode 100644 sites/all/modules/contrib/theme/extlink/README.txt create mode 100644 sites/all/modules/contrib/theme/extlink/config/extlink.settings.yml create mode 100644 sites/all/modules/contrib/theme/extlink/config/schema/extlink.schema.yml create mode 100644 sites/all/modules/contrib/theme/extlink/extlink.css create mode 100644 sites/all/modules/contrib/theme/extlink/extlink.info.yml create mode 100644 sites/all/modules/contrib/theme/extlink/extlink.install create mode 100644 sites/all/modules/contrib/theme/extlink/extlink.js create mode 100644 sites/all/modules/contrib/theme/extlink/extlink.libraries.yml create mode 100644 sites/all/modules/contrib/theme/extlink/extlink.links.menu.yml create mode 100644 sites/all/modules/contrib/theme/extlink/extlink.min.js create mode 100644 sites/all/modules/contrib/theme/extlink/extlink.module create mode 100644 sites/all/modules/contrib/theme/extlink/extlink.routing.yml create mode 100644 sites/all/modules/contrib/theme/extlink/extlink_s.png create mode 100644 sites/all/modules/contrib/theme/extlink/src/Form/ExtlinkAdminSettingsForm.php create mode 100644 sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkAdminTest.php create mode 100644 sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkTest.php create mode 100644 sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkTestBase.php create mode 100644 sites/all/modules/contrib/users/profile/.travis-phpcs.sh create mode 100644 sites/all/modules/contrib/users/profile/.travis.yml create mode 100644 sites/all/modules/contrib/users/profile/LICENSE.txt create mode 100644 sites/all/modules/contrib/users/profile/README.md create mode 100644 sites/all/modules/contrib/users/profile/composer.json create mode 100644 sites/all/modules/contrib/users/profile/config/install/system.action.profile_delete_action.yml create mode 100644 sites/all/modules/contrib/users/profile/config/install/system.action.profile_publish_action.yml create mode 100644 sites/all/modules/contrib/users/profile/config/install/system.action.profile_unpublish_action.yml create mode 100644 sites/all/modules/contrib/users/profile/config/install/views.view.profiles.yml create mode 100644 sites/all/modules/contrib/users/profile/config/schema/profile.schema.yml create mode 100644 sites/all/modules/contrib/users/profile/css/profile.base.css create mode 100644 sites/all/modules/contrib/users/profile/profile.api.php create mode 100644 sites/all/modules/contrib/users/profile/profile.info.yml create mode 100644 sites/all/modules/contrib/users/profile/profile.libraries.yml create mode 100644 sites/all/modules/contrib/users/profile/profile.links.action.yml create mode 100644 sites/all/modules/contrib/users/profile/profile.links.menu.yml create mode 100644 sites/all/modules/contrib/users/profile/profile.links.task.yml create mode 100644 sites/all/modules/contrib/users/profile/profile.module create mode 100644 sites/all/modules/contrib/users/profile/profile.permissions.yml create mode 100644 sites/all/modules/contrib/users/profile/profile.routing.yml create mode 100644 sites/all/modules/contrib/users/profile/profile.services.yml create mode 100644 sites/all/modules/contrib/users/profile/src/Access/ProfileAccessCheck.php create mode 100644 sites/all/modules/contrib/users/profile/src/Controller/ProfileController.php create mode 100644 sites/all/modules/contrib/users/profile/src/Controller/ProfileViewController.php create mode 100644 sites/all/modules/contrib/users/profile/src/Entity/Profile.php create mode 100644 sites/all/modules/contrib/users/profile/src/Entity/ProfileInterface.php create mode 100644 sites/all/modules/contrib/users/profile/src/Entity/ProfileType.php create mode 100644 sites/all/modules/contrib/users/profile/src/Entity/ProfileTypeInterface.php create mode 100644 sites/all/modules/contrib/users/profile/src/Form/DeleteMultiple.php create mode 100644 sites/all/modules/contrib/users/profile/src/Form/ProfileDeleteForm.php create mode 100644 sites/all/modules/contrib/users/profile/src/Form/ProfileForm.php create mode 100644 sites/all/modules/contrib/users/profile/src/Form/ProfileTypeDeleteForm.php create mode 100644 sites/all/modules/contrib/users/profile/src/Form/ProfileTypeForm.php create mode 100644 sites/all/modules/contrib/users/profile/src/Plugin/Action/DeleteProfile.php create mode 100644 sites/all/modules/contrib/users/profile/src/Plugin/Action/PublishProfile.php create mode 100644 sites/all/modules/contrib/users/profile/src/Plugin/Action/UnpublishProfile.php create mode 100644 sites/all/modules/contrib/users/profile/src/Plugin/Derivative/ProfileLocalTask.php create mode 100644 sites/all/modules/contrib/users/profile/src/Plugin/EntityReferenceSelection/ProfileSelection.php create mode 100644 sites/all/modules/contrib/users/profile/src/Plugin/views/field/ProfileBulkForm.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfileAccessControlHandler.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfileHtmlRouteProvider.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfileListBuilder.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfilePermissions.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfileStorage.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfileStorageInterface.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfileTestTrait.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfileTypeListBuilder.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfileViewBuilder.php create mode 100644 sites/all/modules/contrib/users/profile/src/ProfileViewsData.php create mode 100644 sites/all/modules/contrib/users/profile/src/Tests/ProfileDefaultTest.php create mode 100644 sites/all/modules/contrib/users/profile/src/Tests/ProfileFieldAccessTest.php create mode 100644 sites/all/modules/contrib/users/profile/src/Tests/ProfileRoleAccessTest.php create mode 100644 sites/all/modules/contrib/users/profile/src/Tests/ProfileTabTest.php create mode 100644 sites/all/modules/contrib/users/profile/src/Tests/ProfileTestBase.php create mode 100644 sites/all/modules/contrib/users/profile/src/Tests/ProfileTypeCRUDTest.php create mode 100644 sites/all/modules/contrib/users/profile/src/Tests/ProfileTypeMultipleTest.php create mode 100644 sites/all/modules/contrib/users/profile/templates/profile.html.twig create mode 100644 sites/all/modules/contrib/users/profile/tests/profile_test/profile_test.info.yml create mode 100644 sites/all/modules/contrib/users/profile/tests/profile_test/test_views/views.view.users.yml create mode 100644 sites/all/modules/contrib/users/profile/tests/src/Functional/ProfileRegisterFormTest.php create mode 100644 sites/all/modules/contrib/users/profile/tests/src/Functional/ProfileTestBase.php create mode 100644 sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileAccessTest.php create mode 100644 sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileTest.php create mode 100644 sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileViewTest.php diff --git a/base-profile.md b/base-profile.md index b940be4c3..2370ebba6 100644 --- a/base-profile.md +++ b/base-profile.md @@ -16,3 +16,22 @@ drupal 8 base [a tester : configuration synchronizer](https://www.drupal.org/project/config_sync) [features](https://www.drupal.org/project/features) + +[filter permissions](https://www.drupal.org/project/filter_perms) + +### dev + +[devel](https://www.drupal.org/project/devel) + +### mail + +[maillog](https://www.drupal.org/project/maillog) + +### theme + +[extlink](https://www.drupal.org/project/extlink) + + +### users + +[profile](https://www.drupal.org/project/profile) diff --git a/sites/all/modules/contrib/admin/filter_perms/LICENSE.txt b/sites/all/modules/contrib/admin/filter_perms/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/admin/filter_perms/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General +Public License instead of this License. diff --git a/sites/all/modules/contrib/admin/filter_perms/filter_perms.info.yml b/sites/all/modules/contrib/admin/filter_perms/filter_perms.info.yml new file mode 100644 index 000000000..5b652de0a --- /dev/null +++ b/sites/all/modules/contrib/admin/filter_perms/filter_perms.info.yml @@ -0,0 +1,13 @@ +type: module +name: 'Filter permissions' +description: 'Provides role and module filters to simplify the user permissions page.' +package: 'Administration' +# core: 8.x +dependencies: + - user + +# Information added by Drupal.org packaging script on 2016-07-27 +version: '8.x-1.x-dev' +core: '8.x' +project: 'filter_perms' +datestamp: 1469646196 diff --git a/sites/all/modules/contrib/admin/filter_perms/filter_perms.services.yml b/sites/all/modules/contrib/admin/filter_perms/filter_perms.services.yml new file mode 100644 index 000000000..b064ea195 --- /dev/null +++ b/sites/all/modules/contrib/admin/filter_perms/filter_perms.services.yml @@ -0,0 +1,5 @@ +services: + filter_perms.route_subscriber: + class: Drupal\filter_perms\Routing\RouteSubscriber + tags: + - { name: event_subscriber } diff --git a/sites/all/modules/contrib/admin/filter_perms/src/Form/PermissionsForm.php b/sites/all/modules/contrib/admin/filter_perms/src/Form/PermissionsForm.php new file mode 100644 index 000000000..9443ce0e7 --- /dev/null +++ b/sites/all/modules/contrib/admin/filter_perms/src/Form/PermissionsForm.php @@ -0,0 +1,261 @@ +keyValueExpirable = $key_value_expirable; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.permissions'), + $container->get('entity.manager')->getStorage('user_role'), + $container->get('module_handler'), + $container->get('keyvalue.expirable')->get('filter_perms_list') + ); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + // Render role/permission overview: + $hide_descriptions = system_admin_compact_mode(); + + $form['system_compact_link'] = array( + '#id' => FALSE, + '#type' => 'system_compact_link', + ); + + $permissions = $this->permissionHandler->getPermissions(); + + $providers = array(); + foreach ($permissions as $permission) { + $providers[$permission['provider']] = $permission['provider']; + } + + $roles = $this->getRoles(); + + $defined_roles = array(); + foreach ($roles as $role_name => $role) { + $defined_roles[$role_name] = $role->label(); + } + + $filter = $this->getFilterSettings(); + + $form['filters'] = array( + '#type' => 'details', + '#title' => $this->t('Permission Filters'), + '#open' => TRUE, + ); + $form['filters']['container'] = array( + '#type' => 'container', + '#attributes' => array('class' => array('form--inline', 'clearfix')), + ); + // Displays all user roles. + $form['filters']['container']['roles'] = array( + '#title' => $this->t('Roles to display'), + '#type' => 'select', + '#options' => array(self::ALL_OPTIONS => '--All Roles') + $defined_roles, + '#default_value' => $filter['roles'], + '#size' => 8, + '#multiple' => TRUE, + ); + // Displays all modules which define permissions. + $form['filters']['container']['modules'] = array( + '#title' => $this->t('Modules to display'), + '#type' => 'select', + '#options' => array(self::ALL_OPTIONS => '--All Modules') + $providers, + '#default_value' => $filter['modules'], + '#size' => 8, + '#multiple' => TRUE, + ); + $form['filters']['action'] = array('#type' => 'actions'); + $form['filters']['action']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Filter Permissions'), + '#submit' => array('::submitFormFilter'), + ); + + $role_names = $role_permissions = $admin_roles = array(); + + foreach ($roles as $role_name => $role) { + if (in_array(self::ALL_OPTIONS, $filter['roles']) || in_array($role_name, $filter['roles'])) { + // Retrieve role names for columns. + $role_names[$role_name] = $role->label(); + // Fetch permissions for the roles. + $role_permissions[$role_name] = $role->getPermissions(); + $admin_roles[$role_name] = $role->isAdmin(); + } + } + + // Store $role_names for use when saving the data. + $form['role_names'] = array( + '#type' => 'value', + '#value' => $role_names, + ); + + $permissions_by_provider = array(); + foreach ($permissions as $permission_name => $permission) { + if (in_array(self::ALL_OPTIONS, $filter['modules']) || in_array($permission['provider'], $filter['modules'])) { + $permissions_by_provider[$permission['provider']][$permission_name] = $permission; + } + } + + $form['permissions'] = array( + '#type' => 'table', + '#header' => array($this->t('Permission')), + '#id' => 'permissions', + '#attributes' => ['class' => ['permissions', 'js-permissions']], + '#sticky' => TRUE, + '#empty' => $this->t('Please select at least one value from both the Roles and Modules select boxes above and then click the "Filter Permissions" button.'), + ); + + // Only build the rest of the form if there are any filter settings. + if (empty($role_names) || empty($permissions_by_provider)) { + return $form; + } + + foreach ($role_names as $name) { + $form['permissions']['#header'][] = array( + 'data' => $name, + 'class' => array('checkbox'), + ); + } + + foreach ($permissions_by_provider as $provider => $permissions) { + // Module name. + $form['permissions'][$provider] = array(array( + '#wrapper_attributes' => array( + 'colspan' => count($role_names) + 1, + 'class' => array('module'), + 'id' => 'module-' . $provider, + ), + '#markup' => $this->moduleHandler->getName($provider), + )); + foreach ($permissions as $perm => $perm_item) { + // Fill in default values for the permission. + $perm_item += array( + 'description' => '', + 'restrict access' => FALSE, + 'warning' => !empty($perm_item['restrict access']) ? $this->t('Warning: Give to trusted roles only; this permission has security implications.') : '', + ); + $form['permissions'][$perm]['description'] = array( + '#type' => 'inline_template', + '#template' => '
{{ title }}{% if description or warning %}
{% if warning %}{{ warning }} {% endif %}{{ description }}
{% endif %}
', + '#context' => array( + 'title' => $perm_item['title'], + ), + ); + // Show the permission description. + if (!$hide_descriptions) { + $form['permissions'][$perm]['description']['#context']['description'] = $perm_item['description']; + $form['permissions'][$perm]['description']['#context']['warning'] = $perm_item['warning']; + } + foreach ($role_names as $rid => $name) { + $form['permissions'][$perm][$rid] = array( + '#title' => $name . ': ' . $perm_item['title'], + '#title_display' => 'invisible', + '#wrapper_attributes' => array( + 'class' => array('checkbox'), + ), + '#type' => 'checkbox', + '#default_value' => in_array($perm, $role_permissions[$rid]) ? 1 : 0, + '#attributes' => array('class' => array('rid-' . $rid, 'js-rid-' . $rid)), + '#parents' => array($rid, $perm), + ); + // Show a column of disabled but checked checkboxes. + if ($admin_roles[$rid]) { + $form['permissions'][$perm][$rid]['#disabled'] = TRUE; + $form['permissions'][$perm][$rid]['#default_value'] = TRUE; + } + } + } + } + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Save permissions'), + '#button_type' => 'primary', + ); + + $form['#attached']['library'][] = 'user/drupal.user.permissions'; + + return $form; + } + + /** + * Saves the roles and modules selection. + */ + public function submitFormFilter(array &$form, FormStateInterface $form_state) { + $this->saveFilterSettings($form_state->getValue('roles'), $form_state->getValue('modules')); + } + + /** + * Saves the filter settings for the current user. + * + * @param array $roles + * The roles to filter by. + * @param array $modules + * The modules to filter by. + */ + protected function saveFilterSettings(array $roles, array $modules) { + $values = array('roles' => $roles, 'modules' => $modules); + $this->keyValueExpirable->setWithExpire($this->currentUser()->id(), $values, 3600); + } + + /** + * Retrieve the filter settings for the current user. + * + * @return array + * The filter setting for the current user. + */ + protected function getFilterSettings() { + $default = array('roles' => array(), 'modules' => array()); + return $this->keyValueExpirable->get($this->currentUser()->id(), $default); + } + +} diff --git a/sites/all/modules/contrib/admin/filter_perms/src/Form/PermissionsRoleSpecificForm.php b/sites/all/modules/contrib/admin/filter_perms/src/Form/PermissionsRoleSpecificForm.php new file mode 100644 index 000000000..14e594c6a --- /dev/null +++ b/sites/all/modules/contrib/admin/filter_perms/src/Form/PermissionsRoleSpecificForm.php @@ -0,0 +1,24 @@ +saveFilterSettings((array) $user_role->id(), (array) self::ALL_OPTIONS); + return $this->redirect('user.admin_permissions'); + } + +} diff --git a/sites/all/modules/contrib/admin/filter_perms/src/Routing/RouteSubscriber.php b/sites/all/modules/contrib/admin/filter_perms/src/Routing/RouteSubscriber.php new file mode 100644 index 000000000..7998c93fb --- /dev/null +++ b/sites/all/modules/contrib/admin/filter_perms/src/Routing/RouteSubscriber.php @@ -0,0 +1,25 @@ +get('user.admin_permissions')) { + $route->setDefault('_form', '\Drupal\filter_perms\Form\PermissionsForm'); + } + if ($route = $collection->get('entity.user_role.edit_permissions_form')) { + $route->setDefault('_form', '\Drupal\filter_perms\Form\PermissionsRoleSpecificForm'); + } + } + +} diff --git a/sites/all/modules/contrib/dev/devel/LICENSE.txt b/sites/all/modules/contrib/dev/devel/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General +Public License instead of this License. diff --git a/sites/all/modules/contrib/dev/devel/README.txt b/sites/all/modules/contrib/dev/devel/README.txt new file mode 100644 index 000000000..75bae16eb --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/README.txt @@ -0,0 +1,51 @@ +Devel +========== +Devel module contains helper functions and pages for Drupal developers and inquisitive admins: + + - A block for running custom PHP on a page + - A block for quickly accessing devel pages + - A block for masquerading as other users (useful for testing) + - A mail-system class which redirects outbound email to files + - Drush commands such as fn-hook, fn-event, ... + - Docs at https://api.drupal.org/api/devel + - more + +This module is safe to use on a production site. Just be sure to only grant +'access development information' permission to developers. + +Devel Kint +=================== +Provides a dpr() function, which pretty prints variables. +Useful during development. Also see similar helpers like dpm(), dvm(). + +Webprofiler +============== +Adds a debug bar at bottom of all pages with tons of useful information like a query list, +cache hit/miss data, memory profiling, page speed, php info, session info, etc. + +Devel Generate +================= +Bulk creates nodes, users, comment, terms for development. Has Drush integration. + +Devel Node Access +================= +Prints the node_access records for a given node. Also offers hook_node_access_explain for all node access modules to implement. + +Devel Generate Extensions +========================= +Devel Images Provider [http://drupal.org/project/devel_image_provider] allows to configure external providers for images. + +Drush Unit Testing +================== +See develDrushTest.php for an example of unit testing of the Drush integration. +This uses Drush's own test framework, based on PHPUnit. To run the tests, use +run-tests-drush.sh. You may pass in any arguments that are valid for `phpunit`. + +Author/Maintainers +====================== +- Moshe Weitzman http://www.acquia.com +- Hans Salvisberg +- Pedro Cambra https://drupal.org/user/122101/contact http://www.ymbra.com/ +- Juan Pablo Novillo https://www.drupal.org/u/juampynr +- lussoluca https://www.drupal.org/u/lussoluca +- willzyx https://www.drupal.org/u/willzyx diff --git a/sites/all/modules/contrib/dev/devel/composer.json b/sites/all/modules/contrib/dev/devel/composer.json new file mode 100644 index 000000000..db52e40ed --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/composer.json @@ -0,0 +1,25 @@ +{ + "name": "drupal/devel", + "description": "Various blocks, pages, and functions for developers.", + "type": "drupal-module", + "homepage": "http://drupal.org/project/devel", + "authors": [ + { + "name": "Moshe Weitzman", + "email": "weitzman@tejasa.com", + "homepage": "https://github.com/weitzman", + "role": "Maintainer" + } + ], + "support": { + "issues": "http://drupal.org/project/devel", + "irc": "irc://irc.freenode.org/drupal-contribute", + "source": "http://cgit.drupalcode.org/devel" + }, + "license": "GPL-2.0+", + "minimum-stability": "dev", + "require": { }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available" + } +} diff --git a/sites/all/modules/contrib/dev/devel/config/install/devel.settings.yml b/sites/all/modules/contrib/dev/devel/config/install/devel.settings.yml new file mode 100644 index 000000000..66501f0fa --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/config/install/devel.settings.yml @@ -0,0 +1,8 @@ +page_alter: FALSE +raw_names: FALSE +error_handlers: + 1: 1 +rebuild_theme: FALSE +debug_mail_file_format: '%to-%subject-%datetime.mail.txt' +debug_mail_directory: 'temporary://devel-mails' +devel_dumper: 'default' diff --git a/sites/all/modules/contrib/dev/devel/config/install/system.menu.devel.yml b/sites/all/modules/contrib/dev/devel/config/install/system.menu.devel.yml new file mode 100644 index 000000000..21fb00214 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/config/install/system.menu.devel.yml @@ -0,0 +1,9 @@ +id: devel +label: 'Development' +description: 'Links related to Devel module.' +langcode: en +locked: true +dependencies: + enforced: + module: + - devel diff --git a/sites/all/modules/contrib/dev/devel/config/schema/devel.schema.yml b/sites/all/modules/contrib/dev/devel/config/schema/devel.schema.yml new file mode 100644 index 000000000..4931709f9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/config/schema/devel.schema.yml @@ -0,0 +1,43 @@ +# Schema for the configuration files of the Devel module. + +devel.settings: + type: config_object + label: 'Devel settings' + mapping: + page_alter: + type: boolean + label: 'Page alter' + raw_names: + type: boolean + label: 'Raw names' + error_handlers: + label: 'Error handlers' + type: sequence + sequence: + type: integer + rebuild_theme: + type: boolean + label: 'Rebuild theme information' + debug_mail_file_format: + type: string + label: 'Mail debug file format' + debug_mail_directory: + type: string + label: 'Mail debug directory' + devel_dumper: + type: string + label: 'Devel variable dumper' + +block.settings.devel_switch_user: + type: block_settings + label: 'Switch user block' + mapping: + list_size: + type: integer + label: 'List size' + include_anon: + type: boolean + label: 'Include Anonymous user' + show_form: + type: boolean + label: 'Show search form' diff --git a/sites/all/modules/contrib/dev/devel/css/devel-rtl.css b/sites/all/modules/contrib/dev/devel/css/devel-rtl.css new file mode 100644 index 000000000..e69de29bb diff --git a/sites/all/modules/contrib/dev/devel/css/devel.css b/sites/all/modules/contrib/dev/devel/css/devel.css new file mode 100644 index 000000000..0abe966a3 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/css/devel.css @@ -0,0 +1,42 @@ +/** + * Dumpers + */ +.devel-dumper .details-wrapper{ + max-height: 450px; + margin-right: 3px; + overflow: auto; +} + +/** + * Switch User block + */ +#content .block-devel-switch-user ul.links, +#footer .block-devel-switch-user ul.links { + margin-left: 0; + padding-left: 0; +} + +#content .block-devel-switch-user ul.links li, +#footer .block-devel-switch-user ul.links li { + display: inline-block; + margin-left: 0; + margin-right: 1.5em; + padding-right: 0; +} + +#footer .block-devel-switch-user ul.links li a { + margin: 0; + padding: 0; + border: none; +} + +#content #devel-switch-user-form .form-item-username, +#footer #devel-switch-user-form .form-item-username { + float: left; + margin-right: 2em; +} + +#content #devel-switch-user-form .button-primary, +#footer #devel-switch-user-form .button-primary { + margin-top: 1em; +} diff --git a/sites/all/modules/contrib/dev/devel/devel.api.php b/sites/all/modules/contrib/dev/devel/devel.api.php new file mode 100644 index 000000000..4a413db6e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel.api.php @@ -0,0 +1,25 @@ +moduleExists('kint'); + + $default_dumper = $kint_enabled ? 'kint' : 'default'; + + // Set the default dumper plugin to kint if kint module is available. + \Drupal::configFactory()->getEditable('devel.settings') + ->set('devel_dumper', $default_dumper) + ->save(TRUE); +} + +/** + * Add enforced dependencies to system.menu.devel + */ +function devel_update_8002() { + $config = \Drupal::configFactory()->getEditable('system.menu.devel'); + $dependencies = $config->get('dependencies'); + $dependencies['enforced']['module'][] = 'devel'; + $config->set('dependencies', $dependencies)->save(TRUE); +} diff --git a/sites/all/modules/contrib/dev/devel/devel.libraries.yml b/sites/all/modules/contrib/dev/devel/devel.libraries.yml new file mode 100644 index 000000000..b64362408 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel.libraries.yml @@ -0,0 +1,5 @@ +devel: + version: 0 + css: + theme: + css/devel.css: {} diff --git a/sites/all/modules/contrib/dev/devel/devel.links.menu.yml b/sites/all/modules/contrib/dev/devel/devel.links.menu.yml new file mode 100644 index 000000000..b1a995fa0 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel.links.menu.yml @@ -0,0 +1,75 @@ +devel.admin_settings: + title: 'Devel settings' + description: 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the block administration page.' + route_name: devel.admin_settings + parent: 'system.admin_config_development' +devel.admin_settings_link: + title: 'Devel settings' + description: 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the block administration page.' + route_name: devel.admin_settings + menu_name: devel +devel.configs_list: + title: 'Config editor' + description: 'Edit configuration.' + route_name: devel.configs_list + menu_name: devel +devel.reinstall: + title: 'Reinstall Modules' + route_name: devel.reinstall + menu_name: devel + class: \Drupal\devel\Plugin\Menu\DestinationMenuLink +devel.menu_rebuild: + title: 'Rebuild Menu' + route_name: devel.menu_rebuild + menu_name: devel + class: \Drupal\devel\Plugin\Menu\DestinationMenuLink +devel.menu_item: + title: 'Menu Item' + route_name: devel.menu_item + menu_name: devel + class: \Drupal\devel\Plugin\Menu\MenuItemMenuLink +devel.state_system_page: + title: 'State editor' + description: 'Edit state system values.' + route_name: devel.state_system_page + menu_name: devel +devel.theme_registry: + title: 'Theme registry' + route_name: devel.theme_registry + menu_name: devel +devel.entity_info_page: + title: 'Entity Info' + route_name: devel.entity_info_page +# parent: 'system.admin_config_development' + menu_name: devel +devel.field_info_page: + title: 'Field Info' + route_name: devel.field_info_page +# parent: 'system.admin_config_development' + menu_name: devel +devel.phpinfo: + title: 'PHPinfo()' + route_name: system.php + menu_name: devel +devel.execute_php: + title: 'Execute PHP' + route_name: devel.execute_php + menu_name: devel +devel.session: + title: 'View Session' + route_name: devel.session + menu_name: devel +devel.elements_page: + title: 'Element Info' + route_name: devel.elements_page + menu_name: devel +devel.cache_clear: + title: 'Cache clear' + route_name: devel.cache_clear + menu_name: devel + class: \Drupal\devel\Plugin\Menu\DestinationMenuLink +devel.run_cron: + title: 'Run cron' + route_name: devel.run_cron + menu_name: devel + class: \Drupal\devel\Plugin\Menu\DestinationMenuLink diff --git a/sites/all/modules/contrib/dev/devel/devel.links.task.yml b/sites/all/modules/contrib/dev/devel/devel.links.task.yml new file mode 100644 index 000000000..dd33939f0 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel.links.task.yml @@ -0,0 +1,8 @@ +devel.entities: + class: \Drupal\Core\Menu\LocalTaskDefault + deriver: \Drupal\devel\Plugin\Derivative\DevelLocalTask +devel.admin_settings: + title: 'Settings' + route_name: devel.admin_settings + base_route: devel.admin_settings + weight: 0 diff --git a/sites/all/modules/contrib/dev/devel/devel.module b/sites/all/modules/contrib/dev/devel/devel.module new file mode 100644 index 000000000..5a48cbc84 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel.module @@ -0,0 +1,626 @@ +' . t('Warning - will delete your module tables and configuration.') . '

'; + $output .= '

' . t('Uninstall and then install the selected modules. hook_uninstall() and hook_install() will be executed and the schema version number will be set to the most recent update number.') . '

'; + return $output; + + case 'devel/session': + return '

' . t('Here are the contents of your $_SESSION variable.') . '

'; + + case 'devel.state_system_page': + return '

' . t('This is a list of state variables and their values. For more information read online documentation of State API in Drupal 8.', array(':documentation' => "https://www.drupal.org/developing/api/8/state")) . '

'; + + } +} + +/** + * Implements hook_entity_type_alter(). + */ +function devel_entity_type_alter(array &$entity_types) { + /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ + foreach ($entity_types as $entity_type_id => $entity_type) { + if ($entity_type->hasViewBuilderClass() && $entity_type->hasLinkTemplate('canonical')) { + $entity_type->setLinkTemplate('devel-render', "/devel/$entity_type_id/{{$entity_type_id}}/render"); + } + if (($entity_type->getFormClass('default') || $entity_type->getFormClass('edit')) && $entity_type->hasLinkTemplate('edit-form')) { + $entity_type->setLinkTemplate('devel-load', "/devel/$entity_type_id/{{$entity_type_id}}"); + } + } +} + +/** + * Implements hook_entity_operation(). + */ +function devel_entity_operation(EntityInterface $entity) { + $operations = array(); + if (\Drupal::currentUser()->hasPermission('access devel information')) { + if ($entity->hasLinkTemplate('devel-load')) { + $operations['devel'] = array( + 'title' => t('Devel'), + 'weight' => 100, + 'url' => $entity->toUrl('devel-load'), + ); + } + elseif ($entity->hasLinkTemplate('devel-render')) { + $operations['devel'] = array( + 'title' => t('Devel'), + 'weight' => 100, + 'url' => $entity->toUrl('devel-render'), + ); + } + } + return $operations; +} + +/** + * Sets message. + */ +function devel_set_message($msg, $type = NULL) { + if (function_exists('drush_log')) { + drush_log($msg, $type); + } + else { + drupal_set_message($msg, $type, TRUE); + } +} + +/** + * Gets error handlers. + */ +function devel_get_handlers() { + $error_handlers = \Drupal::config('devel.settings')->get('error_handlers'); + if (!empty($error_handlers)) { + unset($error_handlers[DEVEL_ERROR_HANDLER_NONE]); + } + return $error_handlers; +} + +/** + * Sets a new error handler or restores the prior one. + */ +function devel_set_handler($handlers) { + if (empty($handlers)) { + restore_error_handler(); + } + elseif (count($handlers) == 1 && isset($handlers[DEVEL_ERROR_HANDLER_STANDARD])) { + // Do nothing. + } + else { + set_error_handler('backtrace_error_handler'); + } +} + +/** + * Displays backtrace showing the route of calls to the current error. + * + * @param int $error_level + * The level of the error raised. + * @param string $message + * The error message. + * @param string $filename + * The filename that the error was raised in. + * @param int $line + * The line number the error was raised at. + * @param array $context + * An array that points to the active symbol table at the point the error + * occurred. + */ +function backtrace_error_handler($error_level, $message, $filename, $line, $context) { + // Hide stack trace and parameters from unqualified users. + if (!\Drupal::currentUser()->hasPermission('access devel information')) { + // Do what core does in bootstrap.inc and errors.inc. + // (We need to duplicate the core code here rather than calling it + // to avoid having the backtrace_error_handler() on top of the call stack.) + if ($error_level & error_reporting()) { + $types = drupal_error_levels(); + list($severity_msg, $severity_level) = $types[$error_level]; + $backtrace = debug_backtrace(); + $caller = Error::getLastCaller($backtrace); + + // We treat recoverable errors as fatal. + _drupal_log_error(array( + '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error', + '@message' => $message, + '%function' => $caller['function'], + '%file' => $caller['file'], + '%line' => $caller['line'], + 'severity_level' => $severity_level, + 'backtrace' => $backtrace, + ), $error_level == E_RECOVERABLE_ERROR); + } + + return; + } + + // Don't respond to the error if it was suppressed with a '@' + if (error_reporting() == 0) { + return; + } + + // Don't respond to warning caused by ourselves. + if (preg_match('#Cannot modify header information - headers already sent by \\([^\\)]*[/\\\\]devel[/\\\\]#', $message)) { + return; + } + + if ($error_level & error_reporting()) { + // Only write each distinct NOTICE message once, as repeats do not give any + // further information and can choke the page output. + if ($error_level == E_NOTICE) { + static $written = array(); + if (!empty($written[$line][$filename][$message])) { + return; + } + $written[$line][$filename][$message] = TRUE; + } + + $types = drupal_error_levels(); + list($severity_msg, $severity_level) = $types[$error_level]; + + $backtrace = debug_backtrace(); + $caller = Error::getLastCaller($backtrace); + $variables = array( + '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error', + '@message' => $message, + '%function' => $caller['function'], + '%file' => $caller['file'], + '%line' => $caller['line'], + ); + $msg = t('%type: @message in %function (line %line of %file).', $variables); + + // Show message if error_level is ERROR_REPORTING_DISPLAY_SOME or higher. + // (This is Drupal's error_level, which is different from $error_level, + // and we purposely ignore the difference between _SOME and _ALL, + // see #970688!) + if (\Drupal::config('system.logging')->get('error_level') != 'hide') { + $error_handlers = devel_get_handlers(); + if (!empty($error_handlers[DEVEL_ERROR_HANDLER_STANDARD])) { + drupal_set_message($msg, ($severity_level <= RfcLogLevel::NOTICE ? 'error' : 'warning'), TRUE); + } + if (!empty($error_handlers[DEVEL_ERROR_HANDLER_BACKTRACE_KINT])) { + print kprint_r(ddebug_backtrace(TRUE, 1), $return = TRUE, $msg); + } + if (!empty($error_handlers[DEVEL_ERROR_HANDLER_BACKTRACE_DPM])) { + dpm(ddebug_backtrace(TRUE, 1), $msg, 'warning'); + } + } + + \Drupal::logger('php')->log($severity_level, $msg); + } +} + +/** + * Implements hook_page_attachments_alter(). + */ +function devel_page_attachments_alter(&$page) { + if (\Drupal::currentUser()->hasPermission('access devel information') && \Drupal::config('devel.settings')->get('page_alter')) { + dpm($page, 'page'); + } +} + +/** + * Prints an object using either Kint (if enabled) or devel_print_object(). + * + * @param array|object $object + * An array or object to print. + * @param string $prefix + * @todo: this parameter is not needed with Kint. + * Prefix for output items. + * + * @deprecated in Devel 8.x-dev, will be removed before Devel 8.0. + * Use kpr() or devel.dumper service instead. + * + * @TODO remove in https://www.drupal.org/node/2703343 + */ +function kdevel_print_object($object, $prefix = NULL) { + return kpr($object, TRUE, $prefix); +} + +/** + * Wrapper for DevelDumperManager::dump(). + * + * Calls the http://www.firephp.org/ fb() function if it is found. + * + * @see \Drupal\devel\DevelDumperManager::dump() + */ +function dfb() { + $args = func_get_args(); + \Drupal::service('devel.dumper')->dump($args, NULL, 'firephp'); +} + +/** + * Wrapper for DevelDumperManager::dump(). + * + * Calls dfb() to output a backtrace. + * + * @see \Drupal\devel\DevelDumperManager::dump() + */ +function dfbt($label) { + \Drupal::service('devel.dumper')->dump(FirePHP::TRACE, $label, 'firephp'); +} + +/** + * Wrapper for DevelDumperManager::dump(). + * + * Wrapper for ChromePHP Class log method. + * + * @see \Drupal\devel\DevelDumperManager::dump() + */ +function dcp() { + $args = func_get_args(); + \Drupal::service('devel.dumper')->dump($args, NULL, 'chromephp'); +} + +if (!function_exists('dd')) { + /** + * Wrapper for DevelDumperManager::debug(). + * + * @see \Drupal\devel\DevelDumperManager::debug() + */ + function dd($data, $label = NULL) { + return \Drupal::service('devel.dumper')->debug($data, $label, 'default'); + } +} + +/** + * Wrapper for DevelDumperManager::message(). + * + * Prints a variable to the 'message' area of the page. + * + * Uses drupal_set_message(). + * + * @param $input + * An arbitrary value to output. + * @param string $name + * Optional name for identifying the output. + * @param string $type + * Optional message type for drupal_set_message(), defaults to 'status'. + * + * @return input + * The unaltered input value. + * + * @see \Drupal\devel\DevelDumperManager::message() + */ +function dpm($input, $name = NULL, $type = 'status') { + \Drupal::service('devel.dumper')->message($input, $name, $type); + return $input; +} + +/** + * Wrapper for DevelDumperManager::message(). + * + * Displays a Variable::export() variable to the 'message' area of the page. + * + * Uses drupal_set_message(). + * + * @param $input + * An arbitrary value to output. + * @param string $name + * Optional name for identifying the output. + * + * @return input + * The unaltered input value. + * + * @see \Drupal\devel\DevelDumperManager::message() + */ +function dvm($input, $name = NULL) { + \Drupal::service('devel.dumper')->message($input, $name, 'status', 'drupal_variable'); + return $input; +} + +/** + * An alias for dpm(), for historic reasons. + */ +function dsm($input, $name = NULL) { + return dpm($input, $name); +} + +/** + * Wrapper for DevelDumperManager::dumpOrExport(). + * + * An alias for the devel.dumper service. Saves carpal tunnel syndrome. + * + * @see \Drupal\devel\DevelDumperManager::dumpOrExport() + */ +function dpr($input, $export = FALSE, $name = NULL) { + return \Drupal::service('devel.dumper')->dumpOrExport($input, $name, $export, 'default'); +} + +/** + * Wrapper for DevelDumperManager::dumpOrExport(). + * + * An alias for devel_dump(). Saves carpal tunnel syndrome. + * + * @see \Drupal\devel\DevelDumperManager::dumpOrExport() + */ +function kpr($input, $export = FALSE, $name = NULL) { + return \Drupal::service('devel.dumper')->dumpOrExport($input, $name, $export); +} + +/** + * Kint print. + * + * @deprecated in Devel 8.x-dev, will be removed before Devel 8.0. + * Use kpr() or devel.dumper service instead. + * + * @TODO remove in https://www.drupal.org/node/2703343 + */ +function kprint_r($input, $export = FALSE, $name = NULL, $function = 'print_r') { + return kpr($input, $export, $name); +} + +/** + * Wrapper for DevelDumperManager::dumpOrExport(). + * + * Like dpr(), but uses Variable::export() instead. + * + * @see \Drupal\devel\DevelDumperManager::dumpOrExport() + */ +function dvr($input, $export = FALSE, $name = NULL) { + return \Drupal::service('devel.dumper')->dumpOrExport($input, $name, $export, 'drupal_variable'); +} + +/** + * Prints the arguments passed into the current function. + */ +function dargs($always = TRUE) { + static $printed; + if ($always || !$printed) { + $bt = debug_backtrace(); + print kdevel_print_object($bt[1]['args']); + $printed = TRUE; + } +} + +/** + * Prints a SQL string from a DBTNG Select object. Includes quoted arguments. + * + * @param object $query + * An object that implements the SelectInterface interface. + * @param boolean $return + * Whether to return the string. Default is FALSE, meaning to print it + * and return $query instead. + * @param string $name + * Optional name for identifying the output. + * + * @return object|string + * The $query object, or the query string if $return was TRUE. + */ +function dpq($query, $return = FALSE, $name = NULL) { + if (\Drupal::currentUser()->hasPermission('access devel information')) { + if (method_exists($query, 'preExecute')) { + $query->preExecute(); + } + $sql = (string) $query; + $quoted = array(); + $connection = Database::getConnection(); + foreach ((array) $query->arguments() as $key => $val) { + $quoted[$key] = is_null($val) ? 'NULL' : $connection->quote($val); + } + $sql = strtr($sql, $quoted); + if ($return) { + return $sql; + } + dpm($sql, $name); + } + return ($return ? NULL : $query); +} + +/** + * Prints a renderable array element to the screen using kprint_r(). + * + * #pre_render and/or #post_render pass-through callback for kprint_r(). + * + * @todo Investigate appending to #suffix. + * @todo Investigate label derived from #id, #title, #name, and #theme. + */ +function devel_render() { + $args = func_get_args(); + // #pre_render and #post_render pass the rendered $element as last argument. + kprint_r(end($args)); + // #pre_render and #post_render expect the first argument to be returned. + return reset($args); +} + +/** + * Prints the function call stack. + * + * @param $return + * Pass TRUE to return the formatted backtrace rather than displaying it in + * the browser via kprint_r(). + * @param $pop + * How many items to pop from the top of the stack; useful when calling from + * an error handler. + * @param $options + * Options to pass on to PHP's debug_backtrace(). + * + * @return string|NULL + * The formatted backtrace, if requested, or NULL. + * + * @see http://php.net/manual/en/function.debug-backtrace.php + */ +function ddebug_backtrace($return = FALSE, $pop = 0, $options = DEBUG_BACKTRACE_PROVIDE_OBJECT) { + if (\Drupal::currentUser()->hasPermission('access devel information')) { + $backtrace = debug_backtrace($options); + while ($pop-- > 0) { + array_shift($backtrace); + } + $counter = count($backtrace); + $path = $backtrace[$counter - 1]['file']; + $path = substr($path, 0, strlen($path) - 10); + $paths[$path] = strlen($path) + 1; + $paths[DRUPAL_ROOT] = strlen(DRUPAL_ROOT) + 1; + $nbsp = "\xC2\xA0"; + + // Show message if error_level is ERROR_REPORTING_DISPLAY_SOME or higher. + // (This is Drupal's error_level, which is different from $error_level, + // and we purposely ignore the difference between _SOME and _ALL, + // see #970688!) + if (\Drupal::config('system.logging')->get('error_level') != 'hide') { + while (!empty($backtrace)) { + $call = array(); + if (isset($backtrace[0]['file'])) { + $call['file'] = $backtrace[0]['file']; + foreach ($paths as $path => $len) { + if (strpos($backtrace[0]['file'], $path) === 0) { + $call['file'] = substr($backtrace[0]['file'], $len); + } + } + $call['file'] .= ':' . $backtrace[0]['line']; + } + if (isset($backtrace[1])) { + if (isset($backtrace[1]['class'])) { + $function = $backtrace[1]['class'] . $backtrace[1]['type'] . $backtrace[1]['function'] . '()'; + } + else { + $function = $backtrace[1]['function'] . '()'; + } + $backtrace[1] += array('args' => array()); + foreach ($backtrace[1]['args'] as $key => $value) { + $call['args'][$key] = $value; + } + } + else { + $function = 'main()'; + $call['args'] = $_GET; + } + $nicetrace[($counter <= 10 ? $nbsp : '') . --$counter . ': ' . $function] = $call; + array_shift($backtrace); + } + if ($return) { + return $nicetrace; + } + kprint_r($nicetrace); + } + } +} + +/* + * Migration-related functions. + */ + +/** + * Regenerates the data in node_comment_statistics table. + * Technique - http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#101 + * + * @return void + */ +function devel_rebuild_node_comment_statistics() { + // Empty table. + db_truncate('node_comment_statistics')->execute(); + + // TODO: DBTNG. Ignore keyword is Mysql only? Is only used in the rare case + // when two comments on the same node share same timestamp. + $sql = " + INSERT IGNORE INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) ( + SELECT c.nid, c.cid, c.created, c.name, c.uid, c2.comment_count FROM {comment} c + JOIN ( + SELECT c.nid, MAX(c.created) AS created, COUNT(*) AS comment_count FROM {comment} c WHERE status = 1 GROUP BY c.nid + ) AS c2 ON c.nid = c2.nid AND c.created = c2.created + )"; + db_query($sql, array(':published' => CommentInterface::PUBLISHED)); + + // Insert records into the node_comment_statistics for nodes that are missing. + $query = db_select('node', 'n'); + $query->leftJoin('node_comment_statistics', 'ncs', 'ncs.nid = n.nid'); + $query->addField('n', 'changed', 'last_comment_timestamp'); + $query->addField('n', 'uid', 'last_comment_uid'); + $query->addField('n', 'nid'); + $query->addExpression('0', 'comment_count'); + $query->addExpression('NULL', 'last_comment_name'); + $query->isNull('ncs.comment_count'); + + db_insert('node_comment_statistics', array('return' => Database::RETURN_NULL)) + ->from($query) + ->execute(); +} + +/** + * Implements hook_form_FORM_ID_alter(). + * + * Adds mouse-over hints on the Permissions page to display + * language-independent machine names and module base names. + * + * @see \Drupal\user\Form\UserPermissionsForm::buildForm() + */ +function devel_form_user_admin_permissions_alter(&$form, FormStateInterface $form_state) { + if (\Drupal::currentUser()->hasPermission('access devel information') && \Drupal::config('devel.settings')->get('raw_names')) { + foreach (Element::children($form['permissions']) as $key) { + if (isset($form['permissions'][$key][0])) { + $form['permissions'][$key][0]['#wrapper_attributes']['title'] = $key; + } + elseif(isset($form['permissions'][$key]['description'])) { + $form['permissions'][$key]['description']['#wrapper_attributes']['title'] = $key; + } + } + } +} + +/** + * Implements hook_form_FORM_ID_alter(). + * + * Adds mouse-over hints on the Modules page to display module base names. + * + * @see \Drupal\system\Form\ModulesListForm::buildForm() + * @see theme_system_modules_details() + */ +function devel_form_system_modules_alter(&$form, FormStateInterface $form_state) { + if (\Drupal::currentUser()->hasPermission('access devel information') && \Drupal::config('devel.settings')->get('raw_names', FALSE) && isset($form['modules']) && is_array($form['modules'])) { + foreach (Element::children($form['modules']) as $group) { + if (is_array($form['modules'][$group])) { + foreach (Element::children($form['modules'][$group]) as $key) { + if (isset($form['modules'][$group][$key]['name']['#markup'])) { + $form['modules'][$group][$key]['name']['#markup'] = '' . $form['modules'][$group][$key]['name']['#markup'] . ''; + } + } + } + } + } +} + +/** + * Implements hook_query_TAG_alter(). + * + * Makes debugging entity query much easier. + * + * Example usage: + * @code + * $query = \Drupal::entityQuery('node'); + * $query->condition('status', NODE_PUBLISHED); + * $query->addTag('debug'); + * $query->execute(); + * @endcode + */ +function devel_query_debug_alter(AlterableInterface $query) { + if (!$query->hasTag('debug-semaphore')) { + $query->addTag('debug-semaphore'); + dpq($query); + } +} diff --git a/sites/all/modules/contrib/dev/devel/devel.permissions.yml b/sites/all/modules/contrib/dev/devel/devel.permissions.yml new file mode 100644 index 000000000..256919de9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel.permissions.yml @@ -0,0 +1,14 @@ +access devel information: + description: 'View developer output like variable printouts, query log, etc.' + title: 'Access developer information' + restrict access: TRUE + +execute php code: + title: 'Execute PHP code' + description: 'Run arbitrary PHP from a block.' + restrict access: TRUE + +switch users: + title: 'Switch users' + description: 'Become any user on the site with just a click.' + restrict access: TRUE diff --git a/sites/all/modules/contrib/dev/devel/devel.routing.yml b/sites/all/modules/contrib/dev/devel/devel.routing.yml new file mode 100644 index 000000000..bfbb70c3e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel.routing.yml @@ -0,0 +1,172 @@ +devel.admin_settings: + path: '/admin/config/development/devel' + defaults: + _form: '\Drupal\devel\Form\SettingsForm' + _title: 'Devel settings' + requirements: + _permission: 'administer site configuration' + +devel.reinstall: + path: '/devel/reinstall' + defaults: + _form: '\Drupal\devel\Form\DevelReinstall' + _title: 'Reinstall modules' + options: + _admin_route: TRUE + requirements: + _permission: 'administer site configuration' + +devel.menu_rebuild: + path: '/devel/menu/reset' + defaults: + _form: '\Drupal\devel\Form\DevelRebuildMenus' + _title: 'Rebuild menus' + options: + _admin_route: TRUE + requirements: + _permission: 'administer site configuration' + +devel.configs_list: + path: '/devel/config/{filter}' + options: + _admin_route: TRUE + defaults: + _form: '\Drupal\devel\Form\ConfigsList' + _title: 'Config editor' + filter: '' + requirements: + _permission: 'administer site configuration' + +devel.config_edit: + path: '/devel/config/edit/{config_name}' + defaults: + _form: '\Drupal\devel\Form\ConfigEditor' + _title: 'Edit configuration object: !config_name' + options: + _admin_route: TRUE + requirements: + _permission: 'administer site configuration' + +devel.state_system_page: + path: '/devel/state' + defaults: + _controller: '\Drupal\devel\Controller\DevelController::stateSystemPage' + _title: 'State editor' + options: + _admin_route: TRUE + requirements: + _permission: 'access devel information' + +devel.system_state_edit: + path: '/devel/state/edit/{state_name}' + defaults: + _form: '\Drupal\devel\Form\SystemStateEdit' + _title: 'Edit state variable: @state_name' + options: + _admin_route: TRUE + requirements: + _permission: 'administer site configuration' + +devel.menu_item: + path: '/devel/menu/item' + defaults: + _controller: '\Drupal\devel\Controller\DevelController::menuItem' + _title: 'Menu item' + options: + _admin_route: TRUE + requirements: + _permission: 'access devel information' + +devel.theme_registry: + path: '/devel/theme/registry' + defaults: + _controller: '\Drupal\devel\Controller\DevelController::themeRegistry' + _title: 'Theme registry' + options: + _admin_route: TRUE + requirements: + _permission: 'access devel information' + +devel.entity_info_page: + path: '/devel/entity/info' + defaults: + _controller: '\Drupal\devel\Controller\DevelController::entityInfoPage' + _title: 'Entity info' + options: + _admin_route: TRUE + requirements: + _permission: 'access devel information' + +devel.field_info_page: + path: '/devel/field/info' + defaults: + _controller: '\Drupal\devel\Controller\DevelController::fieldInfoPage' + _title: 'Field info' + options: + _admin_route: TRUE + requirements: + _permission: 'access devel information' + +devel.execute_php: + path: '/devel/php' + defaults: + _form: '\Drupal\devel\Form\ExecutePHP' + _title: 'Execute PHP code' + options: + _admin_route: TRUE + requirements: + _permission: 'execute php code' + +devel.session: + path: '/devel/session' + defaults: + _controller: '\Drupal\devel\Controller\DevelController::session' + _title: 'Session viewer' + options: + _admin_route: TRUE + requirements: + _permission: 'access devel information' + +devel.switch: + path: '/devel/switch/{name}' + defaults: + _controller: '\Drupal\devel\Controller\SwitchUserController::switchUser' + _title: 'Switch user' + name: '' + options: + _admin_route: TRUE + requirements: + _permission: 'switch users' + _csrf_token: 'TRUE' + +devel.elements_page: + path: '/devel/elements' + defaults: + _controller: '\Drupal\devel\Controller\DevelController::elementsPage' + _title: 'Element Info' + options: + _admin_route: TRUE + requirements: + _permission: 'access devel information' + +devel.cache_clear: + path: '/devel/cache/clear' + defaults: + _controller: '\Drupal\devel\Controller\DevelController::cacheClear' + _title: 'Clear cache' + options: + _admin_route: TRUE + requirements: + _permission: 'access devel information' + _csrf_token: 'TRUE' + +devel.run_cron: + path: '/devel/run-cron' + defaults: + _controller: '\Drupal\system\CronController::runManually' + _title: 'Run cron' + options: + _admin_route: TRUE + requirements: + _permission: 'administer site configuration' + _csrf_token: 'TRUE' diff --git a/sites/all/modules/contrib/dev/devel/devel.rules.inc b/sites/all/modules/contrib/dev/devel/devel.rules.inc new file mode 100644 index 000000000..0a7a07808 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel.rules.inc @@ -0,0 +1,24 @@ + array( + 'base' => 'devel_rules_debug_action', + 'label' => t('Debug value'), + 'group' => t('Devel'), + 'parameter' => array( + 'value' => array('type' => 'unknown', 'label' => t('Value to debug')), + ), + ), + ); +} + +/** + * Rules action for debugging values. + */ +function devel_rules_debug_action($value) { + dpm($value); +} diff --git a/sites/all/modules/contrib/dev/devel/devel.services.yml b/sites/all/modules/contrib/dev/devel/devel.services.yml new file mode 100644 index 000000000..57e44aa04 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel.services.yml @@ -0,0 +1,26 @@ +services: + devel.event_subscriber: + class: Drupal\devel\EventSubscriber\DevelEventSubscriber + arguments: ['@config.factory', '@current_user', '@module_handler', '@url_generator'] + tags: + - { name: event_subscriber } + + plugin.manager.devel_dumper: + class: Drupal\devel\DevelDumperPluginManager + parent: default_plugin_manager + + devel.dumper: + class: Drupal\devel\DevelDumperManager + arguments: ['@config.factory', '@current_user', '@plugin.manager.devel_dumper'] + + devel.route_subscriber: + class: Drupal\devel\Routing\RouteSubscriber + arguments: ['@entity_type.manager'] + tags: + - { name: event_subscriber } + + devel.twig.debug_extension: + class: Drupal\devel\Twig\Extension\Debug + arguments: ['@devel.dumper'] + tags: + - { name: twig.extension } diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/README.txt b/sites/all/modules/contrib/dev/devel/devel_generate/README.txt new file mode 100644 index 000000000..c5279d014 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/README.txt @@ -0,0 +1,42 @@ +This module creates the "DevelGenerate" plugin type. + +All you need to do to provide a new instance for "DevelGenerate" plugin type +is to create your class extending "DevelGenerateBase" and following the next steps. + +1 - Declaring your plugin with annotations: + +/** + * Provides a ExampleDevelGenerate plugin. + * + * @DevelGenerate( + * id = "example", + * label = @Translation("example"), + * description = @Translation("Generate a given number of example elements. Optionally delete current example elements."), + * url = "example", + * permission = "administer example", + * settings = { + * "num" = 50, + * "kill" = FALSE, + * "another_property" = "default_value" + * } + * ) + */ + +2 - Implement "settingsForm" method to create a form using the properties from annotations. + +3 - Implement "handleDrushParams" method. It should return an array of values. + +4 - Implement "generateElements" method. You can write here your business logic +using the array of values. + +Notes: + +You can alter existing properties for every plugin implementing hook_devel_generate_info_alter. + +DevelGenerateBaseInterface details base wrapping methods that most DevelGenerate implementations +will want to directly inherit from Drupal\devel_generate\DevelGenerateBase. + +DevelGenerateFieldBaseInterface details base wrapping methods that most class implementations +for supporting new field types will want to directly inherit from Drupal\devel_generate\DevelGenerateFieldBase. +So to give support for a new field type should be enough to create a class called +"DevelGenerateFieldNewfieldtype" extending DevelGenerateFieldBase and to implement "generateValues" method. diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.batch.inc b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.batch.inc new file mode 100644 index 000000000..2bfa690b8 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.batch.inc @@ -0,0 +1,25 @@ +$method($vars, $context); +} + +/** + * Common finish batch function available for + * every DevelGeneratePlugin. + */ +function devel_generate_batch_finished($success, $results, $operations) { + + if ($success) { + $message = t('Finished @num elements created successfully.', array('@num' => $results['num'])); + } + else { + $message = t('Finished with an error.'); + } + drupal_set_message($message); +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.info.yml b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.info.yml new file mode 100644 index 000000000..068fbe98b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.info.yml @@ -0,0 +1,13 @@ +type: module +name: 'Devel generate' +description: 'Generate dummy users, nodes, menus, taxonomy terms...' +package: Development +# core: 8.x +tags: + - developer + +# Information added by Drupal.org packaging script on 2016-09-03 +version: '8.x-1.0-alpha1+23-dev' +core: '8.x' +project: 'devel' +datestamp: 1472897643 diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.module b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.module new file mode 100644 index 000000000..530a4fbc8 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.module @@ -0,0 +1,145 @@ +getDefinitions(); + foreach ($devel_generate_plugins as $id => $plugin) { + $label = $plugin['label']; + $links["devel_generate.$id"] = array( + 'title' => "Generate $label", + 'parent' => 'system.admin_config_development', + 'description' => $plugin['description'], + 'route_name' => "devel_generate.$id", + ); + } +} + +/** + * Implements hook_entity_insert(). + * + * Inserts nodes properly based on generation options. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The base node created on submit. Inspects $node->devel_generate. + */ +function devel_generate_entity_insert(EntityInterface $entity) { + if ($entity->getEntityTypeId() != 'node' || !isset($entity->devel_generate)) { + return; + } + /** @var \Drupal\node\NodeInterface $entity */ + $results = $entity->devel_generate; + + if (!empty($results['max_comments'])) { + foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) { + if ($field_definition->getType() == 'comment' && $entity->get($field_name)->status == CommentItemInterface::OPEN) { + // Add comments for each comment field on entity. + devel_generate_add_comments($entity, $field_definition, $results['users'], $results['max_comments'], $results['title_length']); + } + } + } + + // Add an url alias. Cannot happen before save because we don't know the nid. + if (!empty($results['add_alias'])) { + $path = array( + 'source' => '/node/' . $entity->id(), + 'alias' => '/node-' . $entity->id() . '-' . $entity->bundle(), + ); + \Drupal::service('path.alias_storage')->save($path['source'], $path['alias']); + } + + // Add node statistics. + if (!empty($results['add_statistics']) && \Drupal::moduleHandler()->moduleExists('statistics')) { + devel_generate_add_statistics($entity); + } +} + +/** + * Create comments and add them to a node. + * + * @param \Drupal\node\NodeInterface $node + * Node to add comments to. + * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition + * The field storage definition. + * @param array $users + * Array of users to assign comment authors. + * @param int $max_comments + * Max number of comments to generate per node. + * @param int $title_length + * Max length of the title of the comments. + */ +function devel_generate_add_comments(NodeInterface $node, FieldDefinitionInterface $field_definition, $users, $max_comments, $title_length = 8) { + $parents = array(); + $field_name = $field_definition->getName(); + $num_comments = mt_rand(1, $max_comments); + for ($i = 1; $i <= $num_comments; $i++) { + switch ($i % 3) { + case 0: + // No parent. + case 1: + // Top level parent. + $parents = \Drupal::entityQuery('comment') + ->condition('pid', 0) + ->condition('entity_id', $node->id()) + ->condition('entity_type', 'node') + ->condition('field_name', $field_name) + ->range(0, 1) + ->execute(); + break; + case 2: + // Non top level parent. + $parents = \Drupal::entityQuery('comment') + ->condition('pid', 0, '>') + ->condition('entity_id', $node->id()) + ->condition('entity_type', 'node') + ->condition('field_name', $field_name) + ->range(0, 1) + ->execute(); + break; + } + $random = new Random(); + $stub = array( + 'entity_type' => $node->getEntityTypeId(), + 'entity_id' => $node->id(), + 'field_name' => $field_name, + 'name' => 'devel generate', + 'mail' => 'devel_generate@example.com', + 'timestamp' => mt_rand($node->getCreatedTime(), REQUEST_TIME), + 'subject' => substr($random->sentences(mt_rand(2, $title_length), TRUE), 0, 63), + 'uid' => $users[array_rand($users)], + 'langcode' => $node->language()->getId(), + ); + if ($parents) { + $stub['pid'] = current($parents); + } + $comment = entity_create('comment', $stub); + + //Populate all core fields on behalf of field.module + DevelGenerateBase::populateFields($comment); + $comment->save(); + } +} + +/** + * Generate statistics information for a node. + * + * @param \Drupal\node\NodeInterface $node + * A node object. + */ +function devel_generate_add_statistics(NodeInterface $node) { + $statistic = array( + 'nid' => $node->id(), + 'totalcount' => mt_rand(0, 500), + 'timestamp' => REQUEST_TIME - mt_rand(0, $node->getCreatedTime()), + ); + $statistic['daycount'] = mt_rand(0, $statistic['totalcount']); + db_insert('node_counter')->fields($statistic)->execute(); +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.permissions.yml b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.permissions.yml new file mode 100644 index 000000000..10f9479e5 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.permissions.yml @@ -0,0 +1,5 @@ +administer devel_generate: + title: 'Administer devel_generate' + +permission_callbacks: + - \Drupal\devel_generate\DevelGeneratePermissions::permissions diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.routing.yml b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.routing.yml new file mode 100644 index 000000000..eeea66824 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.routing.yml @@ -0,0 +1,2 @@ +route_callbacks: + - '\Drupal\devel_generate\Routing\DevelGenerateRoutes::routes' \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.services.yml b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.services.yml new file mode 100644 index 000000000..16025e1da --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.services.yml @@ -0,0 +1,4 @@ +services: + plugin.manager.develgenerate: + class: Drupal\devel_generate\DevelGeneratePluginManager + parent: default_plugin_manager diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/drush/DevelGenerateUnishTest.php b/sites/all/modules/contrib/dev/devel/devel_generate/drush/DevelGenerateUnishTest.php new file mode 100644 index 000000000..128c2773c --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/drush/DevelGenerateUnishTest.php @@ -0,0 +1,151 @@ +markTestSkipped('Devel Generate Tests only available on D8+.'); + } + + if (!$this->getSites()) { + $this->setUpDrupal(1, TRUE, UNISH_DRUPAL_MAJOR_VERSION, 'standard'); + + // Symlink the devel module into the sandbox. + $devel_directory = dirname(dirname(__DIR__)); + symlink($devel_directory, $this->webroot() . '/modules/devel'); + + // Enable the devel_generate modules. + $this->drush('pm-enable', ['devel_generate'], $this->getOptions()); + } + + } + + /** + * Tests devel generate terms. + */ + public function testDevelGenerateTerms() { + $this->drush('pm-enable', ['taxonomy'], $this->getOptions()); + + $this->drush('generate-terms', [], $this->getOptions(), NULL, NULL, static::EXIT_ERROR); + $this->assertContains('Please provide a vocabulary machine name.', $this->getErrorOutput()); + + $this->drush('generate-terms', ['unknown'], $this->getOptions(), NULL, NULL, static::EXIT_ERROR); + $this->assertContains('Invalid vocabulary name: unknown', $this->getErrorOutput()); + + $this->drush('generate-terms', ['tags', 'NaN'], $this->getOptions(), NULL, NULL, static::EXIT_ERROR); + $this->assertContains('Invalid number of terms: NaN', $this->getErrorOutput()); + + $eval_term_count = "return \\Drupal::entityQuery('taxonomy_term')->count()->execute();"; + $eval_options = $this->getOptions() + ['format' => 'string']; + + $this->drush('generate-terms', ['tags'], $this->getOptions()); + $this->assertContains('Created the following new terms:', $this->getErrorOutput()); + $this->drush('php-eval', [$eval_term_count], $eval_options); + $this->assertEquals(10, $this->getOutput()); + + $this->drush('generate-terms', ['tags', '1'], $this->getOptions()); + $this->assertContains('Created the following new terms:', $this->getErrorOutput()); + $this->drush('php-eval', [$eval_term_count], $eval_options); + $this->assertEquals(11, $this->getOutput()); + + $this->drush('generate-terms', ['tags', '1'], $this->getOptions(TRUE)); + $this->assertContains('Deleted existing terms.', $this->getErrorOutput()); + $this->assertContains('Created the following new terms:', $this->getErrorOutput()); + $this->drush('php-eval', [$eval_term_count], $eval_options); + $this->assertEquals(1, $this->getOutput()); + + $this->drush('gent', ['tags', '1'], $this->getOptions()); + $this->assertContains('Created the following new terms:', $this->getErrorOutput()); + } + + /** + * Tests devel generate contents. + */ + public function testDevelGenerateContents() { + $this->drush('pm-enable', ['node'], $this->getOptions()); + + $eval_content_count = "return \\Drupal::entityQuery('node')->count()->execute();"; + $eval_options = $this->getOptions() + ['format' => 'string']; + + // Try to generate 10 content of type "page" or "article" + $this->drush('generate-content', [10], $this->getOptions(), NULL, NULL, static::EXIT_SUCCESS); + $this->assertContains('Finished creating 10 nodes', $this->getErrorOutput()); + $this->drush('php-eval', [$eval_content_count], $eval_options); + $this->assertEquals(10, $this->getOutput()); + + // Try to generate 1 content of type "page" or "article" + $this->drush('generate-content', [1], $this->getOptions(), NULL, NULL, static::EXIT_SUCCESS); + $this->assertContains('1 node created.', $this->getErrorOutput()); + $this->drush('php-eval', [$eval_content_count], $eval_options); + $this->assertEquals(11, $this->getOutput()); + + // Try to generate 5 content of type "page" or "article", removing all + // previous contents. + $this->drush('generate-content', [5], $this->getOptions(TRUE), NULL, NULL, static::EXIT_SUCCESS); + $this->assertContains('Finished creating 5 nodes', $this->getErrorOutput()); + $this->drush('php-eval', [$eval_content_count], $eval_options); + $this->assertEquals(5, $this->getOutput()); + + // Try to generate other 5 content with "crappy" type. Output should + // remains 5. + $generate_content_wrong_ct = $this->getOptions(TRUE) + ['types' => 'crappy']; + $this->drush('generate-content', [5], $generate_content_wrong_ct, NULL, NULL, static::EXIT_ERROR); + $this->assertContains('One or more content types have been entered that don', $this->getErrorOutput()); + $this->drush('php-eval', [$eval_content_count], $eval_options); + $this->assertEquals(5, $this->getOutput()); + + // Try to generate other 5 content with empty types. Output should + // remains 5. + $generate_content_no_types = $this->getOptions(TRUE) + ['types' => '']; + $this->drush('generate-content', [5], $generate_content_no_types, NULL, NULL, static::EXIT_ERROR); + $this->assertContains('No content types available', $this->getErrorOutput()); + $this->drush('php-eval', [$eval_content_count], $eval_options); + $this->assertEquals(5, $this->getOutput()); + + // Try to generate other 5 content without any types. Output should + // remains 5. + $generate_content_no_types = $this->getOptions(TRUE) + ['types' => NULL]; + $this->drush('generate-content', [5], $generate_content_no_types, NULL, NULL, static::EXIT_ERROR); + $this->assertContains('Wrong syntax or no content type selected. The correct syntax uses', $this->getErrorOutput()); + $this->drush('php-eval', [$eval_content_count], $eval_options); + $this->assertEquals(5, $this->getOutput()); + } + + /** + * Default drush options. + * + * @param bool $kill + * Whether add kill option. + * + * @return array + * An array containing the default options for drush commands. + */ + protected function getOptions($kill = FALSE) { + $options = [ + 'yes' => NULL, + 'root' => $this->webroot(), + 'uri' => key($this->getSites()), + ]; + + if($kill) { + $options['kill'] = NULL; + } + + return $options; + } + + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/drush/devel_generate.drush.inc b/sites/all/modules/contrib/dev/devel/devel_generate/drush/devel_generate.drush.inc new file mode 100644 index 000000000..33ae8a267 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/drush/devel_generate.drush.inc @@ -0,0 +1,196 @@ + 'drush_devel_generate', + 'callback arguments' => array( + 'plugin_id' => 'user', + ), + 'description' => 'Create users.', + 'arguments' => array( + 'num' => 'Number of users to generate.', + ), + 'options' => array( + 'kill' => 'Delete all users before generating new ones.', + 'roles' => 'A comma delimited list of role IDs which should be granted to the new users. No need to specify authenticated user role.', + 'pass' => 'Specify a password to be set for all generated users.', + ), + 'aliases' => array('genu'), + ); + $items['generate-terms'] = array( + 'callback' => 'drush_devel_generate', + 'callback arguments' => array( + 'plugin_id' => 'term', + ), + 'description' => 'Create terms in specified vocabulary.', + 'arguments' => array( + 'machine_name' => 'Vocabulary machine name into which new terms will be inserted.', + 'num' => 'Number of terms to insert. Defaults to 10.', + ), + 'options' => array( + 'kill' => 'Delete all terms in specified vocabulary before generating.', + 'feedback' => 'An integer representing interval for insertion rate logging. Defaults to 1000', + 'pipe' => 'Returns the list of generated terms, one per line.', + ), + 'aliases' => array('gent'), + + ); + $items['generate-vocabs'] = array( + 'callback' => 'drush_devel_generate', + 'callback arguments' => array( + 'plugin_id' => 'vocabulary', + ), + 'description' => 'Create vocabularies.', + 'arguments' => array( + 'num' => 'Number of vocabularies to create. Defaults to 1.', + ), + 'options' => array( + 'kill' => 'Delete all vocabularies before generating.', + 'pipe' => 'Returns the list of generated vocabularies, one per line.', + ), + 'aliases' => array('genv'), + ); + $items['generate-content'] = array( + 'callback' => 'drush_devel_generate', + 'callback arguments' => array( + 'plugin_id' => 'content', + ), + 'description' => 'Create content.', + 'drupal dependencies' => array('devel_generate'), + 'arguments' => array( + 'num' => 'Number of nodes to generate.', + 'max_comments' => 'Maximum number of comments to generate.', + ), + 'options' => array( + 'kill' => 'Delete all content before generating new content.', + 'types' => 'A comma delimited list of content types to create. Defaults to page,article.', + 'feedback' => 'An integer representing interval for insertion rate logging. Defaults to 1000', + 'skip-fields' => 'A comma delimited list of fields to omit when generating random values', + 'languages' => 'A comma-separated list of language codes', + ), + 'aliases' => array('genc'), + ); + $items['generate-menus'] = array( + 'callback' => 'drush_devel_generate', + 'callback arguments' => array( + 'plugin_id' => 'menu', + ), + 'description' => 'Create menus and menu items.', + 'drupal dependencies' => array('devel_generate'), // Remove these once devel.module is moved down a directory. http://drupal.org/node/925246 + 'arguments' => array( + 'number_menus' => 'Number of menus to generate. Defaults to 2.', + 'number_links' => 'Number of links to generate. Defaults to 50.', + 'max_depth' => 'Max link depth. Defaults to 3', + 'max_width' => 'Max width of first level of links. Defaults to 8.', + ), + 'options' => array( + 'kill' => 'Delete all previously generated menus and links before generating new menus and links.', + 'pipe' => 'Returns the list of generated menus, one per line.', + ), + 'aliases' => array('genm'), + ); + return $items; +} + +/** + * Implements drush_hook_COMMAND_validate(). + */ +function drush_devel_generate_generate_users_validate() { + + //Array of "Callback arguments" and "command line args". + $params = func_get_args(); + _drush_plugin_validate($params); +} + +/** + * Implements drush_hook_COMMAND_validate(). + */ +function drush_devel_generate_generate_terms_validate() { + + //Array of "Callback arguments" and "command line args". + $params = func_get_args(); + _drush_plugin_validate($params); +} + +/** + * Implements drush_hook_COMMAND_validate(). + */ +function drush_devel_generate_generate_vocabs_validate() { + + //Array of "Callback arguments" and "command line args". + $params = func_get_args(); + _drush_plugin_validate($params); +} + +/** + * Implements drush_hook_COMMAND_validate(). + */ +function drush_devel_generate_generate_content_validate() { + + //Array of "Callback arguments" and "command line args". + $params = func_get_args(); + _drush_plugin_validate($params); +} + +/** + * Implements drush_hook_COMMAND_validate(). + */ +function drush_devel_generate_generate_menus_validate() { + + //Array of "Callback arguments" and "command line args". + $params = func_get_args(); + _drush_plugin_validate($params); +} + +/** + * Helper function which returns an array with a plugin instance + * for a given id and the validated values ready to be used by + * the generate() function of the plugin. + */ +function _drush_plugin_validate($params) { + + $instance_and_values = &drupal_static('drush_devel_generate_generate_validate'); + //Getting plugin_id and leaving the command line args + $plugin_id = array_shift($params); + + if (!isset($instance_and_values[$plugin_id])) { + + /** @var DevelGeneratePluginManager $manager */ + $manager = \Drupal::service('plugin.manager.develgenerate'); + + /** @var DevelGenerateBaseInterface $instance */ + $instance = $manager->createInstance($plugin_id, array()); + + //Plugin instance suit params in order to fit for generateElements + $values = $instance->validateDrushParams($params); + $instance_and_values[$plugin_id]['instance'] = $instance; + $instance_and_values[$plugin_id]['values'] = $values; + } + + return $instance_and_values[$plugin_id]; +} + +/** + * Command callback. Generate a number of elements. + */ +function drush_devel_generate() { + $params = func_get_args(); + $plugin_id = array_shift($params); + $instance_and_values = drupal_static('drush_devel_generate_generate_validate'); + + /** @var DevelGenerateBaseInterface $instance */ + $instance = $instance_and_values[$plugin_id]['instance']; + $values = $instance_and_values[$plugin_id]['values']; + $instance->generate($values); + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/Annotation/DevelGenerate.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/Annotation/DevelGenerate.php new file mode 100644 index 000000000..9a0ce2525 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/Annotation/DevelGenerate.php @@ -0,0 +1,86 @@ +settings)) { + $this->settings = $this->getDefaultSettings(); + } + return isset($this->settings[$key]) ? $this->settings[$key] : NULL; + } + + /** + * {@inheritdoc} + */ + public function getDefaultSettings() { + $definition = $this->getPluginDefinition(); + return $definition['settings']; + } + + /** + * {@inheritdoc} + */ + public function getSettings() { + return $this->settings; + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + return array(); + } + + /** + * {@inheritdoc} + */ + function settingsFormValidate(array $form, FormStateInterface $form_state) { + // Validation is optional. + } + + /** + * {@inheritdoc} + */ + public function generate(array $values) { + $this->generateElements($values); + $this->setMessage('Generate process complete.'); + } + + /** + * Business logic relating with each DevelGenerate plugin + * + * @param array $values + * The input values from the settings form. + */ + protected function generateElements(array $values) { + + } + + /** + * Populate the fields on a given entity with sample values. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity to be enriched with sample field values. + */ + public static function populateFields(EntityInterface $entity) { + /** @var \Drupal\field\FieldConfigInterface[] $instances */ + $instances = entity_load_multiple_by_properties('field_config', array('entity_type' => $entity->getEntityType()->id(), 'bundle' => $entity->bundle())); + + if ($skips = function_exists('drush_get_option') ? drush_get_option('skip-fields', '') : @$_REQUEST['skip-fields']) { + foreach (explode(',', $skips) as $skip) { + unset($instances[$skip]); + } + } + + foreach ($instances as $instance) { + $field_storage = $instance->getFieldStorageDefinition(); + $max = $cardinality = $field_storage->getCardinality(); + if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) { + // Just an arbitrary number for 'unlimited' + $max = rand(1, 3); + } + $field_name = $field_storage->getName(); + $entity->$field_name->generateSampleItems($max); + } + } + + /** + * {@inheritdoc} + */ + public function handleDrushParams($args) { + + } + + /** + * Set a message for either drush or the web interface. + * + * @param string $msg + * The message to display. + * @param string $type + * (optional) The message type, as defined by drupal_set_message(). Defaults + * to 'status' + */ + protected function setMessage($msg, $type = 'status') { + $function = 'drupal_set_message'; + if (function_exists('drush_log')) { + $function = 'drush_log'; + $msg = strip_tags($msg); + } + $function($msg, $type); + } + + /** + * Check if a given param is a number. + * + * @param mixed $number + * The parameter to check. + * + * @return bool + * TRUE if the parameter is a number, FALSE otherwise. + */ + public static function isNumber($number) { + if ($number == NULL) return FALSE; + if (!is_numeric($number)) return FALSE; + return TRUE; + } + + /** + * Returns the random data generator. + * + * @return \Drupal\Component\Utility\Random + * The random data generator. + */ + protected function getRandom() { + if (!$this->random) { + $this->random = new Random(); + } + return $this->random; + } +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGenerateBaseInterface.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGenerateBaseInterface.php new file mode 100644 index 000000000..c2b814a2b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGenerateBaseInterface.php @@ -0,0 +1,77 @@ +develGeneratePluginManager = $develGeneratePluginManager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('plugin.manager.develgenerate')); + } + + /* + * A permissions callback. + * + * @see devel_generate.permissions.yml. + * + * @return array + */ + function permissions() { + $devel_generate_plugins = $this->develGeneratePluginManager->getDefinitions(); + foreach ($devel_generate_plugins as $plugin) { + + $permission = $plugin['permission']; + $permissions[$permission] = array( + 'title' => t($permission), + ); + } + +// $permissions = array( +// 'administer devel_generate' => array( +// 'title' => t('Administer devel generate'), +// ), +// ); + return $permissions; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGeneratePluginManager.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGeneratePluginManager.php new file mode 100644 index 000000000..626e6dce9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGeneratePluginManager.php @@ -0,0 +1,31 @@ +alterInfo('devel_generate_info'); + $this->setCacheBackend($cache_backend, 'devel_generate_plugins'); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/Form/DevelGenerateForm.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/Form/DevelGenerateForm.php new file mode 100644 index 000000000..63c708b49 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/Form/DevelGenerateForm.php @@ -0,0 +1,114 @@ +develGenerateManager = $devel_generate_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.develgenerate') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'devel_generate_form_' . $this->getPluginIdFromRequest(); + } + + /** + * Returns the value of the param _plugin_id for the current request. + * + * @see \Drupal\devel_generate\Routing\DevelGenerateRouteSubscriber + */ + protected function getPluginIdFromRequest() { + $request = $this->getRequest(); + return $request->get('_plugin_id'); + } + + /** + * Returns a DevelGenerate plugin instance for a given plugin id. + * + * @param string $plugin_id + * The plugin_id for the plugin instance. + * + * @return \Drupal\devel_generate\DevelGenerateBaseInterface + * A DevelGenerate plugin instance. + */ + public function getPluginInstance($plugin_id) { + $instance = $this->develGenerateManager->createInstance($plugin_id, array()); + return $instance; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $plugin_id = $this->getPluginIdFromRequest(); + $instance = $this->getPluginInstance($plugin_id); + $form = $instance->settingsForm($form, $form_state); + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Generate'), + '#button_type' => 'primary', + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + $plugin_id = $this->getPluginIdFromRequest(); + $instance = $this->getPluginInstance($plugin_id); + $instance->settingsFormValidate($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + try { + $plugin_id = $this->getPluginIdFromRequest(); + $instance = $this->getPluginInstance($plugin_id); + $instance->generate($form_state->getValues()); + } + catch (DevelGenerateException $e) { + $this->logger('DevelGenerate', $this->t('Failed to generate elements due to "%error".', array('%error' => $e->getMessage()))); + drupal_set_message($this->t('Failed to generate elements due to "%error".', array('%error' => $e->getMessage()))); + } + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/ContentDevelGenerate.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/ContentDevelGenerate.php new file mode 100644 index 000000000..11d55bff3 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/ContentDevelGenerate.php @@ -0,0 +1,506 @@ +moduleHandler = $module_handler; + $this->nodeStorage = $node_storage; + $this->nodeTypeStorage = $node_type_storage; + $this->commentManager = $comment_manager; + $this->languageManager = $language_manager; + $this->urlGenerator = $url_generator; + $this->dateFormatter = $date_formatter; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + $entity_manager = $container->get('entity.manager'); + return new static( + $configuration, $plugin_id, $plugin_definition, + $entity_manager->getStorage('node'), + $entity_manager->getStorage('node_type'), + $container->get('module_handler'), + $container->has('comment.manager') ? $container->get('comment.manager') : NULL, + $container->get('language_manager'), + $container->get('url_generator'), + $container->get('date.formatter') + ); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $types = $this->nodeTypeStorage->loadMultiple(); + + if (empty($types)) { + $create_url = $this->urlGenerator->generateFromRoute('node.type_add'); + $this->setMessage($this->t('You do not have any content types that can be generated. Go create a new content type', array(':create-type' => $create_url)), 'error', FALSE); + return; + } + + $options = array(); + + foreach ($types as $type) { + $options[$type->id()] = array( + 'type' => array('#markup' => $type->label()), + ); + if ($this->commentManager) { + $comment_fields = $this->commentManager->getFields('node'); + $map = array($this->t('Hidden'), $this->t('Closed'), $this->t('Open')); + + $fields = array(); + foreach ($comment_fields as $field_name => $info) { + // Find all comment fields for the bundle. + if (in_array($type->id(), $info['bundles'])) { + $instance = FieldConfig::loadByName('node', $type->id(), $field_name); + $default_value = $instance->getDefaultValueLiteral(); + $default_mode = reset($default_value); + $fields[] = new FormattableMarkup('@field: @state', array( + '@field' => $instance->label(), + '@state' => $map[$default_mode['status']], + )); + } + } + // @todo Refactor display of comment fields. + if (!empty($fields)) { + $options[$type->id()]['comments'] = array( + 'data' => array( + '#theme' => 'item_list', + '#items' => $fields, + ), + ); + } + else { + $options[$type->id()]['comments'] = $this->t('No comment fields'); + } + } + } + + $header = array( + 'type' => $this->t('Content type'), + ); + if ($this->commentManager) { + $header['comments'] = array( + 'data' => $this->t('Comments'), + 'class' => array(RESPONSIVE_PRIORITY_MEDIUM), + ); + } + + $form['node_types'] = array( + '#type' => 'tableselect', + '#header' => $header, + '#options' => $options, + ); + + $form['kill'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Delete all content in these content types before generating new content.'), + '#default_value' => $this->getSetting('kill'), + ); + $form['num'] = array( + '#type' => 'number', + '#title' => $this->t('How many nodes would you like to generate?'), + '#default_value' => $this->getSetting('num'), + '#required' => TRUE, + '#min' => 0, + ); + + $options = array(1 => $this->t('Now')); + foreach (array(3600, 86400, 604800, 2592000, 31536000) as $interval) { + $options[$interval] = $this->dateFormatter->formatInterval($interval, 1) . ' ' . $this->t('ago'); + } + $form['time_range'] = array( + '#type' => 'select', + '#title' => $this->t('How far back in time should the nodes be dated?'), + '#description' => $this->t('Node creation dates will be distributed randomly from the current time, back to the selected time.'), + '#options' => $options, + '#default_value' => 604800, + ); + + $form['max_comments'] = array( + '#type' => $this->moduleHandler->moduleExists('comment') ? 'number' : 'value', + '#title' => $this->t('Maximum number of comments per node.'), + '#description' => $this->t('You must also enable comments for the content types you are generating. Note that some nodes will randomly receive zero comments. Some will receive the max.'), + '#default_value' => $this->getSetting('max_comments'), + '#min' => 0, + '#access' => $this->moduleHandler->moduleExists('comment'), + ); + $form['title_length'] = array( + '#type' => 'number', + '#title' => $this->t('Maximum number of words in titles'), + '#default_value' => $this->getSetting('title_length'), + '#required' => TRUE, + '#min' => 1, + '#max' => 255, + ); + $form['add_alias'] = array( + '#type' => 'checkbox', + '#disabled' => !$this->moduleHandler->moduleExists('path'), + '#description' => $this->t('Requires path.module'), + '#title' => $this->t('Add an url alias for each node.'), + '#default_value' => FALSE, + ); + $form['add_statistics'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Add statistics for each node (node_counter table).'), + '#default_value' => TRUE, + '#access' => $this->moduleHandler->moduleExists('statistics'), + ); + + $options = array(); + // We always need a language. + $languages = $this->languageManager->getLanguages(LanguageInterface::STATE_ALL); + foreach ($languages as $langcode => $language) { + $options[$langcode] = $language->getName(); + } + + $form['add_language'] = array( + '#type' => 'select', + '#title' => $this->t('Set language on nodes'), + '#multiple' => TRUE, + '#description' => $this->t('Requires locale.module'), + '#options' => $options, + '#default_value' => array( + $this->languageManager->getDefaultLanguage()->getId(), + ), + ); + + $form['#redirect'] = FALSE; + + return $form; + } + + /** + * {@inheritdoc} + */ + function settingsFormValidate(array $form, FormStateInterface $form_state) { + if (!array_filter($form_state->getValue('node_types'))) { + $form_state->setErrorByName('node_types', $this->t('Please select at least one content type')); + } + } + + /** + * {@inheritdoc} + */ + protected function generateElements(array $values) { + if ($values['num'] <= 50 && $values['max_comments'] <= 10) { + $this->generateContent($values); + } + else { + $this->generateBatchContent($values); + } + } + + /** + * Method responsible for creating content when + * the number of elements is less than 50. + */ + private function generateContent($values) { + $values['node_types'] = array_filter($values['node_types']); + if (!empty($values['kill']) && $values['node_types']) { + $this->contentKill($values); + } + + if (!empty($values['node_types'])) { + // Generate nodes. + $this->develGenerateContentPreNode($values); + $start = time(); + for ($i = 1; $i <= $values['num']; $i++) { + $this->develGenerateContentAddNode($values); + if (function_exists('drush_log') && $i % drush_get_option('feedback', 1000) == 0) { + $now = time(); + drush_log(dt('Completed @feedback nodes (@rate nodes/min)', array('@feedback' => drush_get_option('feedback', 1000), '@rate' => (drush_get_option('feedback', 1000) * 60) / ($now - $start))), 'ok'); + $start = $now; + } + } + } + $this->setMessage($this->formatPlural($values['num'], '1 node created.', 'Finished creating @count nodes')); + } + + /** + * Method responsible for creating content when + * the number of elements is greater than 50. + */ + private function generateBatchContent($values) { + // Setup the batch operations and save the variables. + $operations[] = array('devel_generate_operation', array($this, 'batchContentPreNode', $values)); + + // Add the kill operation. + if ($values['kill']) { + $operations[] = array('devel_generate_operation', array($this, 'batchContentKill', $values)); + } + + // Add the operations to create the nodes. + for ($num = 0; $num < $values['num']; $num ++) { + $operations[] = array('devel_generate_operation', array($this, 'batchContentAddNode', $values)); + } + + // Start the batch. + $batch = array( + 'title' => $this->t('Generating Content'), + 'operations' => $operations, + 'finished' => 'devel_generate_batch_finished', + 'file' => drupal_get_path('module', 'devel_generate') . '/devel_generate.batch.inc', + ); + batch_set($batch); + } + + public function batchContentPreNode($vars, &$context) { + $context['results'] = $vars; + $context['results']['num'] = 0; + $this->develGenerateContentPreNode($context['results']); + } + + public function batchContentAddNode($vars, &$context) { + $this->develGenerateContentAddNode($context['results']); + $context['results']['num']++; + } + + public function batchContentKill($vars, &$context) { + $this->contentKill($context['results']); + } + + /** + * {@inheritdoc} + */ + public function validateDrushParams($args) { + $add_language = drush_get_option('languages'); + if (!empty($add_language)) { + $add_language = explode(',', str_replace(' ', '', $add_language)); + // Intersect with the enabled languages to make sure the language args + // passed are actually enabled. + $values['values']['add_language'] = array_intersect($add_language, array_keys($this->languageManager->getLanguages(LanguageInterface::STATE_ALL))); + } + + $values['kill'] = drush_get_option('kill'); + $values['title_length'] = 6; + $values['num'] = array_shift($args); + $values['max_comments'] = array_shift($args); + $all_types = array_keys(node_type_get_names()); + $default_types = array_intersect(array('page', 'article'), $all_types); + $selected_types = _convert_csv_to_array(drush_get_option('types', $default_types)); + + // Validates the input format for content types option. + if (drush_get_option('types', $default_types) === TRUE) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Wrong syntax or no content type selected. The correct syntax uses "=", eg.: --types=page,article')); + } + + if (empty($selected_types)) { + return drush_set_error('DEVEL_GENERATE_NO_CONTENT_TYPES', dt('No content types available')); + } + + $values['node_types'] = array_combine($selected_types, $selected_types); + $node_types = array_filter($values['node_types']); + + if (!empty($values['kill']) && empty($node_types)) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Please provide content type (--types) in which you want to delete the content.')); + } + + // Checks for any missing content types before generating nodes. + if (array_diff($node_types, $all_types)) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('One or more content types have been entered that don\'t exist on this site')); + } + + return $values; + } + + /** + * Deletes all nodes of given node types. + * + * @param array $values + * The input values from the settings form. + */ + protected function contentKill($values) { + $nids = $this->nodeStorage->getQuery() + ->condition('type', $values['node_types'], 'IN') + ->execute(); + + if (!empty($nids)) { + $nodes = $this->nodeStorage->loadMultiple($nids); + $this->nodeStorage->delete($nodes); + $this->setMessage($this->t('Deleted %count nodes.', array('%count' => count($nids)))); + } + } + + /** + * Return the same array passed as parameter + * but with an array of uids for the key 'users'. + */ + protected function develGenerateContentPreNode(&$results) { + // Get user id. + $users = $this->getUsers(); + $users = array_merge($users, array('0')); + $results['users'] = $users; + } + + /** + * Create one node. Used by both batch and non-batch code branches. + */ + protected function develGenerateContentAddNode(&$results) { + if (!isset($results['time_range'])) { + $results['time_range'] = 0; + } + $users = $results['users']; + + $node_type = array_rand(array_filter($results['node_types'])); + $uid = $users[array_rand($users)]; + + $node = $this->nodeStorage->create(array( + 'nid' => NULL, + 'type' => $node_type, + 'title' => $this->getRandom()->sentences(mt_rand(1, $results['title_length']), TRUE), + 'uid' => $uid, + 'revision' => mt_rand(0, 1), + 'status' => TRUE, + 'promote' => mt_rand(0, 1), + 'created' => REQUEST_TIME - mt_rand(0, $results['time_range']), + 'langcode' => $this->getLangcode($results), + )); + + // A flag to let hook_node_insert() implementations know that this is a + // generated node. + $node->devel_generate = $results; + + // Populate all fields with sample values. + $this->populateFields($node); + + // See devel_generate_node_insert() for actions that happen before and after + // this save. + $node->save(); + } + + /** + * Determine language based on $results. + */ + protected function getLangcode($results) { + if (isset($results['add_language'])) { + $langcodes = $results['add_language']; + $langcode = $langcodes[array_rand($langcodes)]; + } + else { + $langcode = $this->languageManager->getDefaultLanguage()->getId(); + } + return $langcode; + } + + /** + * Retrive 50 uids from the database. + */ + protected function getUsers() { + $users = array(); + $result = db_query_range("SELECT uid FROM {users}", 0, 50); + foreach ($result as $record) { + $users[] = $record->uid; + } + return $users; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/MenuDevelGenerate.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/MenuDevelGenerate.php new file mode 100644 index 000000000..7c961a6c3 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/MenuDevelGenerate.php @@ -0,0 +1,407 @@ +menuLinkTree = $menu_tree; + $this->menuStorage = $menu_storage; + $this->menuLinkContentStorage = $menu_link_storage; + $this->moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + $entity_manager = $container->get('entity.manager'); + return new static( + $configuration, $plugin_id, $plugin_definition, + $container->get('menu.link_tree'), + $entity_manager->getStorage('menu'), + $entity_manager->getStorage('menu_link_content'), + $container->get('module_handler') + ); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $menu_enabled = $this->moduleHandler->moduleExists('menu_ui'); + if ($menu_enabled) { + $menus = array('__new-menu__' => $this->t('Create new menu(s)')) + menu_ui_get_menus(); + } + else { + $menus = menu_list_system_menus(); + } + $form['existing_menus'] = array( + '#type' => 'checkboxes', + '#title' => $this->t('Generate links for these menus'), + '#options' => $menus, + '#default_value' => array('__new-menu__'), + '#required' => TRUE, + ); + if ($menu_enabled) { + $form['num_menus'] = array( + '#type' => 'number', + '#title' => $this->t('Number of new menus to create'), + '#default_value' => $this->getSetting('num_menus'), + '#min' => 0, + '#states' => array( + 'visible' => array( + ':input[name="existing_menus[__new-menu__]"]' => array('checked' => TRUE), + ), + ), + ); + } + $form['num_links'] = array( + '#type' => 'number', + '#title' => $this->t('Number of links to generate'), + '#default_value' => $this->getSetting('num_links'), + '#required' => TRUE, + '#min' => 0, + ); + $form['title_length'] = array( + '#type' => 'number', + '#title' => $this->t('Maximum number of characters in menu and menu link names'), + '#description' => $this->t('The minimum length is 2.'), + '#default_value' => $this->getSetting('title_length'), + '#required' => TRUE, + '#min' => 2, + '#max' => 128, + ); + $form['link_types'] = array( + '#type' => 'checkboxes', + '#title' => $this->t('Types of links to generate'), + '#options' => array( + 'node' => $this->t('Nodes'), + 'front' => $this->t('Front page'), + 'external' => $this->t('External'), + ), + '#default_value' => array('node', 'front', 'external'), + '#required' => TRUE, + ); + $form['max_depth'] = array( + '#type' => 'select', + '#title' => $this->t('Maximum link depth'), + '#options' => range(0, $this->menuLinkTree->maxDepth()), + '#default_value' => floor($this->menuLinkTree->maxDepth() / 2), + '#required' => TRUE, + ); + unset($form['max_depth']['#options'][0]); + $form['max_width'] = array( + '#type' => 'number', + '#title' => $this->t('Maximum menu width'), + '#default_value' => $this->getSetting('max_width'), + '#description' => $this->t('Limit the width of the generated menu\'s first level of links to a certain number of items.'), + '#required' => TRUE, + '#min' => 0, + ); + $form['kill'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Delete existing custom generated menus and menu links before generating new ones.'), + '#default_value' => $this->getSetting('kill'), + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function generateElements(array $values) { + // If the create new menus checkbox is off, set the number of new menus to 0. + if (!isset($values['existing_menus']['__new-menu__']) || !$values['existing_menus']['__new-menu__']) { + $values['num_menus'] = 0; + } + else { + // Unset the aux menu to avoid attach menu new items. + unset($values['existing_menus']['__new-menu__']); + } + + // Delete custom menus. + if ($values['kill']) { + $this->deleteMenus(); + $this->setMessage($this->t('Deleted existing menus and links.')); + } + + // Generate new menus. + $new_menus = $this->generateMenus($values['num_menus'], $values['title_length']); + if (!empty($new_menus)) { + $this->setMessage($this->t('Created the following new menus: @menus', array('@menus' => implode(', ', $new_menus)))); + } + + // Generate new menu links. + $menus = $new_menus; + if (isset($values['existing_menus'])) { + $menus = $menus + $values['existing_menus']; + } + $new_links = $this->generateLinks($values['num_links'], $menus, $values['title_length'], $values['link_types'], $values['max_depth'], $values['max_width']); + $this->setMessage($this->t('Created @count new menu links.', array('@count' => count($new_links)))); + } + + /** + * {@inheritdoc} + */ + public function validateDrushParams($args) { + + $link_types = array('node', 'front', 'external'); + $values = array( + 'num_menus' => array_shift($args), + 'num_links' => array_shift($args), + 'kill' => drush_get_option('kill'), + 'pipe' => drush_get_option('pipe'), + 'link_types' => array_combine($link_types, $link_types), + ); + + $max_depth = array_shift($args); + $max_width = array_shift($args); + $values['max_depth'] = $max_depth ? $max_depth : 3; + $values['max_width'] = $max_width ? $max_width : 8; + $values['title_length'] = $this->getSetting('title_length'); + $values['existing_menus']['__new-menu__'] = TRUE; + + if ($this->isNumber($values['num_menus']) == FALSE) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of menus')); + } + if ($this->isNumber($values['num_links']) == FALSE) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of links')); + } + if ($this->isNumber($values['max_depth']) == FALSE || $values['max_depth'] > 9 || $values['max_depth'] < 1) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum link depth. Use a value between 1 and 9')); + } + if ($this->isNumber($values['max_width']) == FALSE || $values['max_width'] < 1) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum menu width. Use a positive numeric value.')); + } + + return $values; + } + + /** + * Deletes custom generated menus. + */ + protected function deleteMenus() { + if ($this->moduleHandler->moduleExists('menu_ui')) { + $menu_ids = array(); + foreach (menu_ui_get_menus(FALSE) as $menu => $menu_title) { + if (strpos($menu, 'devel-') === 0) { + $menu_ids[] = $menu; + } + } + + if ($menu_ids) { + $menus = $this->menuStorage->loadMultiple($menu_ids); + $this->menuStorage->delete($menus); + } + } + + // Delete menu links generated by devel. + $link_ids = $this->menuLinkContentStorage->getQuery() + ->condition('menu_name', 'devel', '<>') + ->condition('link__options', '%' . db_like('s:5:"devel";b:1') . '%', 'LIKE') + ->execute(); + + if ($link_ids) { + $links = $this->menuLinkContentStorage->loadMultiple($link_ids); + $this->menuLinkContentStorage->delete($links); + } + + } + + /** + * Generates new menus. + * + * @param int $num_menus + * Number of menus to create. + * @param int $title_length + * (optional) Maximum length per menu name. + * + * @return array + * Array containing the generated vocabularies id. + */ + protected function generateMenus($num_menus, $title_length = 12) { + $menus = array(); + + if ($this->moduleHandler->moduleExists('menu_ui')) { + for ($i = 1; $i <= $num_menus; $i++) { + $name = $this->getRandom()->word(mt_rand(2, max(2, $title_length))); + + $menu = $this->menuStorage->create(array( + 'label' => $name, + 'id' => 'devel-' . Unicode::strtolower($name), + 'description' => $this->t('Description of @name', array('@name' => $name)), + )); + + $menu->save(); + $menus[$menu->id()] = $menu->label(); + } + } + + return $menus; + } + + /** + * Generates menu links in a tree structure. + */ + protected function generateLinks($num_links, $menus, $title_length, $link_types, $max_depth, $max_width) { + $links = array(); + $menus = array_keys(array_filter($menus)); + $link_types = array_keys(array_filter($link_types)); + + $nids = array(); + for ($i = 1; $i <= $num_links; $i++) { + // Pick a random menu. + $menu_name = $menus[array_rand($menus)]; + // Build up our link. + $link_title = $this->getRandom()->word(mt_rand(2, max(2, $title_length))); + $link = $this->menuLinkContentStorage->create(array( + 'menu_name' => $menu_name, + 'weight' => mt_rand(-50, 50), + 'title' => $link_title, + 'bundle' => 'menu_link_content', + 'description' => $this->t('Description of @title.', array('@title' => $link_title)), + )); + $link->link->options = array('devel' => TRUE); + + // For the first $max_width items, make first level links. + if ($i <= $max_width) { + $depth = 0; + } + else { + // Otherwise, get a random parent menu depth. + $depth = mt_rand(1, max(1, $max_depth - 1)); + } + // Get a random parent link from the proper depth. + do { + $parameters = new MenuTreeParameters(); + $parameters->setMinDepth($depth); + $parameters->setMaxDepth($depth); + $tree = $this->menuLinkTree->load($menu_name, $parameters); + + if ($tree) { + $link->parent = array_rand($tree); + } + $depth--; + } while (!$link->parent && $depth > 0); + + $link_type = array_rand($link_types); + switch ($link_types[$link_type]) { + case 'node': + // Grab a random node ID. + $select = db_select('node_field_data', 'n') + ->fields('n', array('nid', 'title')) + ->condition('n.status', 1) + ->range(0, 1) + ->orderRandom(); + // Don't put a node into the menu twice. + if (!empty($nids[$menu_name])) { + $select->condition('n.nid', $nids[$menu_name], 'NOT IN'); + } + $node = $select->execute()->fetchAssoc(); + if (isset($node['nid'])) { + $nids[$menu_name][] = $node['nid']; + $link->link->uri = 'entity:node/' . $node['nid']; + $link->title = $node['title']; + break; + } + + case 'external': + $link->link->uri = 'http://www.example.com/'; + break; + + case 'front': + $link->link->uri = 'internal:/'; + break; + + default: + $link->devel_link_type = $link_type; + break; + } + + $link->save(); + + $links[$link->id()] = $link->link_title; + } + + return $links; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php new file mode 100644 index 000000000..f7aa39b80 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php @@ -0,0 +1,267 @@ +vocabularyStorage = $vocabulary_storage; + $this->termStorage = $term_storage; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + $entity_manager = $container->get('entity.manager'); + return new static( + $configuration, $plugin_id, $plugin_definition, + $entity_manager->getStorage('taxonomy_vocabulary'), + $entity_manager->getStorage('taxonomy_term') + ); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $options = array(); + foreach ($this->vocabularyStorage->loadMultiple() as $vocabulary) { + $options[$vocabulary->id()] = $vocabulary->label(); + } + $form['vids'] = array( + '#type' => 'select', + '#multiple' => TRUE, + '#title' => $this->t('Vocabularies'), + '#required' => TRUE, + '#default_value' => 'tags', + '#options' => $options, + '#description' => $this->t('Restrict terms to these vocabularies.'), + ); + $form['num'] = array( + '#type' => 'number', + '#title' => $this->t('Number of terms?'), + '#default_value' => $this->getSetting('num'), + '#required' => TRUE, + '#min' => 0, + ); + $form['title_length'] = array( + '#type' => 'number', + '#title' => $this->t('Maximum number of characters in term names'), + '#default_value' => $this->getSetting('title_length'), + '#required' => TRUE, + '#min' => 2, + '#max' => 255, + ); + $form['kill'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Delete existing terms in specified vocabularies before generating new terms.'), + '#default_value' => $this->getSetting('kill'), + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function generateElements(array $values) { + if ($values['kill']) { + $this->deleteVocabularyTerms($values['vids']); + $this->setMessage($this->t('Deleted existing terms.')); + } + + $vocabs = $this->vocabularyStorage->loadMultiple($values['vids']); + $new_terms = $this->generateTerms($values['num'], $vocabs, $values['title_length']); + if (!empty($new_terms)) { + $this->setMessage($this->t('Created the following new terms: @terms', array('@terms' => implode(', ', $new_terms)))); + } + } + + /** + * Deletes all terms of given vocabularies. + * + * @param array $vids + * Array of vocabulary vid. + */ + protected function deleteVocabularyTerms($vids) { + $tids = $this->vocabularyStorage->getToplevelTids($vids); + $terms = $this->termStorage->loadMultiple($tids); + $this->termStorage->delete($terms); + } + + /** + * Generates taxonomy terms for a list of given vocabularies. + * + * @param int $records + * Number of terms to create in total. + * @param \Drupal\taxonomy\TermInterface[] $vocabs + * List of vocabularies to populate. + * @param int $maxlength + * (optional) Maximum length per term. + * + * @return array + * The list of names of the created terms. + */ + protected function generateTerms($records, $vocabs, $maxlength = 12) { + $terms = array(); + + // Insert new data: + $max = db_query('SELECT MAX(tid) FROM {taxonomy_term_data}')->fetchField(); + $start = time(); + for ($i = 1; $i <= $records; $i++) { + $name = $this->getRandom()->word(mt_rand(2, $maxlength)); + + $values = array( + 'name' => $name, + 'description' => 'description of ' . $name, + 'format' => filter_fallback_format(), + 'weight' => mt_rand(0, 10), + 'langcode' => Language::LANGCODE_NOT_SPECIFIED, + ); + + switch ($i % 2) { + case 1: + $vocab = $vocabs[array_rand($vocabs)]; + $values['vid'] = $vocab->id(); + $values['parent'] = array(0); + break; + + default: + while (TRUE) { + // Keep trying to find a random parent. + $candidate = mt_rand(1, $max); + $query = db_select('taxonomy_term_data', 't'); + $parent = $query + ->fields('t', array('tid', 'vid')) + ->condition('t.vid', array_keys($vocabs), 'IN') + ->condition('t.tid', $candidate, '>=') + ->range(0, 1) + ->execute() + ->fetchAssoc(); + if ($parent['tid']) { + break; + } + } + $values['parent'] = array($parent['tid']); + // Slight speedup due to this property being set. + $values['vid'] = $parent['vid']; + break; + } + + $term = $this->termStorage->create($values); + + // Populate all fields with sample values. + $this->populateFields($term); + $term->save(); + + $max++; + + if (function_exists('drush_log')) { + $feedback = drush_get_option('feedback', 1000); + if ($i % $feedback == 0) { + $now = time(); + drush_log(dt('Completed @feedback terms (@rate terms/min)', array('@feedback' => $feedback, '@rate' => $feedback * 60 / ($now - $start))), 'ok'); + $start = $now; + } + } + + // Limit memory usage. Only report first 20 created terms. + if ($i < 20) { + $terms[] = $term->label(); + } + + unset($term); + } + + return $terms; + } + + /** + * {@inheritdoc} + */ + public function validateDrushParams($args) { + $vocabulary_name = array_shift($args); + $number = array_shift($args); + + if ($number === NULL) { + $number = 10; + } + + if (!$vocabulary_name) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Please provide a vocabulary machine name.')); + } + + if (!$this->isNumber($number)) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of terms: @num', array('@num' => $number))); + } + + // Try to convert machine name to a vocabulary id. + if (!$vocabulary = $this->vocabularyStorage->load($vocabulary_name)) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid vocabulary name: @name', array('@name' => $vocabulary_name))); + } + + $values = [ + 'num' => $number, + 'kill' => drush_get_option('kill'), + 'title_length' => 12, + 'vids' => [$vocabulary->id()], + ]; + + return $values; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/UserDevelGenerate.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/UserDevelGenerate.php new file mode 100644 index 000000000..420ef8ff5 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/UserDevelGenerate.php @@ -0,0 +1,191 @@ +userStorage = $entity_storage; + $this->dateFormatter = $date_formatter; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, $plugin_id, $plugin_definition, + $container->get('entity.manager')->getStorage('user'), + $container->get('date.formatter') + ); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $form['num'] = array( + '#type' => 'number', + '#title' => $this->t('How many users would you like to generate?'), + '#default_value' => $this->getSetting('num'), + '#required' => TRUE, + '#min' => 0, + ); + + $form['kill'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Delete all users (except user id 1) before generating new users.'), + '#default_value' => $this->getSetting('kill'), + ); + + $options = user_role_names(TRUE); + unset($options[DRUPAL_AUTHENTICATED_RID]); + $form['roles'] = array( + '#type' => 'checkboxes', + '#title' => $this->t('Which roles should the users receive?'), + '#description' => $this->t('Users always receive the authenticated user role.'), + '#options' => $options, + ); + + $form['pass'] = array( + '#type' => 'textfield', + '#title' => $this->t('Password to be set'), + '#default_value' => $this->getSetting('pass'), + '#size' => 32, + '#description' => $this->t('Leave this field empty if you do not need to set a password'), + ); + + $options = array(1 => $this->t('Now')); + foreach (array(3600, 86400, 604800, 2592000, 31536000) as $interval) { + $options[$interval] = $this->dateFormatter->formatInterval($interval, 1) . ' ' . $this->t('ago'); + } + $form['time_range'] = array( + '#type' => 'select', + '#title' => $this->t('How old should user accounts be?'), + '#description' => $this->t('User ages will be distributed randomly from the current time, back to the selected time.'), + '#options' => $options, + '#default_value' => 604800, + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + protected function generateElements(array $values) { + $num = $values['num']; + $kill = $values['kill']; + $pass = $values['pass']; + $age = $values['time_range']; + $roles = array_filter($values['roles']); + + if ($kill) { + $uids = $this->userStorage->getQuery() + ->condition('uid', 1, '>') + ->execute(); + $users = $this->userStorage->loadMultiple($uids); + $this->userStorage->delete($users); + + $this->setMessage($this->formatPlural(count($uids), '1 user deleted', '@count users deleted.')); + } + + if ($num > 0) { + $names = array(); + while (count($names) < $num) { + $name = $this->getRandom()->word(mt_rand(6, 12)); + $names[$name] = ''; + } + + if (empty($roles)) { + $roles = array(DRUPAL_AUTHENTICATED_RID); + } + foreach ($names as $name => $value) { + $account = $this->userStorage->create(array( + 'uid' => NULL, + 'name' => $name, + 'pass' => $pass, + 'mail' => $name . '@example.com', + 'status' => 1, + 'created' => REQUEST_TIME - mt_rand(0, $age), + 'roles' => array_values($roles), + // A flag to let hook_user_* know that this is a generated user. + 'devel_generate' => TRUE, + )); + + // Populate all fields with sample values. + $this->populateFields($account); + $account->save(); + } + } + $this->setMessage($this->t('@num_users created.', array('@num_users' => $this->formatPlural($num, '1 user', '@count users')))); + } + + /** + * {@inheritdoc} + */ + public function validateDrushParams($args) { + $values = array( + 'num' => array_shift($args), + 'roles' => drush_get_option('roles') ? explode(',', drush_get_option('roles')) : array(), + 'kill' => drush_get_option('kill'), + 'pass' => drush_get_option('pass', NULL), + 'time_range' => 0, + ); + return $values; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/VocabularyDevelGenerate.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/VocabularyDevelGenerate.php new file mode 100644 index 000000000..a34a2ff4c --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/VocabularyDevelGenerate.php @@ -0,0 +1,175 @@ +vocabularyStorage = $entity_storage; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, $plugin_id, $plugin_definition, + $container->get('entity.manager')->getStorage('taxonomy_vocabulary') + ); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $form['num'] = array( + '#type' => 'number', + '#title' => $this->t('Number of vocabularies?'), + '#default_value' => $this->getSetting('num'), + '#required' => TRUE, + '#min' => 0, + ); + $form['title_length'] = array( + '#type' => 'number', + '#title' => $this->t('Maximum number of characters in vocabulary names'), + '#default_value' => $this->getSetting('title_length'), + '#required' => TRUE, + '#min' => 2, + '#max' => 255, + ); + $form['kill'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Delete existing vocabularies before generating new ones.'), + '#default_value' => $this->getSetting('kill'), + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function generateElements(array $values) { + if ($values['kill']) { + $this->deleteVocabularies(); + $this->setMessage($this->t('Deleted existing vocabularies.')); + } + + $new_vocs = $this->generateVocabularies($values['num'], $values['title_length']); + if (!empty($new_vocs)) { + $this->setMessage($this->t('Created the following new vocabularies: @vocs', array('@vocs' => implode(', ', $new_vocs)))); + } + } + + /** + * Deletes all vocabularies. + */ + protected function deleteVocabularies() { + $vocabularies = $this->vocabularyStorage->loadMultiple(); + $this->vocabularyStorage->delete($vocabularies); + } + + /** + * Generates vocabularies. + * + * @param int $records + * Number of vocabularies to create. + * @param int $maxlength + * (optional) Maximum length for vocabulary name. + * + * @return array + * Array containing the generated vocabularies id. + */ + protected function generateVocabularies($records, $maxlength = 12) { + $vocabularies = array(); + + // Insert new data: + for ($i = 1; $i <= $records; $i++) { + $name = $this->getRandom()->word(mt_rand(2, $maxlength)); + + $vocabulary = $this->vocabularyStorage->create(array( + 'name' => $name, + 'vid' => Unicode::strtolower($name), + 'langcode' => Language::LANGCODE_NOT_SPECIFIED, + 'description' => "Description of $name", + 'hierarchy' => 1, + 'weight' => mt_rand(0, 10), + 'multiple' => 1, + 'required' => 0, + 'relations' => 1, + )); + + // Populate all fields with sample values. + $this->populateFields($vocabulary); + $vocabulary->save(); + + $vocabularies[] = $vocabulary->id(); + unset($vocabulary); + } + + return $vocabularies; + } + + /** + * {@inheritdoc} + */ + public function validateDrushParams($args) { + $values = array( + 'num' => array_shift($args), + 'kill' => drush_get_option('kill'), + 'title_length' => 12, + ); + + if ($this->isNumber($values['num']) == FALSE) { + return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of vocabularies: @num.', array('@num' => $values['num']))); + } + + return $values; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/Routing/DevelGenerateRoutes.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/Routing/DevelGenerateRoutes.php new file mode 100644 index 000000000..6cf98d93b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/Routing/DevelGenerateRoutes.php @@ -0,0 +1,57 @@ +DevelGenerateManager = $devel_generate_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.develgenerate') + ); + } + + public function routes() { + $devel_generate_plugins = $this->DevelGenerateManager->getDefinitions(); + + $routes = array(); + foreach ($devel_generate_plugins as $id => $plugin) { + $label = $plugin['label']; + $type_url_str = str_replace('_', '-', $plugin['url']); + $routes["devel_generate.$id"] = new Route( + "admin/config/development/generate/$type_url_str", + array( + '_form' => '\Drupal\devel_generate\Form\DevelGenerateForm', + '_title' => "Generate $label", + '_plugin_id' => $id, + ), + array( + '_permission' => $plugin['permission'], + ) + ); + } + + return $routes; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/src/Tests/DevelGenerateTest.php b/sites/all/modules/contrib/dev/devel/devel_generate/src/Tests/DevelGenerateTest.php new file mode 100644 index 000000000..4782d6fbb --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/src/Tests/DevelGenerateTest.php @@ -0,0 +1,175 @@ +profile != 'standard') { + $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic Page')); + $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); + $this->addDefaultCommentField('node', 'article'); + } + + // Creating a vocabulary to associate taxonomy terms generated. + $this->vocabulary = entity_create('taxonomy_vocabulary', array( + 'name' => $this->randomMachineName(), + 'description' => $this->randomMachineName(), + 'vid' => Unicode::strtolower($this->randomMachineName()), + 'langcode' => Language::LANGCODE_NOT_SPECIFIED, + 'weight' => mt_rand(0, 10), + )); + $this->vocabulary->save(); + + // Creates a field of an entity reference field storage on article. + $field_name = 'taxonomy_' . $this->vocabulary->id(); + + $handler_settings = array( + 'target_bundles' => array( + $this->vocabulary->id() => $this->vocabulary->id(), + ), + 'auto_create' => TRUE, + ); + $this->createEntityReferenceField('node', 'article', $field_name, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED); + + entity_get_form_display('node', 'article', 'default') + ->setComponent($field_name, array( + 'type' => 'options_select', + )) + ->save(); + + entity_get_display('node', 'article', 'default') + ->setComponent($field_name, array( + 'type' => 'entity_reference_label', + )) + ->save(); + + $admin_user = $this->drupalCreateUser(array('administer devel_generate')); + $this->drupalLogin($admin_user); + } + + /** + * Tests generate commands + */ + public function testDevelGenerate() { + // Creating users. + $edit = array( + 'num' => 4, + ); + $this->drupalPostForm('admin/config/development/generate/user', $edit, t('Generate')); + $this->assertText(t('4 users created.')); + $this->assertText(t('Generate process complete.')); + + // Tests that if no content types are selected an error message is shown. + $edit = array( + 'num' => 4, + 'title_length' => 4, + ); + $this->drupalPostForm('admin/config/development/generate/content', $edit, t('Generate')); + $this->assertText(t('Please select at least one content type')); + + // Creating content. + // First we create a node in order to test the Delete content checkbox. + $this->drupalCreateNode(array('type' => 'article')); + + $edit = array( + 'num' => 4, + 'kill' => TRUE, + 'node_types[article]' => TRUE, + 'time_range' => 604800, + 'max_comments' => 3, + 'title_length' => 4, + 'add_alias' => 1, + ); + $this->drupalPostForm('admin/config/development/generate/content', $edit, t('Generate')); + $this->assertText(t('Deleted 1 nodes.')); + $this->assertText(t('Finished creating 4 nodes')); + $this->assertText(t('Generate process complete.')); + + // Tests that nodes have been created in the generation process. + $nodes = Node::loadMultiple(); + $this->assert(count($nodes) == 4, 'Nodes generated successfully.'); + + // Tests url alias for the generated nodes. + foreach ($nodes as $node) { + $alias = 'node-' . $node->id() . '-' . $node->bundle(); + $this->drupalGet($alias); + $this->assertResponse('200'); + $this->assertText($node->getTitle(), 'Generated url alias for the node works.'); + } + + // Creating terms. + $edit = array( + 'vids[]' => $this->vocabulary->id(), + 'num' => 5, + 'title_length' => 12, + ); + $this->drupalPostForm('admin/config/development/generate/term', $edit, t('Generate')); + $this->assertText(t('Created the following new terms: ')); + $this->assertText(t('Generate process complete.')); + + // Creating vocabularies. + $edit = array( + 'num' => 5, + 'title_length' => 12, + 'kill' => TRUE, + ); + $this->drupalPostForm('admin/config/development/generate/vocabs', $edit, t('Generate')); + $this->assertText(t('Created the following new vocabularies: ')); + $this->assertText(t('Generate process complete.')); + + // Creating menus. + $edit = array( + 'num_menus' => 5, + 'num_links' => 7, + 'title_length' => 12, + 'link_types[node]' => 1, + 'link_types[front]' => 1, + 'link_types[external]' => 1, + 'max_depth' => 4, + 'max_width' => 6, + 'kill' => 1, + ); + $this->drupalPostForm('admin/config/development/generate/menu', $edit, t('Generate')); + $this->assertText(t('Created the following new menus: ')); + $this->assertText(t('Created 7 new menu links')); + $this->assertText(t('Generate process complete.')); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.info.yml b/sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.info.yml new file mode 100644 index 000000000..43603a8ce --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.info.yml @@ -0,0 +1,14 @@ +name: 'Devel generate Example' +type: module +description: 'Create an example of DevelGenerate plugin type for testing purposing.' +package: Development +# core: 8.x +configure: admin/config/development/generate +tags: + - developer + +# Information added by Drupal.org packaging script on 2016-09-03 +version: '8.x-1.0-alpha1+23-dev' +core: '8.x' +project: 'devel' +datestamp: 1472897643 diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.module b/sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.module new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.module @@ -0,0 +1 @@ + 'textfield', + '#title' => $this->t('How many examples would you like to generate?'), + '#default_value' => $this->getSetting('num'), + '#size' => 10, + ); + + $form['kill'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Delete all examples before generating new examples.'), + '#default_value' => $this->getSetting('kill'), + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + protected function generateElements(array $values) { + $num = $values['num']; + $kill = $values['kill']; + + if ($kill) { + $this->setMessage($this->t('Old examples have been deleted.')); + } + + //Creating user in order to demonstrate + // how to override default business login generation. + $edit = array( + 'uid' => NULL, + 'name' => 'example_devel_generate', + 'pass' => '', + 'mail' => 'example_devel_generate@example.com', + 'status' => 1, + 'created' => REQUEST_TIME, + 'roles' => '', + 'devel_generate' => TRUE // A flag to let hook_user_* know that this is a generated user. + ); + + $account = user_load_by_name('example_devel_generate'); + if (!$account) { + $account = entity_create('user', $edit); + } + + // Populate all fields with sample values. + $this->populateFields($node); + + $account->save(); + + $this->setMessage($this->t('@num_examples created.', array('@num_examples' => $this->formatPlural($num, '1 example', '@count examples')))); + } + + public function validateDrushParams($args) { + $values = array( + 'num' => array_shift($args), + 'kill' => drush_get_option('kill'), + ); + return $values; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_generate/tests/src/DevelGenerateManagerTest.php b/sites/all/modules/contrib/dev/devel/devel_generate/tests/src/DevelGenerateManagerTest.php new file mode 100644 index 000000000..28e66e491 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_generate/tests/src/DevelGenerateManagerTest.php @@ -0,0 +1,93 @@ + array( + 'id' => 'devel_generate_example', + 'class' => 'Drupal\devel_generate_example\Plugin\DevelGenerate\ExampleDevelGenerate', + 'url' => 'devel_generate_example', + ), + ); + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + // Mock a Discovery object to replace AnnotationClassDiscovery. + $this->discovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface'); + $this->discovery->expects($this->any()) + ->method('getDefinitions') + ->will($this->returnValue($this->definitions)); + + } + + /** + * Test creating an instance of the DevelGenerateManager. + */ + public function testCreateInstance() { + $namespaces = new \ArrayObject(array('Drupal\devel_generate_example' => realpath(dirname(__FILE__) . '/../../../modules/devel_generate_example/lib'))); + $cache_backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + + $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); + $manager = new TestDevelGeneratePluginManager($namespaces, $cache_backend, $module_handler); + $manager->setDiscovery($this->discovery); + + $example_instance = $manager->createInstance('devel_generate_example'); + $plugin_def = $example_instance->getPluginDefinition(); + + $this->assertInstanceOf('Drupal\devel_generate_example\Plugin\DevelGenerate\ExampleDevelGenerate', $example_instance); + $this->assertArrayHasKey('url', $plugin_def); + $this->assertTrue($plugin_def['url'] == 'devel_generate_example'); + } + +} + +/** + * Provides a testing version of DevelGeneratePluginManager with an empty + * constructor. + */ +class TestDevelGeneratePluginManager extends DevelGeneratePluginManager { + /** + * Sets the discovery for the manager. + * + * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery + * The discovery object. + */ + public function setDiscovery(DiscoveryInterface $discovery) { + $this->discovery = $discovery; + } +} diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/README.txt b/sites/all/modules/contrib/dev/devel/devel_node_access/README.txt new file mode 100644 index 000000000..1d9362f37 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/README.txt @@ -0,0 +1,42 @@ +README +====== + +This module contains tools for developers using access control modules +to restrict access to some nodes. It is intended to help catch some +common mistakes and provide feedback to confirm that restricted nodes +are in fact visible only to the intended users. + +Provides a summary page which queries the node_access table and +reports common mistakes such as the presence of Drupal's default entry +which grants all users read access to all nodes. Also reports the +presence of nodes not represented in node_access table. This may +occur when an access control module is installed after nodes have +already been created. + +Provides a block which shows all node_access entries for the nodes +shown on a given page. This gives developers a quick check to see +that grants are provided as they should be. This block auto-enables +to the footer region. You may move it as desired. + +If Views module is installed, allows browsing of nodes by realm, +including those nodes not in the node_access table (NULL realm). + +WISHLIST +======== + +Things I'd like to see but haven't had time to do: + +* Automatically solve common problems. I.e. delete the "all" realm + entry, and automatically save all nodes not in the node_access table. + +* Nicer feedback indicating whether nodes are visible to the public or + not. I.e. use color coding or icons. + +* Summary does not differentiate between view grants and other types + of grants. I personally use node_access only for view grants so I'm + not sure exactly what else it should show. + +AUTHOR +====== + +Dave Cohen AKA yogadex on drupal.org diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/config/install/devel_node_access.settings.yml b/sites/all/modules/contrib/dev/devel/devel_node_access/config/install/devel_node_access.settings.yml new file mode 100644 index 000000000..9937f625e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/config/install/devel_node_access.settings.yml @@ -0,0 +1,3 @@ +debug_mode: FALSE +by_user_mode: FALSE +user_ajax: FALSE diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/config/schema/devel_node_access.schema.yml b/sites/all/modules/contrib/dev/devel/devel_node_access/config/schema/devel_node_access.schema.yml new file mode 100644 index 000000000..a8f582c7e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/config/schema/devel_node_access.schema.yml @@ -0,0 +1,15 @@ +# Schema for the configuration files of the Devel Node Access module. + +devel_node_access.settings: + type: mapping + label: 'Devel Node Access settings' + mapping: + debug_mode: + type: boolean + label: 'Debug Mode' + by_user_mode: + type: boolean + label: 'By-User Analysis' + user_ajax: + type: boolean + label: 'User AJAX' diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/css/devel_node_access.module.css b/sites/all/modules/contrib/dev/devel/devel_node_access/css/devel_node_access.module.css new file mode 100644 index 000000000..23e0b787a --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/css/devel_node_access.module.css @@ -0,0 +1,5 @@ + +div.devel-node-access-inline > div { + display: inline-block; +} + diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.api.php b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.api.php new file mode 100644 index 000000000..f33eef252 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.api.php @@ -0,0 +1,81 @@ +realm == 'mymodule_myrealm') { + if ($row->grant_view) { + /** @var \Drupal\user\RoleInterface $role */ + $role = \Drupal\user\Entity\Role::load($row->gid); + return t('Role %role may view this node.', array('%role' => $role->get('name'))); + } + else { + return 'No access.'; + } + } + return null; +} + +/** + * Acknowledge ownership of 'alien' grant records. + * + * Some node access modules store grant records directly into the {node_access} + * table rather than returning them through hook_node_access_records(). This + * practice is not recommended and DNA will flag all such records as 'alien'. + * + * If this is unavoidable, a module can confess to being the owner of these + * grant records, so that DNA can properly attribute them. + * + * @see hook_node_access_records() + * + * @ingroup node_access + */ +function hook_node_access_acknowledge($grant) { + if ($grant['realm'] == 'mymodule_all' && $grant['nid'] == 0) { + return TRUE; + } + return NULL; +} + +/** + * @} End of "addtogroup hooks". + */ diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.info.yml b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.info.yml new file mode 100644 index 000000000..4ceab8257 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.info.yml @@ -0,0 +1,16 @@ +type: module +name: 'Devel Node Access' +description: 'Developer blocks and page illustrating relevant node_access records.' +package: Development +# core: 8.x +dependencies: + - devel + - menu_ui +tags: + - developer + +# Information added by Drupal.org packaging script on 2016-09-03 +version: '8.x-1.0-alpha1+23-dev' +core: '8.x' +project: 'devel' +datestamp: 1472897643 diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.install b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.install new file mode 100644 index 000000000..ef2508fa4 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.install @@ -0,0 +1,7 @@ +' + + '' + + Drupal.t('Error: could not explain access') + + '' + + '' + ) + .addClass('ajax-processed'); + // Call this function again. + devel_node_access_user_ajax(context); + } + }, + 3000 + ); + + } + } + + /** + * Attach the access by user behavior which initiates ajax. + */ + Drupal.behaviors.develNodeAccessUserAjax = { + attach: function(context, settings) { + // Start the ajax. + devel_node_access_user_ajax(context, settings); + } + }; + +})(jQuery); diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.libraries.yml b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.libraries.yml new file mode 100644 index 000000000..061728a43 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.libraries.yml @@ -0,0 +1,7 @@ +devel_node_access: + version: 0 + js: + devel_node_access.js: {} + css: + component: + css/devel_node_access.module.css: {} diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.module b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.module new file mode 100644 index 000000000..dcef22a48 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.module @@ -0,0 +1,269 @@ +' . t('This module helps in site development. Specifically, when an access control module is used to limit access to some or all nodes, this module provides feedback showing the node_access table in the database.') . "

\n"; + $output .= '

' . t('The node_access table is one method Drupal provides to hide content from some users while displaying it to others. By default, Drupal shows all nodes to all users. There are a number of optional modules which may be installed to hide content from specific users based on various criteria.') . "

\n"; + $output .= '

' . t('If you have not installed any of these modules, you really have no need for the devel_node_access module. This module is intended for use during development, so that developers and admins can confirm that the node_access table is working as expected. You probably do not want this module enabled on a production site.') . "

\n"; + $output .= '

' . t('To use this module, enable its Devel Node Access block in a wide region. The block shows the entries in the node_access table for any nodes on the current page, and it provides options to display much more information at the cost of some overhead (requires JavaScript).') . "

\n"; +// $output .= '

' . t('This module also provides a summary page which shows general information about your node_access table. If you have installed the Views module, you may browse node_access by realm.', +// array('@summary_page' => url('devel/node_access/summary')) +// ) . "

\n"; + return $output; + } + return NULL; +} + +/** + * Implements hook_menu(). + */ +function devel_node_access_menu() { + $items = array(); + + if (!\Drupal::moduleHandler()->moduleExists('devel')) { + // We have to create the 'Devel settings' menu item ourselves. + $items['admin/config/development/devel'] = array( + 'title' => 'Devel settings', + 'description' => 'Helper pages and blocks to assist Drupal developers and admins with node_access. The devel blocks can be managed via the block administration page.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('devel_node_access_admin_settings'), + 'access arguments' => array('administer site configuration'), + ); + $items['devel/settings'] = $items['admin/config/development/devel'] + array( + 'menu_name' => 'devel', + ); + } + + // Create a callback for use by devel_node_access_user_ajax(). + $items['devel/node_access/by_user/%/%'] = array( + 'page callback' => 'devel_node_access_user_ajax', + 'page arguments' => array(3, 4), + 'access arguments' => array(DNA_ACCESS_VIEW), + 'type' => MENU_CALLBACK, + ); + + // Add this to the custom menu 'devel' created by the devel module. + $items['devel/node_access/summary'] = array( + 'title' => 'Node_access summary', + 'page callback' => 'dna_summary', + 'access arguments' => array(DNA_ACCESS_VIEW), + 'menu_name' => 'devel', + ); + + return $items; +} + +/** + * Builds the DNA Summary page. + */ +function dna_summary() { + // Warn user if they have any entries that could grant access to all nodes. + $output = ''; + $result = db_query('SELECT DISTINCT realm FROM {node_access} WHERE nid = 0 AND gid = 0'); + $rows = array(); + foreach ($result as $row) { + $rows[] = array($row->realm); + } + if (!empty($rows)) { + $output .= '

' . t('Access Granted to All Nodes (All Users)') . "

\n"; + $output .= '

' . t('Your node_access table contains entries that may be granting all users access to all nodes. Depending on which access control module(s) you use, you may want to delete these entries. If you are not using an access control module, you should probably leave these entries as is.') . "

\n"; + $headers = array(t('realm')); + $output .= theme('table', array('header' => $headers, 'rows' => $rows)); + $access_granted_to_all_nodes = TRUE; + } + + // How many nodes are not represented in the node_access table? + $num = db_query('SELECT COUNT(n.nid) AS num_nodes FROM {node} n LEFT JOIN {node_access} na ON n.nid = na.nid WHERE na.nid IS NULL')->fetchField(); + if ($num) { + $output .= '

' . t('Legacy Nodes') . "

\n"; + $output .= '

' . + t('You have !num nodes in your node table which are not represented in your node_access table. If you have an access control module installed, these nodes may be hidden from all users. This could be caused by publishing nodes before enabling the access control module. If this is the case, manually updating each node should add it to the node_access table and fix the problem.', array('!num' => l($num, 'devel/node_access/view/NULL'))) + . "

\n"; + if (!empty($access_granted_to_all_nodes)) { + $output .= '

' . + t('This issue may be masked by the one above, so look into the former first.') + . "

\n"; + } + } + else { + $output .= '

' . t('All Nodes Represented') . "

\n"; + $output .= '

' . t('All nodes are represented in the node_access table.') . "

\n"; + } + + + // A similar warning to the one above, but slightly more specific. + $result = db_query('SELECT DISTINCT realm FROM {node_access} WHERE nid = 0 AND gid <> 0'); + $rows = array(); + foreach ($result as $row) { + $rows[] = array($row->realm); + } + if (!empty($rows)) { + $output .= '

' . t('Access Granted to All Nodes (Some Users)') . "

\n"; + $output .= '

' . t('Your node_access table contains entries that may be granting some users access to all nodes. This may be perfectly normal, depending on which access control module(s) you use.') . "

\n"; + $headers = array(t('realm')); + $output .= theme('table', array('header' => $headers, 'rows' => $rows)); + } + + + // Find specific nodes which may be visible to all users. + $result = db_query('SELECT DISTINCT realm, COUNT(DISTINCT nid) as node_count FROM {node_access} WHERE gid = 0 AND nid > 0 GROUP BY realm'); + $rows = array(); + foreach ($result as $row) { + $rows[] = array( + $row->realm, + array( + 'data' => $row->node_count, + 'align' => 'center', + ), + ); + } + if (!empty($rows)) { + $output .= '

' . t('Access Granted to Some Nodes') . "

\n"; + $output .= '

' . + t('The following realms appear to grant all users access to some specific nodes. This may be perfectly normal, if some of your content is available to the public.') + . "

\n"; + $headers = array(t('realm'), t('public nodes')); + $output .= theme('table', array('header' => $headers, 'rows' => $rows, 'caption' => t('Public Nodes'))); + } + + + // Find specific nodes protected by node_access table. + $result = db_query('SELECT DISTINCT realm, COUNT(DISTINCT nid) as node_count FROM {node_access} WHERE gid <> 0 AND nid > 0 GROUP BY realm'); + $rows = array(); + foreach ($result as $row) { + // No Views yet: + //$rows[] = array(l($row->realm, "devel/node_access/view/$row->realm"), + $rows[] = array( + $row->realm, + array( + 'data' => $row->node_count, + 'align' => 'center', + ), + ); + } + if (!empty($rows)) { + $output .= '

' . t('Summary by Realm') . "

\n"; + $output .= '

' . t('The following realms grant limited access to some specific nodes.') . "

\n"; + $headers = array(t('realm'), t('private nodes')); + $output .= theme('table', array('header' => $headers, 'rows' => $rows, 'caption' => t('Protected Nodes'))); + } + + return $output; +} + +/** + * Implements hook_node_view(). + * + * {@inheritdoc} + */ +function devel_node_access_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) { + // Remember this node, for display in our block. + DnaBlock::visibleNodes($entity->id()); +} + +/** + * Callback function for Node Access by User block ajax. + */ +function devel_node_access_user_ajax($data_type, $cell) { + list($op, $nid, $uid) = explode('-', $cell); + $node = node_load($nid); + $account = user_load($uid); + $output = array( + '#theme' => 'dna_permission', + '#permission' => DnaBlock::explainAccess($op, $node, $account), + ); + // JSON output for Ajax callbacks. + if ($data_type == 'json') { + drupal_json_output(\Drupal::service('renderer')->render($output)); + exit; + } + // HTML output for error message click-throughs. + if ($node) { + $node_title = l($node->title, 'node/' . $node->id()); + } + else { + $node_title = t('unknown node %nid', array('%nid' => $nid)); + } + drupal_set_title( + t( + 'Devel Node Access: %op permission for !user on !node', + array( + '%op' => $op, + '!user' => theme('username', array('account' => $account)), + '!node' => $node_title, + ) + ), + PASS_THROUGH + ); + return $output; +} + +/** + * Implements hook_node_access_explain(). + * + * {@inheritdoc} + */ +function devel_node_access_node_access_explain($row) { + if ($row->gid == 0 && $row->realm == 'all') { + foreach (array('view', 'update', 'delete') as $op) { + $gop = 'grant_' . $op; + if (!empty($row->$gop)) { + $ops[] = $op; + } + } + if (empty($ops)) { + return '(No access granted to ' . ($row->nid == 0 ? 'any nodes.)' : 'this node.)'); + } + else { + return 'All users may ' . implode('/', $ops) . ($row->nid == 0 ? ' all nodes.' : ' this node.'); + } + } + if (\Drupal::moduleHandler()->moduleExists('node_access_test')) { + switch ($row->realm) { + case 'node_access_test_author': + return "Author (uid=$row->gid) has full access."; + case 'node_access_test': + return "Users with the 'node test view' permission may view."; + } + } + return NULL; +} + +/** + * Implements hook_theme(). + */ +function devel_node_access_theme() { + return array( + 'dna_permission' => array( + 'variables' => array( + 'permission' => NULL, + ), + ), + ); +} + +/** + * Indicates whether user has a permission or not. + */ +function theme_dna_permission($variables) { + $permission = &$variables['permission']; + return '' . $permission[1] . ''; +} diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.permissions.yml b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.permissions.yml new file mode 100644 index 000000000..460479c24 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/devel_node_access.permissions.yml @@ -0,0 +1,4 @@ +view devel_node_access information: + title: 'Access DNA information' + description: 'View the node_access information block on node pages and the summary page.' + restrict access: TRUE diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/src/Form/DnaForm.php b/sites/all/modules/contrib/dev/devel/devel_node_access/src/Form/DnaForm.php new file mode 100644 index 000000000..8d303c758 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/src/Form/DnaForm.php @@ -0,0 +1,135 @@ + array( + 'devel_node_access/devel_node_access' + ), + ); + + $debug_mode = \Drupal::config('devel_node_access.settings')->get('debug_mode'); + $by_user_mode = \Drupal::config('devel_node_access.settings')->get('by_user_mode'); + + $form['node_content'] = array( + '#prefix' => '
', + '#suffix' => '
', + ); + $form['node_content'][0] = DnaBlock::buildNodeInfo($debug_mode); + + $form['user_content'] = array( + '#prefix' => '
', + '#suffix' => '
', + ); + if ($by_user_mode) { + $form['user_content'][0] = DnaBlock::buildByUserInfo(); + } + + $form['setup'] = array( + '#markup' => t('Enable:'), + '#prefix' => '
', + '#suffix' => '
', + ); + $form['setup']['debug_mode'] = array( + '#type' => 'checkbox', + '#value' => $debug_mode, + '#prefix' => '     ', + '#title' => t('Debug Mode'), + '#ajax' => array( + 'callback' => '::toggleDebugMode', + 'wrapper' => 'devel-node-access-node-content-div', + ), + '#disabled' => TRUE, + ); + $form['setup']['by_user_mode'] = array( + '#type' => 'checkbox', + '#value' => $by_user_mode, + '#prefix' => '     ', + '#title' => t('By-User Analysis (slow!)'), + '#ajax' => array( + 'callback' => '::toggleByUserMode', + 'wrapper' => 'devel-node-access-by-user-content-div', + ), + '#disabled' => TRUE, + ); + + return $form; + } + + /** + * AJAX handler for form_state. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + */ + public function toggleDebugMode($form, FormStateInterface $form_state) { + $debug_mode = $form_state->getUserInput(); + $debug_mode = !empty($debug_mode['debug_mode']); + \Drupal::configFactory()->getEditable('devel_node_access.settings')->set('debug_mode', $debug_mode)->save(TRUE); + $form['node_content'][0] = DnaBlock::buildNodeInfo($debug_mode); + return $form['node_content']; + } + + /** + * AJAX handler for by_user_state. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + */ + public function toggleByUserMode($form, FormStateInterface $form_state) { + $by_user_mode = $form_state->getUserInput(); + $by_user_mode = !empty($by_user_mode['by_user_mode']); + \Drupal::configFactory()->getEditable('devel_node_access.settings')->set('by_user_mode', $by_user_mode)->save(TRUE); + $form['user_content'][0] = ($by_user_mode ? DnaBlock::buildByUserInfo() : []); + return $form['user_content']; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + } + +} diff --git a/sites/all/modules/contrib/dev/devel/devel_node_access/src/Plugin/Block/DnaBlock.php b/sites/all/modules/contrib/dev/devel/devel_node_access/src/Plugin/Block/DnaBlock.php new file mode 100644 index 000000000..c817d3913 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/devel_node_access/src/Plugin/Block/DnaBlock.php @@ -0,0 +1,1263 @@ + 'node_access'); + + use RedirectDestinationTrait; + + /** + * The FormBuilder object. + * + * @var \Drupal\Core\Form\FormBuilderInterface + */ + protected $formBuilder; + + /** + * The Current User object. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** + * The user storage. + * + * @var \Drupal\Core\Entity\EntityStorageInterface + */ + protected $userStorage; + + /** + * Constructs a new DnaBlock object. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Session\AccountInterface $current_user + * Current user. + * @param \Drupal\Core\Entity\EntityStorageInterface $user_storage + * The user storage. + * @param \Drupal\Core\Form\FormBuilderInterface $form_builder + * The form builder service. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, AccountInterface $current_user, EntityStorageInterface $user_storage, FormBuilderInterface $form_builder) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->formBuilder = $form_builder; + $this->currentUser = $current_user; + $this->userStorage = $user_storage; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + /** @noinspection PhpParamsInspection */ + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('current_user'), + $container->get('entity.manager')->getStorage('user'), + $container->get('form_builder') + ); + } + + /** + * {@inheritdoc} + */ + protected function blockAccess(AccountInterface $account) { + return AccessResult::allowedIfHasPermission($account, DNA_ACCESS_VIEW); + } + + /** + * {@inheritdoc} + */ + public function build() { + $build['#cache'] = ['max-age' => 0]; + +// $headers = array( +// array('data' => t('node'), 'style' => 'text-align:right;'), +// array('data' => t('prio'), 'style' => 'text-align:right;'), +// t('status'), +// t('realm'), +// array('data' => t('gid'), 'style' => 'text-align:right;'), +// t('view'), +// t('update'), +// t('delete'), +// t('explained'), +// ); +// $rows[] = array('data' => array(array('data' => 1, 'style' => 'background-color: red; color: blue'), +// "CACHE 5", 3, 4, 5)); + + if (empty(self::visibleNodes())) { + $request_uri = \Drupal::request()->getRequestUri(); + if ($request_uri === '/' || $request_uri === '/node') { + $build['#markup'] = t('@node_access information is not available for the nodes on this page due to caching — flush your caches to display it.', $this->node_access); + } + return $build; + } + + $build['#title'] = t('@node_access entries for nodes shown on this page', $this->node_access); +// $build['test_table'] = array( +// '#type' => 'table', +// '#header' => $headers, +// // array( +// // t('Column 0'), +// // array('data' => t('Active'), 'colspan' => '2'), +// // array('data' => t('Staged'), 'colspan' => '2'), +// // ), +// '#rows' => $rows, +// '#empty' => $this->t('No records found!'), +// '#responsive' => FALSE, +// '#attributes' => array( +// 'class' => array(), +// 'style' => 'text-align: left;', +// ), +// ); + + $build['dna_form'] = $this->formBuilder->getForm('\Drupal\devel_node_access\Form\DnaForm'); + return $build; + } + + /** + * Builds and returns the node information. + * + * @param bool $debug_mode + * The level of detail to include. + * + * @return array + */ + public static function buildNodeInfo($debug_mode) { + global $user; + + $visible_nodes = self::visibleNodes(); + if (count($visible_nodes) == 0) { + return array(); + } + else { + $single_nid = reset($visible_nodes); + } + + // Find out whether our DnaUser block is active or not. + //dpm($blocks = \Drupal::entityTypeManager()->getStorage('block')->load()); + $user_block_active = FALSE; + //foreach ($blocks as $block) { + // if ($block->get('plugin') == 'devel_dna_user_block') { + // $user_block_active = TRUE; + // } + //} + + + // Include rows where nid == 0. + $nids = array_merge(array(0 => 0), $visible_nodes); + $query = \Drupal::database()->select('node_access', 'na'); + $query + ->fields('na') + ->condition('na.nid', $nids, 'IN'); + $query + ->orderBy('na.nid') + ->orderBy('na.realm') + ->orderBy('na.gid'); + $nodes = Node::loadMultiple($nids); + + if (!$debug_mode) { + $headers = array('node', 'realm', 'gid', 'view', 'update', 'delete', 'explained'); + $rows = array(); + foreach ($query->execute() as $row) { + $explained = \Drupal::moduleHandler()->invokeAll('node_access_explain', [$row]); + $node_title = self::get_node_title($nodes[$row->nid]); + $title_attribute = \Drupal::request()->getRequestUri(); + if (Unicode::strlen($node_title) > 20) { + $title_attribute = $title_attribute . ': ' . $node_title; + $node_title = Unicode::substr($node_title, 0, 18) . '...'; + } + + $rows[] = array( + (empty($row->nid) ? '0' + : Link::fromTextAndUrl( + $node_title, + Url::fromUri( + \Drupal::request()->getUri(), + [ + 'fragment' => 'node-' . $row->nid, + 'attributes' => ['title' => $title_attribute] + ] + ) + ) + ), + $row->realm, + $row->gid, + $row->grant_view, + $row->grant_update, + $row->grant_delete, + implode('
', $explained), + ); + } + $output[] = array( + '#theme' => 'table', + '#header' => $headers, + '#rows' => $rows, + '#attributes' => array('style' => 'text-align: left'), + ); + } + else { + $tr = 't'; + $variables = array('@na' => '{node_access}'); + $states = array( + 'default' => array(t('default'), 'ok', t('Default record supplied by core in the absence of any other non-empty records; in @na.', $variables)), + 'ok' => array(t('ok'), 'ok', t('Highest priority record; in @na.', $variables)), + 'removed' => array(t('removed'), '', t('Was removed in @func; not in @na.', $variables + array('@func' => 'hook_node_access_records_alter()'))), + 'static' => array(t('static'), 'ok', t('Non-standard record in @na.', $variables)), + 'unexpected' => array(t('unexpected'), 'warning', t('The 0/all/0/... record applies to all nodes and all users -- usually it should not be present in @na if any node access module is active!')), + 'ignored' => array(t('ignored'), 'warning', t('Lower priority record; not in @na and thus ignored.', $variables)), + 'empty' => array(t('empty'), 'warning', t('Does not grant any access, but could block lower priority records; not in @na.', $variables)), + 'wrong' => array(t('wrong'), 'error', t('Is rightfully in @na but at least one access flag is wrong!', $variables)), + 'missing' => array(t('missing'), 'error', t("Should be in @na but isn't!", $variables)), + 'removed!' => array(t('removed!'), 'error', t('Was removed in @func; should NOT be in @na!', $variables + array('@func' => 'hook_node_access_records_alter()'))), + 'illegitimate' => array(t('illegitimate'), 'error', t('Should NOT be in @na because of lower priority!', $variables)), + 'alien' => array(t('alien'), 'error', t('Should NOT be in @na because of unknown origin!', $variables)), + ); + $active_states = array('default', 'ok', 'static', 'unexpected', 'wrong', 'illegitimate', 'alien'); + $headers = array(t('node'), t('prio'), t('status'), t('realm'), t('gid'), t('view'), t('update'), t('delete'), t('explained')); + $headers = self::format_row($headers); + $active_records = array(); + foreach ($query->execute() as $active_record) { + $active_records[$active_record->nid][$active_record->realm][$active_record->gid] = $active_record; + } + $all_records = $grants_data = $checked_grants = $grants = array(); + + foreach (array('view', 'update', 'delete') as $op) { + $grants[$op] = self::simulate_module_invoke_all('node_grants', $user, $op); + // Call all hook_node_grants_alter() implementations. + $grants_data[$op] = self::simulate_node_grants_alter($grants[$op], $user, $op); + } + + foreach ($nids as $nid) { + $top_priority = -99999; + $acquired_records_nid = array(); + if ($node = Node::load($nid)) { + // Check node_access_acquire_grants(). + $records = self::simulate_module_invoke_all('node_access_records', $node); + // Check drupal_alter('node_access_records'). + $data = self::simulate_node_access_records_alter($records, $node); + if (!empty($data)) { + foreach ($data as $data_by_realm) { + foreach ($data_by_realm as $data_by_realm_gid) { + if (isset($data_by_realm_gid['current'])) { + $record = $data_by_realm_gid['current']; + } + elseif (isset($data_by_realm_gid['original'])) { + $record = $data_by_realm_gid['original']; + $record['#removed'] = 1; + } + else { + continue; + } + $priority = intval(isset($record['priority']) ? $record['priority'] : 0); + $top_priority = (isset($top_priority) ? max($top_priority, $priority) : $priority); + $record['priority'] = (isset($record['priority']) ? $priority : '– '); + $record['history'] = $data_by_realm_gid; + $acquired_records_nid[$priority][$record['realm']][$record['gid']] = $record + array( + '#title' => self::get_node_title($node), + '#module' => (isset($record['#module']) ? $record['#module'] : ''), + ); + } + } + krsort($acquired_records_nid); + } + //dpm($acquired_records_nid, "acquired_records_nid ="); + + // Check node_access_grants(). + if ($node->id()) { + foreach (array('view', 'update', 'delete') as $op) { + $checked_grants[$nid][$op] = array_merge(array('all' => array(0)), $grants[$op]); + } + } + } + + // Check for records in the node_access table that aren't returned by + // node_access_acquire_grants(). + + if (isset($active_records[$nid])) { + foreach ($active_records[$nid] as $realm => $active_records_realm) { + foreach ($active_records_realm as $gid => $active_record) { + $found = FALSE; + $count_nonempty_records = 0; + foreach ($acquired_records_nid as $priority => $acquired_records_nid_priority) { + if (isset($acquired_records_nid_priority[$realm][$gid])) { + $found = TRUE; + } + } + // Take the highest priority only. + // TODO This has changed in D8! + if ($acquired_records_nid_priority = reset($acquired_records_nid)) { + foreach ($acquired_records_nid_priority as $acquired_records_nid_priority_realm) { + foreach ($acquired_records_nid_priority_realm as $acquired_records_nid_priority_realm_gid) { + $count_nonempty_records += (!empty($acquired_records_nid_priority_realm_gid['grant_view']) || !empty($acquired_records_nid_priority_realm_gid['grant_update']) || !empty($acquired_records_nid_priority_realm_gid['grant_delete'])); + } + } + } + $fixed_record = (array) $active_record; + if ($count_nonempty_records == 0 && $realm == 'all' && $gid == 0) { + $fixed_record += array( + 'priority' => '–', + 'state' => 'default', + ); + } + elseif (!$found) { + $acknowledged = self::simulate_module_invoke_all('node_access_acknowledge', $fixed_record); + if (empty($acknowledged)) { + // No module acknowledged this record, mark it as alien. + $fixed_record += array( + 'priority' => '?', + 'state' => 'alien', + ); + } + else { + // At least one module acknowledged the record, + // attribute it to the first one. + $fixed_record += array( + 'priority' => '–', + 'state' => 'static', + '#module' => reset(array_keys($acknowledged)), + ); + } + } + else { + continue; + } + $fixed_record += array( + 'nid' => $nid, + '#title' => self::get_node_title($node), + ); + $all_records[] = $fixed_record; + } + } + } + + // Order records and evaluate their status. + foreach ($acquired_records_nid as $priority => $acquired_records_priority) { + ksort($acquired_records_priority); + foreach ($acquired_records_priority as $realm => $acquired_records_realm) { + ksort($acquired_records_realm); + foreach ($acquired_records_realm as $gid => $acquired_record) { + // TODO: Handle priority. + //if ($priority == $top_priority) { + if (empty($acquired_record['grant_view']) && empty($acquired_record['grant_update']) && empty($acquired_record['grant_delete'])) { + $acquired_record['state'] = 'empty'; + } + else { + if (isset($active_records[$nid][$realm][$gid])) { + $acquired_record['state'] = (isset($acquired_record['#removed']) ? 'removed!' : 'ok'); + } + else { + $acquired_record['state'] = (isset($acquired_record['#removed']) ? 'removed' : 'missing'); + } + if ($acquired_record['state'] == 'ok') { + foreach (array('view', 'update', 'delete') as $op) { + $active_record = (array) $active_records[$nid][$realm][$gid]; + if (empty($acquired_record["grant_$op"]) != empty($active_record["grant_$op"])) { + $acquired_record["grant_$op!"] = $active_record["grant_$op"]; + } + } + } + } + //} + //else { + // $acquired_record['state'] = (isset($active_records[$nid][$realm][$gid]) ? 'illegitimate' : 'ignored'); + //} + $all_records[] = $acquired_record + array('nid' => $nid); + } + } + } + } + + // Fill in the table rows. + $rows = array(); + $error_count = 0; + foreach ($all_records as $record) { + $row = new \stdClass(); + $row->nid = $record['nid']; + $row->title = $record['#title']; + $row->priority = $record['priority']; + $row->state = array('data' => $states[$record['state']][0], 'title' => $states[$record['state']][2]); + $row->realm = $record['realm']; + $row->gid = $record['gid']; + $row->grant_view = $record['grant_view']; + $row->grant_update = $record['grant_update']; + $row->grant_delete = $record['grant_delete']; + $row->explained = implode('
', \Drupal::moduleHandler()->invokeAll('node_access_explain', $row)); + unset($row->title); + if ($row->nid == 0 && $row->gid == 0 && $row->realm == 'all' && count($all_records) > 1) { + $row->state = array('data' => $states['unexpected'][0], 'title' => $states['unexpected'][2]); + $class = $states['unexpected'][1]; + } + else { + $class = $states[$record['state']][1]; + } + $row = (array) $row; + foreach (array('view', 'update', 'delete') as $op) { + $row["grant_$op"] = array('data' => $row["grant_$op"]); + if ((isset($checked_grants[$record['nid']][$op][$record['realm']]) && in_array($record['gid'], $checked_grants[$record['nid']][$op][$record['realm']]) || ($row['nid'] == 0 && $row['gid'] == 0 && $row['realm'] == 'all')) && !empty($row["grant_$op"]['data']) && in_array($record['state'], $active_states)) { + $row["grant_$op"]['data'] .= '′'; + $row["grant_$op"]['title'] = t('This entry grants access to this node to this user.'); + } + if (isset($record["grant_$op!"])) { + $row["grant_$op"]['data'] = $record["grant_$op!"] . '>' . (!$row["grant_$op"]['data'] ? 0 : $row["grant_$op"]['data']); + $row["grant_$op"]['class'][] = 'error'; + if ($class == 'ok') { + $row['state'] = array('data' => $states['wrong'][0], 'title' => $states['wrong'][2]); + $class = $states['wrong'][1]; + } + } + } + $error_count += ($class == 'error'); + $row['nid'] = array( + 'data' => '' . $row['nid'] . '', + 'title' => $record['#title'], + ); + if (empty($record['#module']) || strpos($record['realm'], $record['#module']) === 0) { + $row['realm'] = $record['realm']; + } + else { + $row['realm'] = array( + 'data' => '(' . $record['#module'] . '::) ' . $record['realm'], + 'title' => t("The '@module' module fails to adhere to the best practice of naming its realm(s) after itself.", array('@module' => $record['#module'])), + ); + } + + // Prepend information from the D7 hook_node_access_records_alter(). + $next_style = array(); + if (isset($record['history'])) { + $history = $record['history']; + if (($num_changes = count($history['changes']) - empty($history['current'])) > 0) { + $first_row = TRUE; + while (isset($history['original']) || $num_changes--) { + if (isset($history['original'])) { + $this_record = $history['original']; + $this_action = '[ Original by ' . $this_record['#module'] . ':'; + unset($history['original']); + } + else { + $change = $history['changes'][0]; + $this_record = $change['record']; + $this_action = ($first_row ? '[ ' : '') . $change['op'] . ':'; + array_shift($history['changes']); + } + $rows[] = array( + 'data' => array( + 'data' => array( + 'data' => $this_action, + 'style' => array('padding-bottom: 0;'), + ), + ), + 'style' => array_merge(($first_row ? array() : array('border-top-style: dashed;', 'border-top-width: 1px;')), array('border-bottom-style: none;')), + ); + $next_style = array('border-top-style: none;'); + if (count($history['changes'])) { + $g = $this_record; + $rows[] = array( + 'data' => array('v', $g['priority'], '', $g['realm'], $g['gid'], $g['grant_view'], $g['grant_update'], $g['grant_delete'], 'v'), + 'style' => array('border-top-style: none;', 'border-bottom-style: dashed;'), + ); + $next_style = array('border-top-style: dashed;'); + } + $first_row = FALSE; + } + } + } + + // Fix up the main row cells with the proper class (needed for Bartik). + foreach ($row as $key => $value) { + if (!is_array($value)) { + $row[$key] = array('data' => $value); + } + $row[$key]['class'] = array($class); + } + // Add the main row. + $will_append = empty($history['current']) && !empty($history['changes']); + $rows[] = array( + 'data' => array_values($row), + 'class' => array($class), + 'style' => array_merge($next_style, ($will_append ? array('border-bottom-style: none;') : array())), + ); + + // Append information from the D7 hook_node_access_records_alter(). + if ($will_append) { + $last_change = end($history['changes']); + $rows[] = array( + 'data' => array( + 'data' => array( + 'data' => $last_change['op'] . ' ]', + 'style' => array('padding-top: 0;'), + ), + ), + 'style' => array('border-top-style: none;'), + ); + } + } + + foreach ($rows as $i => $row) { + $rows[$i] = self::format_row($row); + } + + $output[] = array( + '#theme' => 'table', + '#header' => $headers, + '#rows' => $rows, + '#attributes' => array( + 'class' => array('system-status-report'), + 'style' => 'text-align: left;', + ), + ); + + $output[] = array( + '#theme' => 'form_element', + '#description' => t('(Some of the table elements provide additional information if you hover your mouse over them.)'), + ); + + if ($error_count > 0) { + $variables['@Rebuild_permissions'] = '' . $tr('Rebuild permissions') . ''; + $output[] = array( + '#prefix' => "\n", + '#markup' => t("You have errors in your @na table! You may be able to fix these for now by running @Rebuild_permissions, but this is likely to destroy the evidence and make it impossible to identify the underlying issues. If you don't fix those, the errors will probably come back again.
DON'T do this just yet if you intend to ask for help with this situation.", $variables), + '#suffix' => "

\n", + ); + } + + // Explain whether access is granted or denied, and why + // (using code from node_access()). + $tr = 't'; + array_shift($nids); // Remove the 0. + $accounts = array(); + $variables += array( + //'!username' => '' . theme('username', array('account' => $user)) . '', + '@username' => '' . $user->getDisplayName() . '', + '%uid' => $user->id(), + ); + + if (\Drupal::currentUser()->hasPermission('bypass node access')) { + $variables['%bypass_node_access'] = $tr('bypass node access'); + $output[] = array( + '#markup' => t('@username has the %bypass_node_access permission and thus full access to all nodes.', $variables), + '#suffix' => '
 ', + ); + } + else { + $variables['@list'] = '
' . self::get_grant_list($grants_data['view']) . '
'; + $variables['%access'] = 'view'; + $output[] = array( + '#prefix' => "\n
", + '#markup' => t('@username (user %uid) can use these grants (if they are present above) for %access access: @list', $variables), + '#suffix' => "
\n", + ); + $accounts[] = $user; + } + + if (isset($single_nid) && !$user_block_active) { + // Only for single nodes. + if (\Drupal::currentUser()->isAuthenticated()) { + $accounts[] = User::load(0); // Anonymous, too. + } + foreach ($accounts as $account) { + $nid_items = array(); + foreach ($nids as $nid) { + $op_items = array(); + foreach (array('create', 'view', 'update', 'delete') as $op) { + $explain = self::explainAccess($op, Node::load($nid), $account); + $op_items[] = "
" . t('%op:', array('%op' => $op)) . '
' . $explain[2]; + } + $nid_items[] = array( + '#theme' => 'item_list', + '#items' => $op_items, + '#type' => 'ul', + '#prefix' => t('to node @nid:', array('@nid' => l($nid, 'node/' . $nid))) . "\n
", + '#suffix' => '
', + ); + } + if (count($nid_items) == 1) { + $account_items = $nid_items[0]; + } + else { + $account_items = array( + '#theme' => 'item_list', + '#items' => $nid_items, + '#type' => 'ul', + '#prefix' => "\n
", + '#suffix' => '
', + ); + } + $variables['@username'] = '' . theme('username', array('account' => $account)) . ''; + $output[] = array( + '#prefix' => "\n
", + '#type' => 'item', + 'lead-in' => array('#markup' => t("@username has the following access", $variables) . ' '), + 'items' => $account_items, + '#suffix' => "\n
\n", + ); + } + } + } + + return $output; + } + + /** + * Builds and returns the by-user information. + * + * @return array|null + */ + public static function buildByUserInfo() { + global $user; + + $output = array(); + return $output; + + // Show which users can access this node. + $menu_item = menu_get_item(); + $map = $menu_item['original_map']; + if ($map[0] != 'node' || !isset($map[1]) || !is_numeric($map[1]) || isset($map[2])) { + // Ignore anything but node/%. + return NULL; + } + + if (isset($menu_item['map'][1]) && is_object($node = $menu_item['map'][1])) { + // We have the node. + } + elseif (is_numeric($menu_item['original_map'][1])) { + $node = node_load($menu_item['original_map'][1]); + } + if (isset($node)) { + $nid = $node->id(); + $langcode = $node->langcode->value; + $language = language_load($langcode); + $node_type = node_type_load($node->bundle()); + $headers = array(t('username'), ' $language->name, '@Node_type' => $node_type->name)) . '">' . t('create') . '', t('view'), t('update'), t('delete')); + $rows = array(); + // Determine whether to use Ajax or pre-populate the tables. + if ($ajax = \Drupal::config('devel_node_access.settings')->get('user_ajax')) { + $output['#attached']['library'][] = 'devel_node_access/node_access'; + } + // Find all users. The following operations are very inefficient, so we + // limit the number of users returned. It would be better to make a + // pager query, or at least make the number of users configurable. If + // anyone is up for that please submit a patch. + $query = db_select('users', 'u') + ->fields('u', array('uid')) + ->orderBy('u.access', 'DESC') + ->range(0, 9); + $uids = $query->execute()->fetchCol(); + array_unshift($uids, 0); + $accounts = user_load_multiple($uids); + foreach ($accounts as $account) { + $username = theme('username', array('account' => $account)); + $uid = $account->id(); + if ($uid == $user->id()) { + $username = '' . $username . ''; + } + $rows[] = array( + $username, + array( + 'id' => 'create-' . $nid . '-' . $uid, + 'class' => 'dna-permission', + 'data' => $ajax ? NULL : theme('dna_permission', array('permission' => self::explainAccess('create', $node, $account, $langcode))), + ), + array( + 'id' => 'view-' . $nid . '-' . $uid, + 'class' => 'dna-permission', + 'data' => $ajax ? NULL : theme('dna_permission', array('permission' => self::explainAccess('view', $node, $account, $langcode))), + ), + array( + 'id' => 'update-' . $nid . '-' . $uid, + 'class' => 'dna-permission', + 'data' => $ajax ? NULL : theme('dna_permission', array('permission' => self::explainAccess('update', $node, $account, $langcode))), + ), + array( + 'id' => 'delete-' . $nid . '-' . $uid, + 'class' => 'dna-permission', + 'data' => $ajax ? NULL : theme('dna_permission', array('permission' => self::explainAccess('delete', $node, $account, $langcode))), + ), + ); + } + if (count($rows)) { + $output['title'] = array( + '#prefix' => '

', + '#markup' => t('Access permissions by user for the %langname language', array('%langname' => $language->name)), + '#postfix' => '

', + ); + $output[] = array( + '#theme' => 'table', + '#header' => $headers, + '#rows' => $rows, + '#attributes' => array('style' => 'text-align: left'), + ); + $output[] = array( + '#theme' => 'form_element', + '#description' => t('(This table lists the most-recently active users. Hover your mouse over each result for more details.)'), + ); + } + } + return $output; + } + + /** + * Helper function to mimic \Drupal::moduleHandler()->invokeAll() and include + * the name of the responding module(s). + * + * @param $hook + * The name of the hook. + * + * @return array + * An array of results. + * In the case of scalar results, the array is keyed by the name of the + * modules that returned the result (rather than by numeric index), and + * in the case of array results, a '#module' key is added. + */ + private static function simulate_module_invoke_all($hook) { + $args = func_get_args(); + // Remove $hook from the arguments. + array_shift($args); + $return = array(); + foreach (\Drupal::moduleHandler()->getImplementations($hook) as $module) { + $function = $module . '_' . $hook; + $result = call_user_func_array($function, $args); + if (isset($result)) { + if (is_array($result)) { + foreach ($result as $key => $value) { + // Add name of module that returned the value. + $result[$key]['#module'] = $module; + } + } + else { + // Build array with result keyed by $module. + $result = array($module => $result); + } + $return = \Drupal\Component\Utility\NestedArray::mergeDeep($return, $result); + } + } + return $return; + } + + /** + * Helper function to mimic hook_node_access_records_alter() and trace what + * each module does with it. + * + * @param array $records + * An indexed array of NA records, augmented by the '#module' key, + * as created by simulate_module_invoke_all('node_access_records'). + * This array is updated by the hook_node_access_records_alter() + * implementations. + * @param $node + * The node that the NA records belong to. + * + * @return array + * A tree representation of the NA records in $records including their + * history: + * $data[$realm][$gid] + * ['original'] - NA record before processing + * ['current'] - NA record after processing (if still present) + * ['changes'][]['op'] - change message (add/change/delete by $module) + * ['record'] - NA record after change (unless deleted) + */ + private static function simulate_node_access_records_alter(&$records, $node) { + //dpm($records, 'simulate_node_access_records_alter(): records IN'); + $hook = 'node_access_records_alter'; + + // Build the initial tree (and check for duplicates). + $data = self::build_node_access_records_data($records, $node, 'hook_node_access_records()'); + + // Simulate drupal_alter('node_access_records', $records, $node). + foreach (\Drupal::moduleHandler()->getImplementations($hook) as $module) { + // Call hook_node_access_records_alter() for one module at a time + // and analyze. + $function = $module . '_' . $hook; + $function($records, $node); + + foreach ($records as $i => $record) { + if (empty($data[$record['realm']][$record['gid']]['current'])) { + // It's an added record. + $data[$record['realm']][$record['gid']]['current'] = $record; + $data[$record['realm']][$record['gid']]['current']['#module'] = $module; + $data[$record['realm']][$record['gid']]['changes'][] = array( + 'op' => 'added by ' . $module, + 'record' => $record, + ); + $records[$i]['#module'] = $module; + } + else { + // It's an existing record, check for changes. + $view = $update = $delete = FALSE; + foreach (array('view', 'update', 'delete') as $op) { + $$op = $record["grant_$op"] - $data[$record['realm']][$record['gid']]['current']["grant_$op"]; + } + $old_priority = isset($record['priority']) ? $record['priority'] : 0; + $new_priority = isset($data[$record['realm']][$record['gid']]['current']['priority']) ? $data[$record['realm']][$record['gid']]['current']['priority'] : 0; + if ($view || $update || $delete || $old_priority != $new_priority) { + // It was changed. + $data[$record['realm']][$record['gid']]['current'] = $record; + $data[$record['realm']][$record['gid']]['current']['#module'] = $module; + $data[$record['realm']][$record['gid']]['changes'][] = array( + 'op' => 'altered by ' . $module, + 'record' => $record, + ); + $records[$i]['#module'] = $module; + } + } + $data[$record['realm']][$record['gid']]['found'] = TRUE; + } + + // Check for newly introduced duplicates. + self::build_node_access_records_data($records, $node, 'hook_node_access_records_alter()'); + + // Look for records that have disappeared. + foreach ($data as $realm => $data2) { + foreach ($data2 as $gid => $data3) { + if (empty($data[$realm][$gid]['found']) && isset($data[$realm][$gid]['current'])) { + unset($data[$realm][$gid]['current']); + $data[$realm][$gid]['changes'][] = array('op' => 'removed by ' . $module); + } + else { + unset($data[$realm][$gid]['found']); + } + } + } + } + //dpm($data, 'simulate_node_access_records_alter() returns'); + //dpm($records, 'simulate_node_access_records_alter(): records OUT'); + return $data; + } + + /** + * Helper function to build an associative array of node access records and + * their history. If there are duplicate records, display an error message. + * + * @param $records + * An indexed array of node access records, augmented by the '#module' key, + * as created by simulate_module_invoke_all('node_access_records'). + * @param $node + * The node that the NA records belong to. + * @param $function + * The name of the hook that produced the records array, in case we need to + * display an error message. + * + * @return array + * See _devel_node_access_nar_alter() for the description of the result. + */ + private static function build_node_access_records_data($records, $node, $function) { + $data = array(); + $duplicates = array(); + foreach ($records as $record) { + if (empty($data[$record['realm']][$record['gid']])) { + $data[$record['realm']][$record['gid']] = array('original' => $record, 'current' => $record, 'changes' => array()); + } + else { + if (empty($duplicates[$record['realm']][$record['gid']])) { + $duplicates[$record['realm']][$record['gid']][] = $data[$record['realm']][$record['gid']]['original']; + } + $duplicates[$record['realm']][$record['gid']][] = $record; + } + } + if (!empty($duplicates)) { + // Generate an error message. + $msg = t('Devel Node Access has detected duplicate records returned from %function:', array('%function' => $function)); + $msg .= '
    '; + foreach ($duplicates as $realm => $data_by_realm) { + foreach ($data_by_realm as $gid => $data_by_realm_gid) { + $msg .= '
    • '; + foreach ($data_by_realm_gid as $record) { + $msg .= "
    • $node->id()/$realm/$gid/" . ($record['grant_view'] ? 1 : 0) . ($record['grant_update'] ? 1 : 0) . ($record['grant_delete'] ? 1 : 0) . ' by ' . $record['#module'] . '
    • '; + } + $msg .= '
  • '; + } + } + $msg .= '
'; + drupal_set_message($msg, 'error', FALSE); + } + return $data; + } + + /** + * Helper function to mimic hook_node_grants_alter() and trace what + * each module does with it. + * + * @param array $grants + * An indexed array of grant records, augmented by the '#module' key, + * as created by simulate_module_invoke_all('node_grants'). + * This array is updated by the hook_node_grants_alter() + * implementations. + * @param $account + * The user account requesting access to content. + * @param $op + * The operation being performed, 'view', 'update' or 'delete'. + * + * @return array + * A tree representation of the grant records in $grants including their + * history: + * $data[$realm][$gid] + * ['cur'] - TRUE or FALSE whether the gid is present or not + * ['ori'][] - array of module names that contributed this grant (if any) + * ['chg'][] - array of changes, such as + * - 'added' if module name is a prefix if the $realm, + * - 'added by module' otherwise, or + * - 'removed by module' + */ + private static function simulate_node_grants_alter(&$grants, $account, $op) { + //dpm($grants, "simulate_node_grants_alter($account->name, $op): grants IN"); + $hook = 'node_grants_alter'; + + // Build the initial structure. + $data = array(); + foreach ($grants as $realm => $gids) { + foreach ($gids as $i => $gid) { + if ($i !== '#module') { + $data[$realm][$gid]['cur'] = TRUE; + $data[$realm][$gid]['ori'][] = $gids['#module']; + } + } + unset($grants[$realm]['#module']); + } + + // Simulate drupal_alter('node_grants', $grants, $account, $op). + foreach (\Drupal::moduleHandler()->getImplementations($hook) as $module) { + // Call hook_node_grants_alter() for one module at a time and analyze. + $function = $module . '_' . $hook; + $function($grants, $account, $op); + + // Check for new gids. + foreach ($grants as $realm => $gids) { + foreach ($gids as $i => $gid) { + if (empty($data[$realm][$gid]['cur'])) { + $data[$realm][$gid]['cur'] = TRUE; + $data[$realm][$gid]['chg'][] = 'added by ' . $module; + } + } + } + + // Check for removed gids. + foreach ($data as $realm => $gids) { + foreach ($gids as $gid => $history) { + if ($history['cur'] && array_search($gid, $grants[$realm]) === FALSE) { + $data[$realm][$gid]['cur'] = FALSE; + $data[$realm][$gid]['chg'][] = 'removed by ' . $module; + } + } + } + } + + //dpm($data, "simulate_node_grants_alter($account->name, $op) returns"); + //dpm($grants, "simulate_node_grants_alter($account->name, $op): grants OUT"); + return $data; + } + + /** + * Helper function to create a list of grants returned by hook_node_grants(). + */ + private static function get_grant_list($grants_data) { + //dpm($grants_data, "get_grant_list() IN:"); + $grants_data = array_merge(array('all' => array(0 => array('cur' => TRUE, 'ori' => array('all')))), $grants_data); + $items = array(); + if (count($grants_data)) { + foreach ($grants_data as $realm => $gids) { + ksort($gids); + $gs = array(); + foreach ($gids as $gid => $history) { + if ($history['cur']) { + if (isset($history['ori'])) { + $g = $gid; // Original grant, still active. + } + else { + $g = '' . $gid . ''; // New grant, still active. + } + } + else { + $g = '' . $gid . ''; // Deleted grant. + } + + $ghs = array(); + if (isset($history['ori']) && strpos($realm, $history['ori'][0]) !== 0) { + $realm = '(' . $history['ori'][0] . '::) ' . $realm; + } + if (isset($history['chg'])) { + foreach ($history['chg'] as $h) { + $ghs[] = $h; + } + } + if (!empty($ghs)) { + $g .= ' (' . implode(', ', $ghs) . ')'; + } + $gs[] = $g; + } + $items[] = $realm . ': ' . implode(', ', $gs); + } + if (!empty($items)) { + return theme('item_list', array('items' => $items, 'type' => 'ul')); + } + } + return ''; + } + + /** + * Helper function to return a sanitized node title. + */ + private static function get_node_title(Node $node) { + if (isset($node)) { + $nid = $node->id(); + if ($node_title = $node->getTitle()) { + return $node_title; + } + elseif ($nid) { + return $nid; + } + } + return '—'; + } + + /** + * Helper function to apply common formatting to a debug-mode table row. + */ + private static function format_row($row, $may_unpack = TRUE) { + if ($may_unpack && isset($row['data'])) { + $row['data'] = self::format_row($row['data'], FALSE); + $row['class'][] = 'even'; + return $row; + } + if (count($row) == 1) { + if (is_scalar($row['data'])) { + $row['data'] = array('data' => $row['data']); + } + $row['data']['colspan'] = 9; + } + else { + $row = array_values($row); + foreach (array(0, 1, 4) as $j) { // node, prio, gid + if (is_scalar($row[$j])) { + $row[$j] = array('data' => $row[$j]); + } + dpm($j); + dpm($row); +// $row[$j]['style'][] = 'text-align: right;'; + } + } + return $row; + } + + + /** + * Helper function that mimics node.module's node_access() function. + * + * Unfortunately, this needs to be updated manually whenever node.module + * changes! + * + * @param string $op + * Operation to check. + * @param NodeInterface $node + * Node to check. + * @param AccountInterface $account + * (optional) The user object for the user whose access is being checked. If + * omitted, the current user is used. Defaults to NULL. + * @param string $langcode + * (optional) The language code for which access is being checked. If + * omitted, the default language is used. Defaults to NULL. + * + * @return array + * An array suitable for theming with theme_dna_permission(). + */ + public static function explainAccess($op, NodeInterface $node, AccountInterface $account = NULL, $langcode = NULL) { + $user = Drupal::currentUser(); + + if (!$node) { + return array( + FALSE, + '???', + t('No node passed to node_access(); this should never happen!'), + ); + } + if (!in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) { + return array( + FALSE, + t('@NO: invalid $op', array('@NO' => t('NO'))), + t("'@op' is an invalid operation!", array('@op' => $op)), + ); + } + + if ($op == 'create' && is_object($node)) { + $node = $node->bundle(); + } + + if (!empty($account)) { + // To try to get the most authentic result we impersonate the given user! + // This may reveal bugs in other modules, leading to contradictory + // results. + /* @var \Drupal\Core\Session\AccountSwitcherInterface $account_switcher */ + $account_switcher = Drupal::service('account_switcher'); + $account_switcher->switchTo($account); + $result = DnaBlock::explainAccess($op, $node, NULL, $langcode); + $account_switcher->switchBack(); + $access_handler = Drupal::entityTypeManager()->getAccessControlHandler('node'); + $second_opinion = $access_handler->access($node, $op, $account); + if ($second_opinion != $result[0]) { + $result[1] .= '*'; + } + return $result; + } + + if (empty($langcode)) { + $langcode = (is_object($node) && $node->id()) ? $node->language()->getId() : ''; + } + + $variables = array( + '@NO' => t('NO'), + '@YES' => t('YES'), + '@bypass_node_access' => t('bypass node access'), + '@access_content' => t('access content'), + ); + + if (Drupal::currentUser()->hasPermission('bypass node access')) { + return array( + TRUE, + t('@YES: bypass node access', $variables), + t("@YES: This user has the '@bypass_node_access' permission and may do everything with nodes.", $variables), + ); + } + + if (!Drupal::currentUser()->hasPermission('access content')) { + return array( + FALSE, + t('@NO: access content', $variables), + t("@NO: This user does not have the '@access_content' permission and is denied doing anything with content.", $variables), + ); + } + + foreach (Drupal::moduleHandler()->getImplementations('node_access') as $module) { + $function = $module . '_node_access'; + if (function_exists($function)) { + $result = $function($node, $op, $user, $langcode); + if ($module == 'node') { + $module = 'node (permissions)'; + } + if (isset($result)) { + /* TODO + if ($result === NODE_ACCESS_DENY) { + $denied_by[] = $module; + } + elseif ($result === NODE_ACCESS_ALLOW) { + $allowed_by[] = $module; + } + */ + $access[] = $result; + } + } + } + $variables += array( + '@deniers' => (empty($denied_by) ? NULL : implode(', ', $denied_by)), + '@allowers' => (empty($allowed_by) ? NULL : implode(', ', $allowed_by)), + ); + if (!empty($denied_by)) { + $variables += array( + '%module' => $denied_by[0] . (count($denied_by) > 1 ? '+' : ''), + ); + return [ + FALSE, + t('@NO: by %module', $variables), + empty($allowed_by) + ? t("@NO: hook_node_access() of the following module(s) denies this: @deniers.", $variables) + : t("@NO: hook_node_access() of the following module(s) denies this: @deniers – even though the following module(s) would allow it: @allowers.", $variables), + ]; + } + if (!empty($allowed_by)) { + $variables += array( + '%module' => $allowed_by[0] . (count($allowed_by) > 1 ? '+' : ''), + '@view_own_unpublished_content' => t('view own unpublished content'), + ); + return array( + TRUE, + t('@YES: by %module', $variables), + t("@YES: hook_node_access() of the following module(s) allows this: @allowers.", $variables), + ); + } + + // TODO if ($op == 'view' && !$node->get('status', $langcode) && \Drupal::currentUser()->hasPermission('view own unpublished content') && $user->uid == $node->get('uid', $langcode) && $user->uid != 0) { + if ($op == 'view' && !$node->isPublished() && Drupal::currentUser()->hasPermission('view own unpublished content') && $user->id() == $node->getRevisionAuthor()->id() && $user->id() != 0) { + return array( + TRUE, + t('@YES: view own unpublished content', $variables), + t("@YES: The node is unpublished, but the user has the '@view_own_unpublished_content' permission.", $variables), + ); + } + + if ($op != 'create' && $node->id()) { + $access_handler = Drupal::entityTypeManager()->getAccessControlHandler('node'); + // TODO if (node_access($op, $node, $user, $langcode)) { // delegate this part + if ($access_handler->access($node, $op, $user)) { + // Delegate this part. + $variables['@node_access_table'] = '{node_access}'; + return array( + TRUE, + t('@YES: @node_access_table', $variables), + t('@YES: Node access allows this based on one or more records in the @node_access_table table (see the other DNA block!).', $variables), + ); + } + } + + return array( + FALSE, + t('@NO: no reason', $variables), + t("@NO: None of the checks resulted in allowing this, so it's denied.", $variables) + . ($op == 'create' ? ' ' . t('This is most likely due to a withheld permission.') : ''), + ); + } + + /** + * Collects the IDs of the visible nodes on the current page. + * + * @param int|null $nid + * A node ID to save. + * + * @return array + * The array of saved node IDs. + */ + public static function visibleNodes($nid = NULL) { + static $nids = array(); + if (isset($nid)) { + $nids[$nid] = $nid; + } + elseif (empty($nids)) { + ///** @var NodeInterface $node */ + //$node = NULL; + if ($node = \Drupal::routeMatch()->getParameter('node')) { + $nid = $node->id(); + $nids[$nid] = $nid; + } + } + return $nids; + } + + +} diff --git a/sites/all/modules/contrib/dev/devel/drush/devel.drush.inc b/sites/all/modules/contrib/dev/devel/drush/devel.drush.inc new file mode 100644 index 000000000..b38cf9aef --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/drush/devel.drush.inc @@ -0,0 +1,274 @@ + dt('Uninstall, and Install a list of projects.'), + 'drush dependencies' => array('pm'), + 'arguments' => array( + 'projects' => dt('A space-separated list of project names.'), + ), + 'allow-additional-options' => array('pm-uninstall', 'pm-enable'), + 'required-arguments' => 1, + 'aliases' => array('dre'), + ); + $items['fn-hook'] = array( + 'description' => 'List implementations of a given hook and explore the source of the selected one.', + 'arguments' => array( + 'hook' => 'The name of the hook to explore (e.g. "menu" for hook_menu()).' + ), + 'examples' => array( + 'fn-hook cron' => 'List implementations of hook_cron().', + ), + 'allow-additional-options' => array('fn-view'), + 'required-arguments' => 1, + 'aliases' => array('fnh', 'hook'), + ); + $items['fn-event'] = array( + 'description' => 'List implementations of a given event and explore source of specified one.', + 'arguments' => array( + 'event' => 'The name of the event to explore. If omitted, a list of events is shown.' + ), + 'examples' => array( + 'fn-event' => 'Pick a Kernel event, then pick an implementation, and then view its source code.', + 'fn-event kernel.terminate' => 'Pick a terminate subscribers and view its source code.', + ), + 'allow-additional-options' => array('fn-view'), + 'aliases' => array('fne', 'event'), + ); + $items['fn-view'] = array( + 'description' => 'Show the source of specified function or method.', + 'arguments' => array( + 'function' => 'The name of the function or method to view.', + ), + 'options' => array( + 'pipe' => 'Output just the filename of the function or method', + 'format' => 'Specify how the filename should be printed. Available placeholders are @startline, @endline and @file', + ), + 'examples' => array( + 'fn-view drupal_set_breadcrumb' => 'View the source code for function "drupal_set_breadcrumb"', + 'vi `drush --pipe fn-view user_access --format=\'+@startline @file\'`' => 'Edit the file that contains the function "user_access"', + 'fn-view NodeController::load' => 'View the source code for method load in the class NodeController' + ), + 'aliases' => array('fnv'), + 'required-arguments' => 1, + ); + $items['devel-token'] = array( + 'description' => dt('List available tokens'), + 'aliases' => array('token'), + //@todo support --format option for json, csv, etc. + ); + + $items['devel-container-services'] = array( + 'description' => 'Get a list of available container services.', + 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL, + 'core' => array('8+'), + 'aliases' => array('dcs'), + 'options' => array( + 'format' => 'Format to output. Allowed values are: json, export, html.', + ), + 'arguments' => array( + 'prefix' => 'A prefix to filter the service list by.', + ), + 'examples' => array( + 'drush container-services' => 'Gets a list of all available container services', + 'drush container-services plugin.manager' => 'Get all services containing "plugin.manager"', + ), + 'outputformat' => array( + 'default' => 'list', + 'pipe-format' => 'export', + ), + ); + + $items['devel-generate-uuid'] = array( + 'description' => 'Generate a UUID.', + 'core' => array('8+'), + 'examples' => array( + "drush devel-generate-uuid" => "Outputs a Universally Unique IDentifier.", + ), + 'aliases' => array('uuid'), + 'outputformat' => array( + 'default' => 'string', + ), + ); + + return $items; +} + +/** + * A command callback. This is faster than 3 separate bootstraps. + */ +function drush_devel_reinstall() { + $projects = func_get_args(); + + $args = array_merge(array('pm-uninstall'), $projects); + call_user_func_array('drush_invoke', $args); + + $args = array_merge(array('pm-enable'), $projects); + call_user_func_array('drush_invoke', $args); +} + +/** + * Command handler. Show hook implementations. + */ +function drush_devel_fn_hook($hook) { + // Get implementations in the .install files as well. + include_once './core/includes/install.inc'; + drupal_load_updates(); + + if ($hook_implementations = \Drupal::moduleHandler()->getImplementations($hook)) { + if ($choice = drush_choice(array_combine($hook_implementations, $hook_implementations), 'Enter the number of the hook implementation you wish to view.')) { + return drush_devel_fn_view($choice . "_$hook"); + } + } + else { + drush_log(dt('No implementations.'), 'ok'); + } +} + +/** + * Command handler. Show hook implementations. + */ +function drush_devel_fn_event($event = NULL) { + $dispatcher = Drupal::service('event_dispatcher'); + if (empty($event)) { + $events = array('kernel.controller', 'kernel.exception', 'kernel.request', 'kernel.response', 'kernel.terminate', 'kernel.view'); + $events = array_combine($events, $events); + if (!$event = drush_choice($events, 'Enter the event you wish to explore.')) { + return drush_user_abort(); + } + } + if ($implementations = $dispatcher->getListeners($event)) { + foreach ($implementations as $implementation) { + $callable = get_class($implementation[0]) . '::' . $implementation[1]; + $choices[$callable] = $callable; + } + if ($choice = drush_choice($choices, 'Enter the number of the implementation you wish to view.')) { + return drush_devel_fn_view($choice); + } + } + else { + drush_log(dt('No implementations.'), 'ok'); + } +} +/** + * Command handler. Show source code of specified function or method. + */ +function drush_devel_fn_view($function_name) { + // Get implementations in the .install files as well. + include_once './core/includes/install.inc'; + drupal_load_updates(); + + if (strpos($function_name, '::') === FALSE) { + if (!function_exists($function_name)) { + return drush_set_error(dt('Function not found')); + } + $reflect = new ReflectionFunction($function_name); + } + else { + list($class, $method) = explode('::', $function_name); + if (!method_exists($class, $method)) { + return drush_set_error(dt('Method not found')); + } + $reflect = new ReflectionMethod($class, $method); + } + $func_info = array('@file' => $reflect->getFileName(), '@startline' => $reflect->getStartLine(), '@endline' => $reflect->getEndLine()); + $format = drush_get_option('format', '@file'); + drush_print_pipe(dt($format, $func_info)); + drush_print(dt("// file: @file, lines @startline-@endline", $func_info)); + + _drush_devel_print_function($reflect->getFileName(), $reflect->getStartLine(), $reflect->getEndLine()); +} + +/** + * Command callback. List available tokens. + */ +function drush_devel_token() { + $rows[] = array(dt('Group'), dt('Token'), dt('Name')); + $all = \Drupal::token()->getInfo(); + foreach ($all['tokens'] as $group => $tokens) { + foreach ($tokens as $key => $token) { + $rows[] = array($group, $key, $token['name']); + } + } + drush_print_table($rows, TRUE); +} + +/** + * Command callback. Outputs a UUID. + * + * @return string + * A freshly generated UUID. + */ +function drush_devel_generate_uuid() { + $uuid = new Php(); + return $uuid->generate(); +} + +/** + * Print the specified function, including any + * doxygen-style comments that come before it. + */ +function _drush_devel_print_function($file, $start_line, $end_line) { + $line_num = 0; + $doxygen = NULL; + $fp = fopen( $file, 'r' ); + + while (!feof($fp) && ($line_num < ($start_line - 1))) { + $line = fgets($fp); + ++$line_num; + + if (substr($line,0,3) == '/**') { + $doxygen = $line; + } + elseif (isset($doxygen)) { + $doxygen .= $line; + if ($line_num + 1 == $start_line) { + drush_print(rtrim($doxygen)); + } + if (strstr($line, '*/') !== FALSE) { + $doxygen = NULL; + } + } + } + while (!feof($fp) && ($line_num < $end_line)) { + $line = fgets($fp); + ++$line_num; + drush_print(rtrim($line)); + } +} + +/** + * Command callback to list available container services. + */ +function drush_devel_container_services($prefix = NULL) { + $container = Drupal::getContainer(); + + if (empty($container)) { + return drush_set_error(dt('No container was found.')); + } + + // Get a list of all available service IDs. + $services = $container->getServiceIds(); + + // If there is a prefix, try to find matches. + if (isset($prefix)) { + $services = preg_grep("/$prefix/", $services); + } + + if (empty($services)) { + return drush_log(dt('No container services found.'), 'ok'); + } + + sort($services); + return $services; +} diff --git a/sites/all/modules/contrib/dev/devel/drush/develDrushTest.php b/sites/all/modules/contrib/dev/devel/drush/develDrushTest.php new file mode 100644 index 000000000..54a5cf936 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/drush/develDrushTest.php @@ -0,0 +1,38 @@ +setUpDrupal(1, TRUE, '8'); + + // Symlink this module into the Site Under test so it can be enabled. + $target = dirname(__DIR__); + \symlink($target, $this->webroot() . '/modules/devel'); + $options = array( + 'root' => $this->webroot(), + 'uri' => key($sites), + ); + $this->drush('pm-enable', array('devel'), $options + array('skip' => NULL, 'yes' => NULL)); + + $this->drush('fn-view', array('drush_main'), $options); + $output = $this->getOutput(); + $this->assertContains('@return', $output, 'Output contain @return Doxygen.'); + $this->assertContains('function drush_main() {', $output, 'Output contains function drush_main() declaration'); + + // $this->drush('fn-hook', array('cron'), $options); + // $output = $this->getOutputAsList(); + // $expected = array('dblog', 'file', 'field', 'system', 'update'); + // $this->assertSame($expected, $output); + } + } + +} diff --git a/sites/all/modules/contrib/dev/devel/drush/phpstorm.drush.inc b/sites/all/modules/contrib/dev/devel/drush/phpstorm.drush.inc new file mode 100644 index 000000000..416bdd63d --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/drush/phpstorm.drush.inc @@ -0,0 +1,105 @@ + 'Save the PhpStorm Metadata file to Drupal root.', + 'core' => array('8+'), + 'aliases' => array('phpm'), + 'category' => 'devel', + ); + + return $items; +} + +/** + * Implements hook_drush_help_alter(). + */ +function phpstorm_drush_help_alter(&$command) { + if ($command['command'] == 'cache-rebuild') { + $command['options']['storm'] = 'Write a new PHPstorm metadata file to Drupal root.'; + } +} + +/* + * Implements drush_hook_post_COMMAND(). + */ +function drush_phpstorm_post_cache_rebuild() { + if (drush_get_option('storm')) { + drush_invoke_process('@self', 'phpstorm-metadata'); + } +} + +/** + * Generate PhpStorm Metadata file. + * + * @see http://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata + */ +function drush_phpstorm_metadata() { + $container = \Drupal::getContainer(); + + $reflectedClass = new ReflectionClass($container); + + $map = array(); + + // Map for all services of the container. + // @see \Symfony\Component\DependencyInjection\Container::getServiceIds(). + foreach ($reflectedClass->getMethods() as $method) { + if (preg_match('/^get(.+)Service$/', $method->name, $match)) { + $id = strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), strtr($match[1], '_', '.'))); + $service = \Drupal::service($id); + if (is_object($service)) { + $map["\\Drupal::service('')"][$id] = '\\' . get_class($service); + } + } + } + + // Entity Manager - getStorage + foreach (\Drupal::entityTypeManager()->getDefinitions() as $type => $definition) { + $class = Drupal::entityTypeManager()->getStorage($type); + $map["\\Drupal::entityManager()->getStorage('')"][$type] = '\\' . get_class($class); + $map["\\Drupal::entityTypeManager()->getStorage('')"][$type] = '\\' . get_class($class); + } + + $content = _drush_phpstorm_metadata_phpstorm_metadata_template($map); + file_put_contents(DRUPAL_ROOT . '/.phpstorm.meta.php', $content); +} + +function _drush_phpstorm_metadata_phpstorm_metadata_template($data) { + $file = ' $map) { + $file .= "\n"; + $file .= " {$method} => [\n"; + + foreach ($map as $argument => $class) { + $file .= " '{$argument}' instanceof {$class},\n"; + } + + $file .= " ],"; + $file .= "\n"; + } + + $file .= ' + ]; + } + '; + + return $file; +} diff --git a/sites/all/modules/contrib/dev/devel/kint/README.txt b/sites/all/modules/contrib/dev/devel/kint/README.txt new file mode 100644 index 000000000..75b68b921 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/README.txt @@ -0,0 +1,37 @@ +WHAT IS IT? +----------- +Kint for PHP is a tool designed to present your debugging data in the absolutely +best way possible. + +In other words, it's var_dump() and debug_backtrace() on steroids. Easy to use, +but powerful and customizable. An essential addition to your development +toolbox. + +USAGE +----- +This module allows to use these aliases: + kint($data1, $data2, $data3, ...); + ksm($data1, $data2, $data3, ...) + kint_trace(); + +But to get the most out of Kint, you will want to use directly the Kint class: + kint_require(); + Kint::dump($data); + +Learn more about Kint: http://raveren.github.io/kint/ + + +The Kint class function dd() will not work as expected, because this alias +is already defined in devel.module for other purposes. + +CONTACTS +-------- +Module author: + Alexander Danilenko + danilenko.dn@gmail.com + https://drupal.org/user/1072104 + +Kint author: + Rokas Šleinius a.k.a. Raveren + raveren@gmail.com + https://github.com/raveren \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint.info.yml b/sites/all/modules/contrib/dev/devel/kint/kint.info.yml new file mode 100644 index 000000000..22c7cbf35 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint.info.yml @@ -0,0 +1,13 @@ +name: 'Devel Kint' +type: module +description: 'Wrapper for Kint debugging tool' +package: Development +# core: 8.x +tags: + - developer + +# Information added by Drupal.org packaging script on 2016-09-03 +version: '8.x-1.0-alpha1+23-dev' +core: '8.x' +project: 'devel' +datestamp: 1472897643 diff --git a/sites/all/modules/contrib/dev/devel/kint/kint.module b/sites/all/modules/contrib/dev/devel/kint/kint.module new file mode 100644 index 000000000..c8bef22e9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint.module @@ -0,0 +1,65 @@ +hasPermission('access kint')) { + $args = func_get_args(); + if (PHP_SAPI === 'cli') { + s($args); + } + else { + \Kint::dump($args); + } + } +} + +/** + * Alias of Kint::trace(). + * + * Prints backtrace in Kint debug tool. + */ +function kint_trace() { + kint_require(); + if (\Drupal::currentUser()->hasPermission('access kint')) { + call_user_func_array(array('Kint', 'trace'), array()); + } +} + +/** + * Alias of Kint::kintLite(). + * + * Prints with lightweight formatting, using whitespace for formatting instead + * of HTML. + */ +function kint_lite() { + if (\Drupal::currentUser()->hasPermission('access kint')) { + $args = func_get_args(); + call_user_func_array('kintLite', $args); + } +} + +/** + * Prints passed argument(s) to the 'message' area of the page. + */ +function ksm() { + kint_require(); + if (\Drupal::currentUser()->hasPermission('access kint')) { + $args = func_get_args(); + $msg = @Kint::dump($args); + drupal_set_message(Markup::create($msg)); + } +} + +/** + * Load the Kint class. + */ +function kint_require() { + return require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'kint') . '/kint/Kint.class.php'; +} diff --git a/sites/all/modules/contrib/dev/devel/kint/kint.permissions.yml b/sites/all/modules/contrib/dev/devel/kint/kint.permissions.yml new file mode 100644 index 000000000..fe28b8593 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint.permissions.yml @@ -0,0 +1,4 @@ +access kint: + description: 'View developer debug output.' + title: 'Access kint information' + restrict access: TRUE diff --git a/sites/all/modules/contrib/dev/devel/kint/kint.services.yml b/sites/all/modules/contrib/dev/devel/kint/kint.services.yml new file mode 100644 index 000000000..5ae75ae68 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint.services.yml @@ -0,0 +1,5 @@ +services: + kint.twig.kint_extension: + class: Drupal\kint\Twig\KintExtension + tags: + - { name: twig.extension } diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/.gitignore b/sites/all/modules/contrib/dev/devel/kint/kint/.gitignore new file mode 100644 index 000000000..21a628423 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/.gitignore @@ -0,0 +1,3 @@ + +/config.php +/.idea \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/Kint.class.php b/sites/all/modules/contrib/dev/devel/kint/kint/Kint.class.php new file mode 100644 index 000000000..c15e3f419 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/Kint.class.php @@ -0,0 +1,836 @@ += 0 ); + +require KINT_DIR . 'config.default.php'; +require KINT_DIR . 'inc/kintVariableData.class.php'; +require KINT_DIR . 'inc/kintParser.class.php'; +require KINT_DIR . 'inc/kintObject.class.php'; +require KINT_DIR . 'decorators/rich.php'; +require KINT_DIR . 'decorators/plain.php'; + +if ( is_readable( KINT_DIR . 'config.php' ) ) { + require KINT_DIR . 'config.php'; +} + +# init settings +if ( !empty( $GLOBALS['_kint_settings'] ) ) { + Kint::enabled( $GLOBALS['_kint_settings']['enabled'] ); + + foreach ( $GLOBALS['_kint_settings'] as $key => $val ) { + property_exists( 'Kint', $key ) and Kint::$$key = $val; + } + + unset( $GLOBALS['_kint_settings'], $key, $val ); +} + +class Kint +{ + // these are all public and 1:1 config array keys so you can switch them easily + private static $_enabledMode; # stores mode and active statuses + + public static $returnOutput; + public static $fileLinkFormat; + public static $displayCalledFrom; + public static $charEncodings; + public static $maxStrLength; + public static $appRootDirs; + public static $maxLevels; + public static $theme; + public static $expandedByDefault; + + public static $cliDetection; + public static $cliColors; + + const MODE_RICH = 'r'; + const MODE_WHITESPACE = 'w'; + const MODE_CLI = 'c'; + const MODE_PLAIN = 'p'; + + + public static $aliases = array( + 'methods' => array( + array( 'kint', 'dump' ), + array( 'kint', 'trace' ), + ), + 'functions' => array( + 'd', + 'dd', + 'ddd', + 's', + 'sd', + ) + ); + + private static $_firstRun = true; + + /** + * Enables or disables Kint, can globally enforce the rendering mode. If called without parameters, returns the + * current mode. + * + * @param mixed $forceMode + * null or void - return current mode + * false - disable (no output) + * true - enable and detect cli automatically + * Kint::MODE_* - enable and force selected mode disregarding detection and function + * shorthand (s()/d()), note that you can still override this + * with the "~" modifier + * + * @return mixed previously set value if a new one is passed + */ + public static function enabled( $forceMode = null ) + { + # act both as a setter... + if ( isset( $forceMode ) ) { + $before = self::$_enabledMode; + self::$_enabledMode = $forceMode; + + return $before; + } + + # ...and a getter + return self::$_enabledMode; + } + + /** + * Prints a debug backtrace, same as Kint::dump(1) + * + * @param array $trace [OPTIONAL] you can pass your own trace, otherwise, `debug_backtrace` will be called + * + * @return mixed + */ + public static function trace( $trace = null ) + { + if ( !self::enabled() ) return ''; + + return self::dump( isset( $trace ) ? $trace : debug_backtrace( true ) ); + } + + + /** + * Dump information about variables, accepts any number of parameters, supports modifiers: + * + * clean up any output before kint and place the dump at the top of page: + * - Kint::dump() + * ***** + * expand all nodes on display: + * ! Kint::dump() + * ***** + * dump variables disregarding their depth: + * + Kint::dump() + * ***** + * return output instead of displaying it: + * @ Kint::dump() + * ***** + * force output as plain text + * ~ Kint::dump() + * + * Modifiers are supported by all dump wrapper functions, including Kint::trace(). Space is optional. + * + * + * You can also use the following shorthand to display debug_backtrace(): + * Kint::dump( 1 ); + * + * Passing the result from debug_backtrace() to kint::dump() as a single parameter will display it as trace too: + * $trace = debug_backtrace( true ); + * Kint::dump( $trace ); + * Or simply: + * Kint::dump( debug_backtrace() ); + * + * + * @param mixed $data + * + * @return void|string + */ + public static function dump( $data = null ) + { + if ( !self::enabled() ) return ''; + + list( $names, $modifiers, $callee, $previousCaller, $miniTrace ) = self::_getCalleeInfo( + defined( 'DEBUG_BACKTRACE_IGNORE_ARGS' ) + ? debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ) + : debug_backtrace() + ); + $modeOldValue = self::enabled(); + $firstRunOldValue = self::$_firstRun; + + # process modifiers: @, +, !, ~ and - + if ( strpos( $modifiers, '-' ) !== false ) { + self::$_firstRun = true; + while ( ob_get_level() ) { + ob_end_clean(); + } + } + if ( strpos( $modifiers, '!' ) !== false ) { + $expandedByDefaultOldValue = self::$expandedByDefault; + self::$expandedByDefault = true; + } + if ( strpos( $modifiers, '+' ) !== false ) { + $maxLevelsOldValue = self::$maxLevels; + self::$maxLevels = false; + } + if ( strpos( $modifiers, '@' ) !== false ) { + $returnOldValue = self::$returnOutput; + self::$returnOutput = true; + self::$_firstRun = true; + } + if ( strpos( $modifiers, '~' ) !== false ) { + self::enabled( self::MODE_WHITESPACE ); + } + + # set mode for current run + $mode = self::enabled(); + if ( $mode === true ) { + $mode = PHP_SAPI === 'cli' + ? self::MODE_CLI + : self::MODE_RICH; + } + self::enabled( $mode ); + + $decorator = self::enabled() === self::MODE_RICH + ? 'Kint_Decorators_Rich' + : 'Kint_Decorators_Plain'; + + $output = ''; + if ( self::$_firstRun ) { + $output .= call_user_func( array( $decorator, 'init' ) ); + } + + + $trace = false; + if ( $names === array( null ) && func_num_args() === 1 && $data === 1 ) { # Kint::dump(1) shorthand + $trace = KINT_PHP53 ? debug_backtrace( true ) : debug_backtrace(); + } elseif ( func_num_args() === 1 && is_array( $data ) ) { + $trace = $data; # test if the single parameter is result of debug_backtrace() + } + $trace and $trace = self::_parseTrace( $trace ); + + + $output .= call_user_func( array( $decorator, 'wrapStart' ) ); + if ( $trace ) { + $output .= call_user_func( array( $decorator, 'decorateTrace' ), $trace ); + } else { + $data = func_num_args() === 0 + ? array( "[[no arguments passed]]" ) + : func_get_args(); + + foreach ( $data as $k => $argument ) { + kintParser::reset(); + # when the dump arguments take long to generate output, user might have changed the file and + # Kint might not parse the arguments correctly, so check if names are set and while the + # displayed names might be wrong, at least don't throw an error + $output .= call_user_func( + array( $decorator, 'decorate' ), + kintParser::factory( $argument, isset( $names[ $k ] ) ? $names[ $k ] : '' ) + ); + } + } + + $output .= call_user_func( array( $decorator, 'wrapEnd' ), $callee, $miniTrace, $previousCaller ); + + self::enabled( $modeOldValue ); + + self::$_firstRun = false; + if ( strpos( $modifiers, '~' ) !== false ) { + self::$_firstRun = $firstRunOldValue; + } else { + self::enabled( $modeOldValue ); + } + if ( strpos( $modifiers, '!' ) !== false ) { + self::$expandedByDefault = $expandedByDefaultOldValue; + } + if ( strpos( $modifiers, '+' ) !== false ) { + self::$maxLevels = $maxLevelsOldValue; + } + if ( strpos( $modifiers, '@' ) !== false ) { + self::$returnOutput = $returnOldValue; + self::$_firstRun = $firstRunOldValue; + return $output; + } + + if ( self::$returnOutput ) return $output; + + echo $output; + return ''; + } + + + /** + * generic path display callback, can be configured in the settings; purpose is to show relevant path info and hide + * as much of the path as possible. + * + * @param string $file + * + * @return string + */ + public static function shortenPath( $file ) + { + $file = str_replace( '\\', '/', $file ); + $shortenedName = $file; + $replaced = false; + if ( is_array( self::$appRootDirs ) ) foreach ( self::$appRootDirs as $path => $replaceString ) { + if ( empty( $path ) ) continue; + + $path = str_replace( '\\', '/', $path ); + + if ( strpos( $file, $path ) === 0 ) { + $shortenedName = $replaceString . substr( $file, strlen( $path ) ); + $replaced = true; + break; + } + } + + # fallback to find common path with Kint dir + if ( !$replaced ) { + $pathParts = explode( '/', str_replace( '\\', '/', KINT_DIR ) ); + $fileParts = explode( '/', $file ); + $i = 0; + foreach ( $fileParts as $i => $filePart ) { + if ( !isset( $pathParts[ $i ] ) || $pathParts[ $i ] !== $filePart ) break; + } + + $shortenedName = ( $i ? '.../' : '' ) . implode( '/', array_slice( $fileParts, $i ) ); + } + + return $shortenedName; + } + + public static function getIdeLink( $file, $line ) + { + return str_replace( array( '%f', '%l' ), array( $file, $line ), self::$fileLinkFormat ); + } + + /** + * trace helper, shows the place in code inline + * + * @param string $file full path to file + * @param int $lineNumber the line to display + * @param int $padding surrounding lines to show besides the main one + * + * @return bool|string + */ + private static function _showSource( $file, $lineNumber, $padding = 7 ) + { + if ( !$file OR !is_readable( $file ) ) { + # continuing will cause errors + return false; + } + + # open the file and set the line position + $file = fopen( $file, 'r' ); + $line = 0; + + # Set the reading range + $range = array( + 'start' => $lineNumber - $padding, + 'end' => $lineNumber + $padding + ); + + # set the zero-padding amount for line numbers + $format = '% ' . strlen( $range['end'] ) . 'd'; + + $source = ''; + while ( ( $row = fgets( $file ) ) !== false ) { + # increment the line number + if ( ++$line > $range['end'] ) { + break; + } + + if ( $line >= $range['start'] ) { + # make the row safe for output + $row = htmlspecialchars( $row, ENT_NOQUOTES, 'UTF-8' ); + + # trim whitespace and sanitize the row + $row = '' . sprintf( $format, $line ) . ' ' . $row; + + if ( $line === $lineNumber ) { + # apply highlighting to this row + $row = '
' . $row . '
'; + } else { + $row = '
' . $row . '
'; + } + + # add to the captured source + $source .= $row; + } + } + + # close the file + fclose( $file ); + + return $source; + } + + + /** + * returns parameter names that the function was passed, as well as any predefined symbols before function + * call (modifiers) + * + * @param array $trace + * + * @return array( $parameters, $modifier, $callee, $previousCaller ) + */ + private static function _getCalleeInfo( $trace ) + { + $previousCaller = array(); + $miniTrace = array(); + $prevStep = array(); + + # go from back of trace to find first occurrence of call to Kint or its wrappers + while ( $step = array_pop( $trace ) ) { + + if ( self::_stepIsInternal( $step ) ) { + $previousCaller = $prevStep; + break; + } elseif ( isset( $step['file'], $step['line'] ) ) { + unset( $step['object'], $step['args'] ); + array_unshift( $miniTrace, $step ); + } + + $prevStep = $step; + } + $callee = $step; + + if ( !isset( $callee['file'] ) || !is_readable( $callee['file'] ) ) return false; + + + # open the file and read it up to the position where the function call expression ended + $file = fopen( $callee['file'], 'r' ); + $line = 0; + $source = ''; + while ( ( $row = fgets( $file ) ) !== false ) { + if ( ++$line > $callee['line'] ) break; + $source .= $row; + } + fclose( $file ); + $source = self::_removeAllButCode( $source ); + + + if ( empty( $callee['class'] ) ) { + $codePattern = $callee['function']; + } else { + if ( $callee['type'] === '::' ) { + $codePattern = $callee['class'] . "\x07*" . $callee['type'] . "\x07*" . $callee['function'];; + } else /*if ( $callee['type'] === '->' )*/ { + $codePattern = ".*\x07*" . $callee['type'] . "\x07*" . $callee['function'];; + } + } + + // todo if more than one call in one line - not possible to determine variable names + // todo does not recognize string concat + # get the position of the last call to the function + preg_match_all( " + [ + # beginning of statement + [\x07{(] + + # search for modifiers (group 1) + ([-+!@~]*)? + + # spaces + \x07* + + # check if output is assigned to a variable (group 2) todo: does not detect concat + ( + \\$[a-z0-9_]+ # variable + \x07*\\.?=\x07* # assignment + )? + + # possibly a namespace symbol + \\\\? + + # spaces again + \x07* + + # main call to Kint + {$codePattern} + + # spaces everywhere + \x07* + + # find the character where kint's opening bracket resides (group 3) + (\\() + + ]ix", + $source, + $matches, + PREG_OFFSET_CAPTURE + ); + + $modifiers = end( $matches[1] ); + $assignment = end( $matches[2] ); + $bracket = end( $matches[3] ); + + $modifiers = $modifiers[0]; + if ( $assignment[1] !== -1 ) { + $modifiers .= '@'; + } + + $paramsString = preg_replace( "[\x07+]", ' ', substr( $source, $bracket[1] + 1 ) ); + # we now have a string like this: + # ); + + # remove everything in brackets and quotes, we don't need nested statements nor literal strings which would + # only complicate separating individual arguments + $c = strlen( $paramsString ); + $inString = $escaped = $openedBracket = $closingBracket = false; + $i = 0; + $inBrackets = 0; + $openedBrackets = array(); + + while ( $i < $c ) { + $letter = $paramsString[ $i ]; + + if ( !$inString ) { + if ( $letter === '\'' || $letter === '"' ) { + $inString = $letter; + } elseif ( $letter === '(' || $letter === '[' ) { + $inBrackets++; + $openedBrackets[] = $openedBracket = $letter; + $closingBracket = $openedBracket === '(' ? ')' : ']'; + } elseif ( $inBrackets && $letter === $closingBracket ) { + $inBrackets--; + array_pop( $openedBrackets ); + $openedBracket = end( $openedBrackets ); + $closingBracket = $openedBracket === '(' ? ')' : ']'; + } elseif ( !$inBrackets && $letter === ')' ) { + $paramsString = substr( $paramsString, 0, $i ); + break; + } + } elseif ( $letter === $inString && !$escaped ) { + $inString = false; + } + + # replace whatever was inside quotes or brackets with untypeable characters, we don't + # need that info. We'll later replace the whole string with '...' + if ( $inBrackets > 0 ) { + if ( $inBrackets > 1 || $letter !== $openedBracket ) { + $paramsString[ $i ] = "\x07"; + } + } + if ( $inString ) { + if ( $letter !== $inString || $escaped ) { + $paramsString[ $i ] = "\x07"; + } + } + + $escaped = !$escaped && ( $letter === '\\' ); + $i++; + } + + # by now we have an un-nested arguments list, lets make it to an array for processing further + $arguments = explode( ',', preg_replace( "[\x07+]", '...', $paramsString ) ); + + # test each argument whether it was passed literary or was it an expression or a variable name + $parameters = array(); + $blacklist = array( 'null', 'true', 'false', 'array(...)', 'array()', '"..."', '[...]', 'b"..."', ); + foreach ( $arguments as $argument ) { + $argument = trim( $argument ); + + if ( is_numeric( $argument ) + || in_array( str_replace( "'", '"', strtolower( $argument ) ), $blacklist, true ) + ) { + $parameters[] = null; + } else { + $parameters[] = $argument; + } + } + + return array( $parameters, $modifiers, $callee, $previousCaller, $miniTrace ); + } + + /** + * removes comments and zaps whitespace & true, T_INLINE_HTML => true, T_DOC_COMMENT => true + ); + $whiteSpaceTokens = array( + T_WHITESPACE => true, T_CLOSE_TAG => true, + T_OPEN_TAG => true, T_OPEN_TAG_WITH_ECHO => true, + ); + + $cleanedSource = ''; + foreach ( token_get_all( $source ) as $token ) { + if ( is_array( $token ) ) { + if ( isset( $commentTokens[ $token[0] ] ) ) continue; + + if ( isset( $whiteSpaceTokens[ $token[0] ] ) ) { + $token = "\x07"; + } else { + $token = $token[1]; + } + } elseif ( $token === ';' ) { + $token = "\x07"; + } + + $cleanedSource .= $token; + } + return $cleanedSource; + } + + /** + * returns whether current trace step belongs to Kint or its wrappers + * + * @param $step + * + * @return array + */ + private static function _stepIsInternal( $step ) + { + if ( isset( $step['class'] ) ) { + foreach ( self::$aliases['methods'] as $alias ) { + if ( $alias[0] === strtolower( $step['class'] ) && $alias[1] === strtolower( $step['function'] ) ) { + return true; + } + } + return false; + } else { + return in_array( strtolower( $step['function'] ), self::$aliases['functions'], true ); + } + } + + private static function _parseTrace( array $data ) + { + $trace = array(); + $traceFields = array( 'file', 'line', 'args', 'class' ); + $fileFound = false; # file element must exist in one of the steps + + # validate whether a trace was indeed passed + while ( $step = array_pop( $data ) ) { + if ( !is_array( $step ) || !isset( $step['function'] ) ) return false; + if ( !$fileFound && isset( $step['file'] ) && file_exists( $step['file'] ) ) { + $fileFound = true; + } + + $valid = false; + foreach ( $traceFields as $element ) { + if ( isset( $step[ $element ] ) ) { + $valid = true; + break; + } + } + if ( !$valid ) return false; + + if ( self::_stepIsInternal( $step ) ) { + $step = array( + 'file' => $step['file'], + 'line' => $step['line'], + 'function' => '', + ); + array_unshift( $trace, $step ); + break; + } + if ( $step['function'] !== 'spl_autoload_call' ) { # meaningless + array_unshift( $trace, $step ); + } + } + if ( !$fileFound ) return false; + + $output = array(); + foreach ( $trace as $step ) { + if ( isset( $step['file'] ) ) { + $file = $step['file']; + + if ( isset( $step['line'] ) ) { + $line = $step['line']; + # include the source of this step + if ( self::enabled() === self::MODE_RICH ) { + $source = self::_showSource( $file, $line ); + } + } + } + + $function = $step['function']; + + if ( in_array( $function, array( 'include', 'include_once', 'require', 'require_once' ) ) ) { + if ( empty( $step['args'] ) ) { + # no arguments + $args = array(); + } else { + # sanitize the included file path + $args = array( 'file' => self::shortenPath( $step['args'][0] ) ); + } + } elseif ( isset( $step['args'] ) ) { + if ( empty( $step['class'] ) && !function_exists( $function ) ) { + # introspection on closures or language constructs in a stack trace is impossible before PHP 5.3 + $params = null; + } else { + try { + if ( isset( $step['class'] ) ) { + if ( method_exists( $step['class'], $function ) ) { + $reflection = new ReflectionMethod( $step['class'], $function ); + } else if ( isset( $step['type'] ) && $step['type'] == '::' ) { + $reflection = new ReflectionMethod( $step['class'], '__callStatic' ); + } else { + $reflection = new ReflectionMethod( $step['class'], '__call' ); + } + } else { + $reflection = new ReflectionFunction( $function ); + } + + # get the function parameters + $params = $reflection->getParameters(); + } catch ( Exception $e ) { # avoid various PHP version incompatibilities + $params = null; + } + } + + $args = array(); + foreach ( $step['args'] as $i => $arg ) { + if ( isset( $params[ $i ] ) ) { + # assign the argument by the parameter name + $args[ $params[ $i ]->name ] = $arg; + } else { + # assign the argument by number + $args[ '#' . ( $i + 1 ) ] = $arg; + } + } + } + + if ( isset( $step['class'] ) ) { + # Class->method() or Class::method() + $function = $step['class'] . $step['type'] . $function; + } + + // todo it's possible to parse the object name out from the source! + $output[] = array( + 'function' => $function, + 'args' => isset( $args ) ? $args : null, + 'file' => isset( $file ) ? $file : null, + 'line' => isset( $line ) ? $line : null, + 'source' => isset( $source ) ? $source : null, + 'object' => isset( $step['object'] ) ? $step['object'] : null, + ); + + unset( $function, $args, $file, $line, $source ); + } + + return $output; + } +} + + +if ( !function_exists( 'd' ) ) { + /** + * Alias of Kint::dump() + * + * @return string + */ + function d() + { + if ( !Kint::enabled() ) return ''; + $_ = func_get_args(); + return call_user_func_array( array( 'Kint', 'dump' ), $_ ); + } +} + +if ( !function_exists( 'dd' ) ) { + /** + * Alias of Kint::dump() + * [!!!] IMPORTANT: execution will halt after call to this function + * + * @return string + * @deprecated + */ + function dd() + { + if ( !Kint::enabled() ) return ''; + + echo "
Kint: dd() is being deprecated, please use ddd() instead
\n"; + $_ = func_get_args(); + call_user_func_array( array( 'Kint', 'dump' ), $_ ); + die; + } +} + +if ( !function_exists( 'ddd' ) ) { + /** + * Alias of Kint::dump() + * [!!!] IMPORTANT: execution will halt after call to this function + * + * @return string + */ + function ddd() + { + if ( !Kint::enabled() ) return ''; + $_ = func_get_args(); + call_user_func_array( array( 'Kint', 'dump' ), $_ ); + die; + } +} + +if ( !function_exists( 's' ) ) { + /** + * Alias of Kint::dump(), however the output is in plain htmlescaped text and some minor visibility enhancements + * added. If run in CLI mode, output is pure whitespace. + * + * To force rendering mode without autodetecting anything: + * + * Kint::enabled( Kint::MODE_PLAIN ); + * Kint::dump( $variable ); + * + * [!!!] IMPORTANT: execution will halt after call to this function + * + * @return string + */ + function s() + { + $enabled = Kint::enabled(); + if ( !$enabled ) return ''; + + if ( $enabled === Kint::MODE_WHITESPACE ) { # if already in whitespace, don't elevate to plain + $restoreMode = Kint::MODE_WHITESPACE; + } else { + $restoreMode = Kint::enabled( # remove cli colors in cli mode; remove rich interface in HTML mode + PHP_SAPI === 'cli' ? Kint::MODE_WHITESPACE : Kint::MODE_PLAIN + ); + } + + $params = func_get_args(); + $dump = call_user_func_array( array( 'Kint', 'dump' ), $params ); + Kint::enabled( $restoreMode ); + return $dump; + } +} + +if ( !function_exists( 'sd' ) ) { + /** + * @see s() + * + * [!!!] IMPORTANT: execution will halt after call to this function + * + * @return string + */ + function sd() + { + $enabled = Kint::enabled(); + if ( !$enabled ) return ''; + + if ( $enabled !== Kint::MODE_WHITESPACE ) { + Kint::enabled( + PHP_SAPI === 'cli' ? Kint::MODE_WHITESPACE : Kint::MODE_PLAIN + ); + } + + $params = func_get_args(); + call_user_func_array( array( 'Kint', 'dump' ), $params ); + die; + } +} diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/LICENCE b/sites/all/modules/contrib/dev/devel/kint/kint/LICENCE new file mode 100644 index 000000000..936fe1f16 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/LICENCE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Rokas Šleinius (raveren@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/README.md b/sites/all/modules/contrib/dev/devel/kint/kint/README.md new file mode 100644 index 000000000..74edcba11 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/README.md @@ -0,0 +1,141 @@ +# Kint - debugging helper for PHP developers + +[![Total Downloads](https://poser.pugx.org/raveren/kint/downloads.png)](https://packagist.org/packages/raveren/kint) + +> **New version** v1.0.0 is released with more than two years of active development - changes are too numerous to list, but there's CLI output and literally hundreds of improvements and additions. + +![Screenshot](http://raveren.github.com/kint/img/preview.png) + +## What am I looking at? + +At first glance Kint is just a pretty replacement for **[var_dump()](http://php.net/manual/en/function.var-dump.php)**, **[print_r()](http://php.net/manual/en/function.print-r.php)** and **[debug_backtrace()](http://php.net/manual/en/function.debug-backtrace.php)**. + +However, it's much, *much* more than that. Even the excellent `xdebug` var_dump improvements don't come close - you will eventually wonder how you developed without it. + +Just to list some of the most useful features: + + * The **variable name and place in code** where Kint was called from is displayed; + * You can **disable all Kint output easily and on the fly** - so you can even debug live systems without anyone knowing (even though you know you shouldn't be doing that!:). + * **CLI is detected** and formatted for automatically (but everything can be overridden on the fly) - if your setup supports it, the output is colored too: + ![Kint CLI output](http://i.imgur.com/6B9MCLw.png) + * **Debug backtraces** are finally fully readable, actually informative and a pleasure to the eye. + * Kint has been **in active development for more than six years** and is shipped with [Drupal 8](https://www.drupal.org/) by default as part of its devel suite. You can trust it not being abandoned or getting left behind in features. + * Variable content is **displayed in the most informative way** - and you *never, ever* miss anything! Kint guarantees you see every piece of physically available information about everything you are dumping*; + * in some cases, the content is truncated where it would otherwise be too large to view anyway - but the user is always made aware of that; + * Some variable content types have an alternative display - for example you will be able see `JSON` in its raw form - but also as an associative array: + ![Kint displays data intelligently](http://i.imgur.com/9P57Ror.png) + There are more than ten custom variable type displays inbuilt and more are added periodically. + + +## Installation and Usage + +One of the main goals of Kint is to be **zero setup**. + +[Download the archive](https://github.com/raveren/kint/releases/download/1.0.2/kint.zip) and simply +```php +');`) - so even if you accidentally leave a dump in production, no one will know. + * `sd()` and `ddd()` are shorthands for `s();die;` and `d();die;` respectively. + * **Important:** The older shorthand `dd()` is deprecated due to compatibility issues and will eventually be removed. Use the analogous `ddd()` instead. + * When looking at Kint output, press D on the keyboard and you will be able to traverse the tree with arrows and tab keys - and expand/collapse nodes with space or enter. + * Double clicking the `[+]` sign in the output will expand/collapse ALL nodes; triple clicking big blocks of text will select it all. + * Clicking the tiny arrows on the right of the output open it in a separate window where you can keep it for comparison. + * To catch output from Kint just assign it to a variablebeta +```php +$o = Kint::dump($GLOBALS); +// yes, the assignment is automatically detected, and $o +// now holds whatever was going to be printed otherwise. + +// it also supports modifiers (read on) for the variable: +~$o = Kint::dump($GLOBALS); // this output will be in whitespace +``` + * There are a couple of real-time modifiers you can use: + * `~d($var)` this call will output in plain text format. + * `+d($var)` will disregard depth level limits and output everything (careful, this can hang your browser on huge objects) + * `!d($var)` will show expanded rich output. + * `-d($var)` will attempt to `ob_clean` the previous output so if you're dumping something inside a HTML page, you will still see Kint output. + You can combine modifiers too: `~+d($var)` + * To force a specific dump output type just pass it to the `Kint::enabled()` method. Available options are: `Kint::MODE_RICH` (default), `Kint::MODE_PLAIN`, `Kint::MODE_WHITESPACE` and `Kint::MODE_CLI`: +```php +Kint::enabled(Kint::MODE_WHITESPACE); +$kintOutput = Kint::dump($GLOBALS); +// now $kintOutput can be written to a text log file and +// be perfectly readable from there +``` + * To change display theme, use `Kint::$theme = '';` where available options are: `'original'` (default), `'solarized'`, `'solarized-dark'` and `'aante-light'`. Here's an (outdated) preview: + ![Kint themes](http://raveren.github.io/kint/img/theme-preview.png) + * Kint also includes a naïve profiler you may find handy. It's for determining relatively which code blocks take longer than others: +```php +Kint::dump( microtime() ); // just pass microtime() +sleep( 1 ); +Kint::dump( microtime(), 'after sleep(1)' ); +sleep( 2 ); +ddd( microtime(), 'final call, after sleep(2)' ); +``` + ![Kint profiling feature](http://i.imgur.com/tmHUMW4.png) +---- + +[Visit the project page](http://raveren.github.com/kint/) for documentation, configuration, and more advanced usage examples. + +### Author + +**Rokas Šleinius** (Raveren) + +### License + +Licensed under the MIT License diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/composer.json b/sites/all/modules/contrib/dev/devel/kint/kint/composer.json new file mode 100644 index 000000000..78eb1b654 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/composer.json @@ -0,0 +1,29 @@ +{ + "name": "raveren/kint", + "description": "Kint - debugging helper for PHP developers", + "keywords": ["kint", "php", "debug"], + "type": "library", + "homepage": "https://github.com/raveren/kint", + "license": "MIT", + "authors": [ + { + "name": "Rokas Šleinius", + "homepage": "https://github.com/raveren" + }, + { + "name": "Contributors", + "homepage": "https://github.com/raveren/kint/contributors" + } + ], + "require": { + "php": ">=5.1.0" + }, + "autoload": { + "files": ["Kint.class.php"] + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/config.default.php b/sites/all/modules/contrib/dev/devel/kint/kint/config.default.php new file mode 100644 index 000000000..72f0a6933 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/config.default.php @@ -0,0 +1,94 @@ + '<ROOT>' ) + * + * [!] EXAMPLE (for Kohana framework): + * + * $_kintSettings['appRootDirs'] = array( + * APPPATH => 'APPPATH', // make sure the constants are already defined at the time of including this config file + * SYSPATH => 'SYSPATH', + * MODPATH => 'MODPATH', + * DOCROOT => 'DOCROOT', + * ); + * + * [!] EXAMPLE #2 (for a semi-universal approach) + * + * $_kintSettings['appRootDirs'] = array( + * realpath( __DIR__ . '/../../..' ) => 'ROOT', // go up as many levels as needed in the realpath() param + * ); + * + * $_kintSettings['fileLinkFormat'] = 'http://localhost:8091/?message=%f:%l'; + * + */ +$_kintSettings['appRootDirs'] = isset( $_SERVER['DOCUMENT_ROOT'] ) + ? array( $_SERVER['DOCUMENT_ROOT'] => '<ROOT>' ) + : array(); + + +/** @var int max length of string before it is truncated and displayed separately in full. Zero or false to disable */ +$_kintSettings['maxStrLength'] = 80; + +/** @var array possible alternative char encodings in order of probability, eg. array('windows-1251') */ +$_kintSettings['charEncodings'] = array( + 'UTF-8', + 'Windows-1252', # Western; includes iso-8859-1, replace this with windows-1251 if you have Russian code + 'euc-jp', # Japanese + + # all other charsets cannot be differentiated by PHP and/or are not supported by mb_* functions, + # I need a better means of detecting the codeset, no idea how though :( + + // 'iso-8859-13', # Baltic + // 'windows-1251', # Cyrillic + // 'windows-1250', # Central European + // 'shift_jis', # Japanese + // 'iso-2022-jp', # Japanese +); + + +/** @var int max array/object levels to go deep, if zero no limits are applied */ +$_kintSettings['maxLevels'] = 7; + + +/** @var string name of theme for rich view */ +$_kintSettings['theme'] = 'original'; + + +/** @var bool enable detection when Kint is command line. Formats output with whitespace only; does not HTML-escape it */ +$_kintSettings['cliDetection'] = true; + +/** @var bool in addition to above setting, enable detection when Kint is run in *UNIX* command line. + * Attempts to add coloring, but if seen as plain text, the color information is visible as gibberish + */ +$_kintSettings['cliColors'] = true; + + +unset( $_kintSettings ); \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/decorators/plain.php b/sites/all/modules/contrib/dev/devel/kint/kint/decorators/plain.php new file mode 100644 index 000000000..22ebd2ae9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/decorators/plain.php @@ -0,0 +1,335 @@ + '1', 'dark' => '2', + 'italic' => '3', 'underline' => '4', + 'blink' => '5', 'reverse' => '7', + 'concealed' => '8', 'default' => '39', + + # colors + 'black' => '30', 'red' => '31', + 'green' => '32', 'yellow' => '33', + 'blue' => '34', 'magenta' => '35', + 'cyan' => '36', 'light_gray' => '37', + 'dark_gray' => '90', 'light_red' => '91', + 'light_green' => '92', 'light_yellow' => '93', + 'light_blue' => '94', 'light_magenta' => '95', + 'light_cyan' => '96', 'white' => '97', + + # backgrounds + 'bg_default' => '49', 'bg_black' => '40', + 'bg_red' => '41', 'bg_green' => '42', + 'bg_yellow' => '43', 'bg_blue' => '44', + 'bg_magenta' => '45', 'bg_cyan' => '46', + 'bg_light_gray' => '47', 'bg_dark_gray' => '100', + 'bg_light_red' => '101', 'bg_light_green' => '102', + 'bg_light_yellow' => '103', 'bg_light_blue' => '104', + 'bg_light_magenta' => '105', 'bg_light_cyan' => '106', + 'bg_white' => '107', + ); + private static $_utfSymbols = array( + '┌', '═', '┐', + '│', + '└', '─', '┘', + ); + private static $_winShellSymbols = array( + "\xda", "\xdc", "\xbf", + "\xb3", + "\xc0", "\xc4", "\xd9", + ); + private static $_htmlSymbols = array( + "┌", "▄", "┐", + "│", + "└", "─", "┘", + ); + + public static function decorate( kintVariableData $kintVar, $level = 0 ) + { + $output = ''; + if ( $level === 0 ) { + $name = $kintVar->name ? $kintVar->name : 'literal'; + $kintVar->name = null; + + $output .= self::_title( $name ); + } + + + $space = str_repeat( $s = ' ', $level ); + $output .= $space . self::_drawHeader( $kintVar ); + + + if ( $kintVar->extendedValue !== null ) { + $output .= ' ' . ( $kintVar->type === 'array' ? '[' : '(' ) . PHP_EOL; + + + if ( is_array( $kintVar->extendedValue ) ) { + foreach ( $kintVar->extendedValue as $v ) { + $output .= self::decorate( $v, $level + 1 ); + } + } elseif ( is_string( $kintVar->extendedValue ) ) { + $output .= $space . $s . $kintVar->extendedValue . PHP_EOL; # depth too great or similar + } else { + $output .= self::decorate( $kintVar->extendedValue, $level + 1 ); //it's kintVariableData + } + $output .= $space . ( $kintVar->type === 'array' ? ']' : ')' ) . PHP_EOL; + } else { + $output .= PHP_EOL; + } + + return $output; + } + + public static function decorateTrace( $traceData ) + { + $output = self::_title( 'TRACE' ); + $lastStep = count( $traceData ); + foreach ( $traceData as $stepNo => $step ) { + $title = str_pad( ++$stepNo . ': ', 4, ' ' ); + + $title .= self::_colorize( + ( isset( $step['file'] ) ? self::_buildCalleeString( $step ) : 'PHP internal call' ), + 'title' + ); + + if ( !empty( $step['function'] ) ) { + $title .= ' ' . $step['function']; + if ( isset( $step['args'] ) ) { + $title .= '('; + if ( empty( $step['args'] ) ) { + $title .= ')'; + } else { + } + $title .= PHP_EOL; + } + } + + $output .= $title; + + if ( !empty( $step['args'] ) ) { + $appendDollar = $step['function'] === '{closure}' ? '' : '$'; + + $i = 0; + foreach ( $step['args'] as $name => $argument ) { + $argument = kintParser::factory( + $argument, + $name ? $appendDollar . $name : '#' . ++$i + ); + $argument->operator = $name ? ' =' : ':'; + $maxLevels = Kint::$maxLevels; + if ( $maxLevels ) { + Kint::$maxLevels = $maxLevels + 2; + } + $output .= self::decorate( $argument, 2 ); + if ( $maxLevels ) { + Kint::$maxLevels = $maxLevels; + } + } + $output .= ' )' . PHP_EOL; + } + + if ( !empty( $step['object'] ) ) { + $output .= self::_colorize( + ' ' . self::_char( '─', 27 ) . ' Callee object ' . self::_char( '─', 34 ), + 'title' + ); + + $maxLevels = Kint::$maxLevels; + if ( $maxLevels ) { + # in cli the terminal window is filled too quickly to display huge objects + Kint::$maxLevels = Kint::enabled() === Kint::MODE_CLI + ? 1 + : $maxLevels + 1; + } + $output .= self::decorate( kintParser::factory( $step['object'] ), 1 ); + if ( $maxLevels ) { + Kint::$maxLevels = $maxLevels; + } + } + + if ( $stepNo !== $lastStep ) { + $output .= self::_colorize( self::_char( '─', 80 ), 'title' ); + } + } + + return $output; + } + + + private static function _colorize( $text, $type, $nlAfter = true ) + { + $nlAfter = $nlAfter ? PHP_EOL : ''; + + switch ( Kint::enabled() ) { + case Kint::MODE_PLAIN: + if ( !self::$_enableColors ) return $text . $nlAfter; + + switch ( $type ) { + case 'value': + $text = "{$text}"; + break; + case 'type': + $text = "{$text}"; + break; + case 'title': + $text = "{$text}"; + break; + } + + return $text . $nlAfter; + break; + case Kint::MODE_CLI: + if ( !self::$_enableColors ) return $text . $nlAfter; + + $optionsMap = array( + 'title' => "\x1b[36m", # cyan + 'type' => "\x1b[35;1m", # magenta bold + 'value' => "\x1b[32m", # green + ); + + return $optionsMap[ $type ] . $text . "\x1b[0m" . $nlAfter; + break; + case Kint::MODE_WHITESPACE: + default: + return $text . $nlAfter; + break; + } + } + + + private static function _char( $char, $repeat = null ) + { + switch ( Kint::enabled() ) { + case Kint::MODE_PLAIN: + $char = self::$_htmlSymbols[ array_search( $char, self::$_utfSymbols, true ) ]; + break; + case Kint::MODE_CLI: + $inWindowsShell = PHP_SAPI === 'cli' && DIRECTORY_SEPARATOR !== '/'; + if ( $inWindowsShell ) { + $char = self::$_winShellSymbols[ array_search( $char, self::$_utfSymbols, true ) ]; + } + break; + case Kint::MODE_WHITESPACE: + default: + break; + } + + return $repeat ? str_repeat( $char, $repeat ) : $char; + } + + private static function _title( $text ) + { + $escaped = kintParser::escape( $text ); + $lengthDifference = strlen( $escaped ) - strlen( $text ); + return + self::_colorize( + self::_char( '┌' ) . self::_char( '─', 78 ) . self::_char( '┐' ) . PHP_EOL + . self::_char( '│' ), + 'title', + false + ) + + . self::_colorize( str_pad( $escaped, 78 + $lengthDifference, ' ', STR_PAD_BOTH ), 'title', false ) + + . self::_colorize( self::_char( '│' ) . PHP_EOL + . self::_char( '└' ) . self::_char( '─', 78 ) . self::_char( '┘' ), + 'title' + ); + } + + public static function wrapStart() + { + if ( Kint::enabled() === Kint::MODE_PLAIN ) { + return '
';
+		}
+		return '';
+	}
+
+	public static function wrapEnd( $callee, $miniTrace, $prevCaller )
+	{
+		$lastLine = self::_colorize( self::_char( "═", 80 ), 'title' );
+		$lastChar = Kint::enabled() === Kint::MODE_PLAIN ? '
' : ''; + + + if ( !Kint::$displayCalledFrom ) return $lastLine . $lastChar; + + + return $lastLine . self::_colorize( 'Called from ' . self::_buildCalleeString( $callee ), 'title' ) . $lastChar; + } + + + private static function _drawHeader( kintVariableData $kintVar ) + { + $output = ''; + + if ( $kintVar->access ) { + $output .= ' ' . $kintVar->access; + } + + if ( $kintVar->name !== null && $kintVar->name !== '' ) { + $output .= ' ' . kintParser::escape( $kintVar->name ); + } + + if ( $kintVar->operator ) { + $output .= ' ' . $kintVar->operator; + } + + $output .= ' ' . self::_colorize( $kintVar->type, 'type', false ); + + if ( $kintVar->size !== null ) { + $output .= ' (' . $kintVar->size . ')'; + } + + + if ( $kintVar->value !== null && $kintVar->value !== '' ) { + $output .= ' ' . self::_colorize( + $kintVar->value, # escape shell + 'value', + false + ); + } + + return ltrim( $output ); + } + + private static function _buildCalleeString( $callee ) + { + if ( Kint::enabled() === Kint::MODE_CLI ) { // todo win/nix + return "+{$callee['line']} {$callee['file']}"; + } + + $url = Kint::getIdeLink( $callee['file'], $callee['line'] ); + $shortenedName = Kint::shortenPath( $callee['file'] ) . ':' . $callee['line']; + + if ( Kint::enabled() === Kint::MODE_PLAIN ) { + if ( strpos( $url, 'http://' ) === 0 ) { + $calleeInfo = "{$shortenedName}"; + } else { + $calleeInfo = "{$shortenedName}"; + } + } else { + $calleeInfo = $shortenedName; + } + + return $calleeInfo; + } + + public static function init() + { + self::$_enableColors = + Kint::$cliColors + && ( DIRECTORY_SEPARATOR === '/' || getenv( 'ANSICON' ) !== false || getenv( 'ConEmuANSI' ) === 'ON' ); + + return Kint::enabled() === Kint::MODE_PLAIN + ? '' + : ''; + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/decorators/rich.php b/sites/all/modules/contrib/dev/devel/kint/kint/decorators/rich.php new file mode 100644 index 000000000..bb6c97825 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/decorators/rich.php @@ -0,0 +1,319 @@ +'; + + $extendedPresent = $kintVar->extendedValue !== null || $kintVar->_alternatives !== null; + + if ( $extendedPresent ) { + $class = 'kint-parent'; + if ( Kint::$expandedByDefault ) { + $class .= ' kint-show'; + } + $output .= '
'; + } else { + $output .= '
'; + } + + if ( $extendedPresent ) { + $output .= ''; + } + + $output .= self::_drawHeader( $kintVar ) . $kintVar->value . '
'; + + + if ( $extendedPresent ) { + $output .= '
'; + } + + if ( isset( $kintVar->extendedValue ) ) { + + if ( is_array( $kintVar->extendedValue ) ) { + foreach ( $kintVar->extendedValue as $v ) { + $output .= self::decorate( $v ); + } + } elseif ( is_string( $kintVar->extendedValue ) ) { + $output .= '
' . $kintVar->extendedValue . '
'; + } else { + $output .= self::decorate( $kintVar->extendedValue ); //it's kint's container + } + + } elseif ( isset( $kintVar->_alternatives ) ) { + $output .= "
    "; + + foreach ( $kintVar->_alternatives as $k => $var ) { + $active = $k === 0 ? ' class="kint-active-tab"' : ''; + $output .= "" . self::_drawHeader( $var, false ) . ''; + } + + $output .= "
    "; + + foreach ( $kintVar->_alternatives as $var ) { + $output .= "
  • "; + + $var = $var->value; + + if ( is_array( $var ) ) { + foreach ( $var as $v ) { + $output .= is_string( $v ) + ? '
    ' . $v . '
    ' + : self::decorate( $v ); + } + } elseif ( is_string( $var ) ) { + $output .= '
    ' . $var . '
    '; + } elseif ( isset( $var ) ) { + throw new Exception( + 'Kint has encountered an error, ' + . 'please paste this report to https://github.com/raveren/kint/issues
    ' + . 'Error encountered at ' . basename( __FILE__ ) . ':' . __LINE__ . '
    ' + . ' variables: ' + . htmlspecialchars( var_export( $kintVar->_alternatives, true ), ENT_QUOTES ) + ); + } + + $output .= "
  • "; + } + + $output .= "
"; + } + if ( $extendedPresent ) { + $output .= '
'; + } + + $output .= ''; + + return $output; + } + + public static function decorateTrace( $traceData ) + { + $output = '
'; + + foreach ( $traceData as $i => $step ) { + $class = 'kint-parent'; + if ( Kint::$expandedByDefault ) { + $class .= ' kint-show'; + } + + $output .= '
' + . '' . ( $i + 1 ) . ' ' + . '' + . ''; + + if ( isset( $step['file'] ) ) { + $output .= self::_ideLink( $step['file'], $step['line'] ); + } else { + $output .= 'PHP internal call'; + } + + $output .= ''; + + $output .= $step['function']; + + if ( isset( $step['args'] ) ) { + $output .= '(' . implode( ', ', array_keys( $step['args'] ) ) . ')'; + } + $output .= '
'; + $firstTab = ' class="kint-active-tab"'; + $output .= '
    '; + + if ( !empty( $step['source'] ) ) { + $output .= "Source"; + $firstTab = ''; + } + + if ( !empty( $step['args'] ) ) { + $output .= "Arguments"; + $firstTab = ''; + } + + if ( !empty( $step['object'] ) ) { + kintParser::reset(); + $calleeDump = kintParser::factory( $step['object'] ); + + $output .= "Callee object [{$calleeDump->type}]"; + } + + + $output .= '
    '; + + + if ( !empty( $step['source'] ) ) { + $output .= "
  • {$step['source']}
  • "; + } + + if ( !empty( $step['args'] ) ) { + $output .= "
  • "; + foreach ( $step['args'] as $k => $arg ) { + kintParser::reset(); + $output .= self::decorate( kintParser::factory( $arg, $k ) ); + } + $output .= "
  • "; + } + if ( !empty( $step['object'] ) ) { + $output .= "
  • " . self::decorate( $calleeDump ) . "
  • "; + } + + $output .= '
'; + } + $output .= '
'; + + return $output; + } + + + /** + * called for each dump, opens the html tag + * + * @param array $callee caller information taken from debug backtrace + * + * @return string + */ + public static function wrapStart() + { + return "
"; + } + + + /** + * closes Kint::_wrapStart() started html tags and displays callee information + * + * @param array $callee caller information taken from debug backtrace + * @param array $miniTrace full path to kint call + * @param array $prevCaller previous caller information taken from debug backtrace + * + * @return string + */ + public static function wrapEnd( $callee, $miniTrace, $prevCaller ) + { + if ( !Kint::$displayCalledFrom ) { + return '
'; + } + + $callingFunction = ''; + $calleeInfo = ''; + $traceDisplay = ''; + if ( isset( $prevCaller['class'] ) ) { + $callingFunction = $prevCaller['class']; + } + if ( isset( $prevCaller['type'] ) ) { + $callingFunction .= $prevCaller['type']; + } + if ( isset( $prevCaller['function'] ) + && !in_array( $prevCaller['function'], array( 'include', 'include_once', 'require', 'require_once' ) ) + ) { + $callingFunction .= $prevCaller['function'] . '()'; + } + $callingFunction and $callingFunction = " [{$callingFunction}]"; + + + if ( isset( $callee['file'] ) ) { + $calleeInfo .= 'Called from ' . self::_ideLink( $callee['file'], $callee['line'] ); + } + + if ( !empty( $miniTrace ) ) { + $traceDisplay = '
    '; + foreach ( $miniTrace as $step ) { + $traceDisplay .= '
  1. ' . self::_ideLink( $step['file'], $step['line'] ); // closing tag not required + if ( isset( $step['function'] ) + && !in_array( $step['function'], array( 'include', 'include_once', 'require', 'require_once' ) ) + ) { + $classString = ' ['; + if ( isset( $step['class'] ) ) { + $classString .= $step['class']; + } + if ( isset( $step['type'] ) ) { + $classString .= $step['type']; + } + $classString .= $step['function'] . '()]'; + $traceDisplay .= $classString; + } + } + $traceDisplay .= '
'; + + $calleeInfo = '' . $calleeInfo; + } + + + return "
" + . ' ' + . "{$calleeInfo}{$callingFunction}{$traceDisplay}" + . "
"; + } + + + private static function _drawHeader( kintVariableData $kintVar, $verbose = true ) + { + $output = ''; + if ( $verbose ) { + if ( $kintVar->access !== null ) { + $output .= "" . $kintVar->access . " "; + } + + if ( $kintVar->name !== null && $kintVar->name !== '' ) { + $output .= "" . kintParser::escape( $kintVar->name ) . " "; + } + + if ( $kintVar->operator !== null ) { + $output .= $kintVar->operator . " "; + } + } + + if ( $kintVar->type !== null ) { + if ( $verbose ) { + $output .= ""; + } + + $output .= $kintVar->type; + + if ( $verbose ) { + $output .= ""; + } else { + $output .= " "; + } + } + + + if ( $kintVar->size !== null ) { + $output .= "(" . $kintVar->size . ") "; + } + + return $output; + } + + private static function _ideLink( $file, $line ) + { + $shortenedPath = Kint::shortenPath( $file ); + if ( !Kint::$fileLinkFormat ) return $shortenedPath . ':' . $line; + + $ideLink = Kint::getIdeLink( $file, $line ); + $class = ( strpos( $ideLink, 'http://' ) === 0 ) ? 'class="kint-ide-link" ' : ''; + return "{$shortenedPath}:{$line}"; + } + + + /** + * produces css and js required for display. May be called multiple times, will only produce output once per + * pageload or until `-` or `@` modifier is used + * + * @return string + */ + public static function init() + { + $baseDir = KINT_DIR . 'view/compiled/'; + + if ( !is_readable( $cssFile = $baseDir . Kint::$theme . '.css' ) ) { + $cssFile = $baseDir . 'original.css'; + } + + return + '' + . '\n"; + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/examples/overview.php b/sites/all/modules/contrib/dev/devel/kint/kint/examples/overview.php new file mode 100644 index 000000000..70f5be919 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/examples/overview.php @@ -0,0 +1,176 @@ +additionalData = $data; } + + /** + * @return \DateTime date object + */ + public function getCreatedDate() { } + + /** + * @param \DateTime $date + */ + public function setCreatedDate( DateTime $date ) { $this->createdDate = $date; } + + /** + * Dummy method that triggers trace + */ + public function ensure() { Kint::trace(); } +} + +class UserManager +{ + private $user; + + /** + * Get user from manager + */ + public function getUser() { return $this->user; } + + /** + * Debug specific user + * + * @param \User $user + */ + public function debugUser( $user ) + { + $this->user = $user; + d( $this->getUser() ); + } + + /** + * Ensure user (triggers ensure() method on \User object that trace) + * + * @void + */ + public function ensureUser() { $this->user->ensure(); } +} + +$user = new User; +$user->setAdditionalData( array( + 'last_login' => new DateTime(), + 'current_unix_timestamp' => time(), + 'random_rgb_color_code' => '#FF9900', + 'impressions' => 60, + 'nickname' => 'Someuser', + ) +); +$user->setCreatedDate( new DateTime( '2013-10-10' ) ); +$userManager = new UserManager(); + +for ( $i = 1; $i < 6; $i++ ) { + $tabularData[] = array( + 'date' => "2013-01-0{$i}", + 'allowed' => $i % 3 == 0, + 'action' => "action {$i}", + 'clicks' => rand( 100, 50000 ), + 'impressions' => rand( 10000, 500000 ), + ); + + if ( $i % 2 == 0 ) { + unset( $tabularData[ $i - 1 ]['clicks'] ); + } +} + +$nestedArray = array(); + +for ( $i = 1; $i < 6; $i++ ) { + $nestedArray["user group {$i}"] = array( + "user {$i}" => array( + 'name' => "Name {$i}", + 'surname' => "Surname {$i}" + ), + + 'data' => array( + 'conversions' => rand( 100, 5000 ), + 'spent' => array( 'currency' => 'EUR', 'amount' => rand( 10000, 500000 ) ) + ), + ); +} +?> + + + Kint PHP debugging tool - overview + + +
+ + +

Kint PHP debugging tool - overview

+
+

Debug variables

+debugUser( $user ); +d( $userManager, $tabularData ); +d( $nestedArray ); +?> +

Trace

+ensureUser(); ?> + + \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/inc/kintObject.class.php b/sites/all/modules/contrib/dev/devel/kint/kint/inc/kintObject.class.php new file mode 100644 index 000000000..63651237f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/inc/kintObject.class.php @@ -0,0 +1,19 @@ + self::$_level, + 'objects' => self::$_objects, + ); + + self::$_level++; + + $varData = new kintVariableData; + $varData->name = $name; + + # first parse the variable based on its type + $varType = gettype( $variable ); + $varType === 'unknown type' and $varType = 'unknown'; # PHP 5.4 inconsistency + $methodName = '_parse_' . $varType; + + # objects can be presented in a different way altogether, INSTEAD, not ALONGSIDE the generic parser + if ( $varType === 'object' ) { + foreach ( self::$_objectParsers as $parserClass ) { + $className = 'Kint_Objects_' . $parserClass; + + /** @var $object KintObject */ + $object = new $className; + if ( ( $alternativeTabs = $object->parse( $variable ) ) !== false ) { + self::$_skipAlternatives = true; + $alternativeDisplay = new kintVariableData; + $alternativeDisplay->type = $object->name; + $alternativeDisplay->value = $object->value; + $alternativeDisplay->name = $name; + + foreach ( $alternativeTabs as $name => $values ) { + $alternative = kintParser::factory( $values ); + $alternative->type = $name; + if ( Kint::enabled() === Kint::MODE_RICH ) { + empty( $alternative->value ) and $alternative->value = $alternative->extendedValue; + $alternativeDisplay->_alternatives[] = $alternative; + } else { + $alternativeDisplay->extendedValue[] = $alternative; + } + } + + self::$_skipAlternatives = false; + self::$_level = $revert['level']; + self::$_objects = $revert['objects']; + return $alternativeDisplay; + } + } + } + + # base type parser returning false means "stop processing further": e.g. recursion + if ( self::$methodName( $variable, $varData ) === false ) { + self::$_level--; + return $varData; + } + + if ( Kint::enabled() === Kint::MODE_RICH && !self::$_skipAlternatives ) { + # if an alternative returns something that can be represented in an alternative way, don't :) + self::$_skipAlternatives = true; + + # now check whether the variable can be represented in a different way + foreach ( self::$_customDataTypes as $parserClass ) { + $className = 'Kint_Parsers_' . $parserClass; + + /** @var $parser kintParser */ + $parser = new $className; + $parser->name = $name; # the parser may overwrite the name value, so set it first + + if ( $parser->_parse( $variable ) !== false ) { + $varData->_alternatives[] = $parser; + } + } + + + # if alternatives exist, push extendedValue to their front and display it as one of alternatives + if ( !empty( $varData->_alternatives ) && isset( $varData->extendedValue ) ) { + $_ = new kintVariableData; + + $_->value = $varData->extendedValue; + $_->type = 'contents'; + $_->size = null; + + array_unshift( $varData->_alternatives, $_ ); + $varData->extendedValue = null; + } + + self::$_skipAlternatives = false; + } + + self::$_level = $revert['level']; + self::$_objects = $revert['objects']; + + if ( strlen( $varData->name ) > 80 ) { + $varData->name = + self::_substr( $varData->name, 0, 37 ) + . '...' + . self::_substr( $varData->name, -38, null ); + } + return $varData; + } + + private static function _checkDepth() + { + return Kint::$maxLevels != 0 && self::$_level >= Kint::$maxLevels; + } + + private static function _isArrayTabular( array $variable ) + { + if ( Kint::enabled() !== Kint::MODE_RICH ) return false; + + $arrayKeys = array(); + $keys = null; + $closeEnough = false; + foreach ( $variable as $row ) { + if ( !is_array( $row ) || empty( $row ) ) return false; + + foreach ( $row as $col ) { + if ( !empty( $col ) && !is_scalar( $col ) ) return false; // todo add tabular "tolerance" + } + + if ( isset( $keys ) && !$closeEnough ) { + # let's just see if the first two rows have same keys, that's faster and has the + # positive side effect of easily spotting missing keys in later rows + if ( $keys !== array_keys( $row ) ) return false; + + $closeEnough = true; + } else { + $keys = array_keys( $row ); + } + + $arrayKeys = array_unique( array_merge( $arrayKeys, $keys ) ); + } + + return $arrayKeys; + } + + private static function _decorateCell( kintVariableData $kintVar ) + { + if ( $kintVar->extendedValue !== null || !empty( $kintVar->_alternatives ) ) { + return '' . Kint_Decorators_Rich::decorate( $kintVar ) . ''; + } + + $output = 'value !== null ) { + $output .= ' title="' . $kintVar->type; + + if ( $kintVar->size !== null ) { + $output .= " (" . $kintVar->size . ")"; + } + + $output .= '">' . $kintVar->value; + } else { + $output .= '>'; + + if ( $kintVar->type !== 'NULL' ) { + $output .= '' . $kintVar->type; + + if ( $kintVar->size !== null ) { + $output .= "(" . $kintVar->size . ")"; + } + + $output .= ''; + } else { + $output .= 'NULL'; + } + } + + + return $output . ''; + } + + + public static function escape( $value, $encoding = null ) + { + if ( empty( $value ) ) return $value; + + if ( Kint::enabled() === Kint::MODE_CLI ) { + $value = str_replace( "\x1b", "\\x1b", $value ); + } + + if ( Kint::enabled() === Kint::MODE_CLI || Kint::enabled() === Kint::MODE_WHITESPACE ) return $value; + + $encoding or $encoding = self::_detectEncoding( $value ); + $value = htmlspecialchars( $value, ENT_NOQUOTES, $encoding === 'ASCII' ? 'UTF-8' : $encoding ); + + + if ( $encoding === 'UTF-8' ) { + // todo we could make the symbols hover-title show the code for the invisible symbol + # when possible force invisible characters to have some sort of display (experimental) + $value = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]/u', '?', $value ); + } + + # this call converts all non-ASCII characters into html chars of format + if ( function_exists( 'mb_encode_numericentity' ) ) { + $value = mb_encode_numericentity( + $value, + array( 0x80, 0xffff, 0, 0xffff, ), + $encoding + ); + } + + return $value; + } + + + private static $_dealingWithGlobals = false; + + private static function _parse_array( &$variable, kintVariableData $variableData ) + { + isset( self::$_marker ) or self::$_marker = "\x00" . uniqid(); + + # naturally, $GLOBALS variable is an intertwined recursion nightmare, use black magic + $globalsDetector = false; + if ( array_key_exists( 'GLOBALS', $variable ) && is_array( $variable['GLOBALS'] ) ) { + $globalsDetector = "\x01" . uniqid(); + + $variable['GLOBALS'][ $globalsDetector ] = true; + if ( isset( $variable[ $globalsDetector ] ) ) { + unset( $variable[ $globalsDetector ] ); + self::$_dealingWithGlobals = true; + } else { + unset( $variable['GLOBALS'][ $globalsDetector ] ); + $globalsDetector = false; + } + } + + $variableData->type = 'array'; + $variableData->size = count( $variable ); + + if ( $variableData->size === 0 ) { + return; + } + if ( isset( $variable[ self::$_marker ] ) ) { # recursion; todo mayhaps show from where + if ( self::$_dealingWithGlobals ) { + $variableData->value = '*RECURSION*'; + } else { + unset( $variable[ self::$_marker ] ); + $variableData->value = self::$_marker; + } + return false; + } + if ( self::_checkDepth() ) { + $variableData->extendedValue = "*DEPTH TOO GREAT*"; + return false; + } + + $isSequential = self::_isSequential( $variable ); + + if ( $variableData->size > 1 && ( $arrayKeys = self::_isArrayTabular( $variable ) ) !== false ) { + $variable[ self::$_marker ] = true; # this must be AFTER _isArrayTabular + $firstRow = true; + $extendedValue = ''; + + foreach ( $variable as $rowIndex => & $row ) { + # display strings in their full length + self::$_placeFullStringInValue = true; + + if ( $rowIndex === self::$_marker ) continue; + + if ( isset( $row[ self::$_marker ] ) ) { + $variableData->value = "*RECURSION*"; + return false; + } + + + $extendedValue .= ''; + if ( $isSequential ) { + $output = ''; + } else { + $output = self::_decorateCell( kintParser::factory( $rowIndex ) ); + } + if ( $firstRow ) { + $extendedValue .= ''; + } + + # we iterate the known full set of keys from all rows in case some appeared at later rows, + # as we only check the first two to assume + foreach ( $arrayKeys as $key ) { + if ( $firstRow ) { + $extendedValue .= ''; + } + + if ( !array_key_exists( $key, $row ) ) { + $output .= ''; + continue; + } + + $var = kintParser::factory( $row[ $key ] ); + + if ( $var->value === self::$_marker ) { + $variableData->value = '*RECURSION*'; + return false; + } elseif ( $var->value === '*RECURSION*' ) { + $output .= ''; + } else { + $output .= self::_decorateCell( $var ); + } + unset( $var ); + } + + if ( $firstRow ) { + $extendedValue .= ''; + $firstRow = false; + } + + $extendedValue .= $output . ''; + } + self::$_placeFullStringInValue = false; + + $variableData->extendedValue = $extendedValue . '
' . '#' . ( $rowIndex + 1 ) . ' ' . self::escape( $key ) . '*RECURSION*
'; + + } else { + $variable[ self::$_marker ] = true; + $extendedValue = array(); + + foreach ( $variable as $key => & $val ) { + if ( $key === self::$_marker ) continue; + + $output = kintParser::factory( $val ); + if ( $output->value === self::$_marker ) { + $variableData->value = "*RECURSION*"; // recursion occurred on a higher level, thus $this is recursion + return false; + } + if ( !$isSequential ) { + $output->operator = '=>'; + } + $output->name = $isSequential ? null : "'" . $key . "'"; + $extendedValue[] = $output; + } + $variableData->extendedValue = $extendedValue; + } + + if ( $globalsDetector ) { + self::$_dealingWithGlobals = false; + } + + unset( $variable[ self::$_marker ] ); + } + + + private static function _parse_object( &$variable, kintVariableData $variableData ) + { + if ( function_exists( 'spl_object_hash' ) ) { + $hash = spl_object_hash( $variable ); + } else { + ob_start(); + var_dump( $variable ); + preg_match( '[#(\d+)]', ob_get_clean(), $match ); + $hash = $match[1]; + } + + $castedArray = (array) $variable; + $variableData->type = get_class( $variable ); + $variableData->size = count( $castedArray ); + + if ( isset( self::$_objects[ $hash ] ) ) { + $variableData->value = '*RECURSION*'; + return false; + } + if ( self::_checkDepth() ) { + $variableData->extendedValue = "*DEPTH TOO GREAT*"; + return false; + } + + + # ArrayObject (and maybe ArrayIterator, did not try yet) unsurprisingly consist of mainly dark magic. + # What bothers me most, var_dump sees no problem with it, and ArrayObject also uses a custom, + # undocumented serialize function, so you can see the properties in internal functions, but + # can never iterate some of them if the flags are not STD_PROP_LIST. Fun stuff. + if ( $variableData->type === 'ArrayObject' || is_subclass_of( $variable, 'ArrayObject' ) ) { + $arrayObjectFlags = $variable->getFlags(); + $variable->setFlags( ArrayObject::STD_PROP_LIST ); + } + + self::$_objects[ $hash ] = true; // todo store reflectorObject here for alternatives cache + $reflector = new ReflectionObject( $variable ); + + # add link to definition of userland objects + if ( Kint::enabled() === Kint::MODE_RICH && Kint::$fileLinkFormat && $reflector->isUserDefined() ) { + $url = Kint::getIdeLink( $reflector->getFileName(), $reflector->getStartLine() ); + + $class = ( strpos( $url, 'http://' ) === 0 ) ? 'class="kint-ide-link" ' : ''; + $variableData->type = "{$variableData->type}"; + } + $variableData->size = 0; + + $extendedValue = array(); + $encountered = array(); + + # copy the object as an array as it provides more info than Reflection (depends) + foreach ( $castedArray as $key => $value ) { + /* casting object to array: + * integer properties are inaccessible; + * private variables have the class name prepended to the variable name; + * protected variables have a '*' prepended to the variable name. + * These prepended values have null bytes on either side. + * http://www.php.net/manual/en/language.types.array.php#language.types.array.casting + */ + if ( $key{0} === "\x00" ) { + + $access = $key{1} === "*" ? "protected" : "private"; + + // Remove the access level from the variable name + $key = substr( $key, strrpos( $key, "\x00" ) + 1 ); + } else { + $access = "public"; + } + + $encountered[ $key ] = true; + + $output = kintParser::factory( $value, self::escape( $key ) ); + $output->access = $access; + $output->operator = '->'; + $extendedValue[] = $output; + $variableData->size++; + } + + foreach ( $reflector->getProperties() as $property ) { + $name = $property->name; + if ( $property->isStatic() || isset( $encountered[ $name ] ) ) continue; + + if ( $property->isProtected() ) { + $property->setAccessible( true ); + $access = "protected"; + } elseif ( $property->isPrivate() ) { + $property->setAccessible( true ); + $access = "private"; + } else { + $access = "public"; + } + + $value = $property->getValue( $variable ); + + $output = kintParser::factory( $value, self::escape( $name ) ); + $output->access = $access; + $output->operator = '->'; + $extendedValue[] = $output; + $variableData->size++; + } + + if ( isset( $arrayObjectFlags ) ) { + $variable->setFlags( $arrayObjectFlags ); + } + + if ( $variableData->size ) { + $variableData->extendedValue = $extendedValue; + } + } + + + private static function _parse_boolean( &$variable, kintVariableData $variableData ) + { + $variableData->type = 'bool'; + $variableData->value = $variable ? 'TRUE' : 'FALSE'; + } + + private static function _parse_double( &$variable, kintVariableData $variableData ) + { + $variableData->type = 'float'; + $variableData->value = $variable; + } + + private static function _parse_integer( &$variable, kintVariableData $variableData ) + { + $variableData->type = 'integer'; + $variableData->value = $variable; + } + + private static function _parse_null( &$variable, kintVariableData $variableData ) + { + $variableData->type = 'NULL'; + } + + private static function _parse_resource( &$variable, kintVariableData $variableData ) + { + $resourceType = get_resource_type( $variable ); + $variableData->type = "resource ({$resourceType})"; + + if ( $resourceType === 'stream' && $meta = stream_get_meta_data( $variable ) ) { + + if ( isset( $meta['uri'] ) ) { + $file = $meta['uri']; + + if ( function_exists( 'stream_is_local' ) ) { + // Only exists on PHP >= 5.2.4 + if ( stream_is_local( $file ) ) { + $file = Kint::shortenPath( $file ); + } + } + + $variableData->value = $file; + } + } + } + + private static function _parse_string( &$variable, kintVariableData $variableData ) + { + $variableData->type = 'string'; + + $encoding = self::_detectEncoding( $variable ); + if ( $encoding !== 'ASCII' ) { + $variableData->type .= ' ' . $encoding; + } + + + $variableData->size = self::_strlen( $variable, $encoding ); + if ( Kint::enabled() !== Kint::MODE_RICH ) { + $variableData->value = '"' . self::escape( $variable, $encoding ) . '"'; + return; + } + + + if ( !self::$_placeFullStringInValue ) { + + $strippedString = preg_replace( '[\s+]', ' ', $variable ); + if ( Kint::$maxStrLength && $variableData->size > Kint::$maxStrLength ) { + + // encode and truncate + $variableData->value = '"' + . self::escape( self::_substr( $strippedString, 0, Kint::$maxStrLength, $encoding ), $encoding ) + . '…"'; + $variableData->extendedValue = self::escape( $variable, $encoding ); + + return; + } elseif ( $variable !== $strippedString ) { // omit no data from display + + $variableData->value = '"' . self::escape( $variable, $encoding ) . '"'; + $variableData->extendedValue = self::escape( $variable, $encoding ); + + return; + } + } + + $variableData->value = '"' . self::escape( $variable, $encoding ) . '"'; + } + + private static function _parse_unknown( &$variable, kintVariableData $variableData ) + { + $type = gettype( $variable ); + $variableData->type = "UNKNOWN" . ( !empty( $type ) ? " ({$type})" : '' ); + $variableData->value = var_export( $variable, true ); + } + +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/inc/kintVariableData.class.php b/sites/all/modules/contrib/dev/devel/kint/kint/inc/kintVariableData.class.php new file mode 100644 index 000000000..3ac567ed4 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/inc/kintVariableData.class.php @@ -0,0 +1,102 @@ +getMethods() as $method ) { + $params = array(); + + // Access type + $access = implode( ' ', Reflection::getModifierNames( $method->getModifiers() ) ); + + // Method parameters + foreach ( $method->getParameters() as $param ) { + $paramString = ''; + + if ( $param->isArray() ) { + $paramString .= 'array '; + } else { + try { + if ( $paramClassName = $param->getClass() ) { + $paramString .= $paramClassName->name . ' '; + } + } catch ( ReflectionException $e ) { + preg_match( '/\[\s\<\w+?>\s([\w]+)/s', $param->__toString(), $matches ); + $paramClassName = isset( $matches[1] ) ? $matches[1] : ''; + + $paramString .= ' UNDEFINED CLASS (' . $paramClassName . ') '; + } + } + + $paramString .= ( $param->isPassedByReference() ? '&' : '' ) . '$' . $param->getName(); + + if ( $param->isDefaultValueAvailable() ) { + if ( is_array( $param->getDefaultValue() ) ) { + $arrayValues = array(); + foreach ( $param->getDefaultValue() as $key => $value ) { + $arrayValues[] = $key . ' => ' . $value; + } + + $defaultValue = 'array(' . implode( ', ', $arrayValues ) . ')'; + } elseif ( $param->getDefaultValue() === null ) { + $defaultValue = 'NULL'; + } elseif ( $param->getDefaultValue() === false ) { + $defaultValue = 'false'; + } elseif ( $param->getDefaultValue() === true ) { + $defaultValue = 'true'; + } elseif ( $param->getDefaultValue() === '' ) { + $defaultValue = '""'; + } else { + $defaultValue = $param->getDefaultValue(); + } + + $paramString .= ' = ' . $defaultValue; + } + + $params[] = $paramString; + } + + $output = new kintVariableData; + + // Simple DocBlock parser, look for @return + if ( ( $docBlock = $method->getDocComment() ) ) { + $matches = array(); + if ( preg_match_all( '/@(\w+)\s+(.*)\r?\n/m', $docBlock, $matches ) ) { + $lines = array_combine( $matches[1], $matches[2] ); + if ( isset( $lines['return'] ) ) { + $output->operator = '->'; + # since we're outputting code, assumption that the string is utf8 is most likely correct + # and saves resources + $output->type = self::escape( $lines['return'], 'UTF-8' ); + } + } + } + + $output->name = ( $method->returnsReference() ? '&' : '' ) . $method->getName() . '(' + . implode( ', ', $params ) . ')'; + $output->access = $access; + + if ( is_string( $docBlock ) ) { + $lines = array(); + foreach ( explode( "\n", $docBlock ) as $line ) { + $line = trim( $line ); + + if ( in_array( $line, array( '/**', '/*', '*/' ) ) ) { + continue; + } elseif ( strpos( $line, '*' ) === 0 ) { + $line = substr( $line, 1 ); + } + + $lines[] = self::escape( trim( $line ), 'UTF-8' ); + } + + $output->extendedValue = implode( "\n", $lines ) . "\n\n"; + } + + $declaringClass = $method->getDeclaringClass(); + $declaringClassName = $declaringClass->getName(); + + if ( $declaringClassName !== $className ) { + $output->extendedValue .= "Inherited from {$declaringClassName}\n"; + } + + $fileName = Kint::shortenPath( $method->getFileName() ) . ':' . $method->getStartLine(); + $output->extendedValue .= "Defined in {$fileName}"; + + $sortName = $access . $method->getName(); + + if ( $method->isPrivate() ) { + $private[ $sortName ] = $output; + } elseif ( $method->isProtected() ) { + $protected[ $sortName ] = $output; + } else { + $public[ $sortName ] = $output; + } + } + + if ( !$private && !$protected && !$public ) { + self::$cache[ $className ] = false; + } + + ksort( $public ); + ksort( $protected ); + ksort( $private ); + + self::$cache[ $className ] = $public + $protected + $private; + } + + if ( count( self::$cache[ $className ] ) === 0 ) { + return false; + } + + $this->value = self::$cache[ $className ]; + $this->type = 'Available methods'; + $this->size = count( self::$cache[ $className ] ); + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/classstatics.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/classstatics.php new file mode 100644 index 000000000..cde23c650 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/classstatics.php @@ -0,0 +1,49 @@ +getProperties( ReflectionProperty::IS_STATIC ) as $property ) { + if ( $property->isPrivate() ) { + if ( !method_exists( $property, 'setAccessible' ) ) { + break; + } + $property->setAccessible( true ); + $access = "private"; + } elseif ( $property->isProtected() ) { + $property->setAccessible( true ); + $access = "protected"; + } else { + $access = 'public'; + } + + $_ = $property->getValue(); + $output = kintParser::factory( $_, '$' . $property->getName() ); + + $output->access = $access; + $output->operator = '::'; + $extendedValue[] = $output; + } + + foreach ( $reflection->getConstants() as $constant => $val ) { + $output = kintParser::factory( $val, $constant ); + + $output->access = 'constant'; + $output->operator = '::'; + $extendedValue[] = $output; + } + + if ( empty( $extendedValue ) ) return false; + + $this->value = $extendedValue; + $this->type = 'Static class properties'; + $this->size = count( $extendedValue ); + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/color.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/color.php new file mode 100644 index 000000000..5ca44d88b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/color.php @@ -0,0 +1,400 @@ +'#f0f8ff','antiquewhite'=>'#faebd7','aqua'=>'#00ffff','aquamarine'=>'#7fffd4','azure'=>'#f0ffff', + 'beige'=>'#f5f5dc','bisque'=>'#ffe4c4','black'=>'#000000','blanchedalmond'=>'#ffebcd','blue'=>'#0000ff', + 'blueviolet'=>'#8a2be2','brown'=>'#a52a2a','burlywood'=>'#deb887','cadetblue'=>'#5f9ea0','chartreuse'=>'#7fff00', + 'chocolate'=>'#d2691e','coral'=>'#ff7f50','cornflowerblue'=>'#6495ed','cornsilk'=>'#fff8dc','crimson'=>'#dc143c', + 'cyan'=>'#00ffff','darkblue'=>'#00008b','darkcyan'=>'#008b8b','darkgoldenrod'=>'#b8860b','darkgray'=>'#a9a9a9', + 'darkgrey'=>'#a9a9a9','darkgreen'=>'#006400','darkkhaki'=>'#bdb76b','darkmagenta'=>'#8b008b', + 'darkolivegreen'=>'#556b2f','darkorange'=>'#ff8c00','darkorchid'=>'#9932cc','darkred'=>'#8b0000', + 'darksalmon'=>'#e9967a','darkseagreen'=>'#8fbc8f','darkslateblue'=>'#483d8b','darkslategray'=>'#2f4f4f', + 'darkslategrey'=>'#2f4f4f','darkturquoise'=>'#00ced1','darkviolet'=>'#9400d3','deeppink'=>'#ff1493', + 'deepskyblue'=>'#00bfff','dimgray'=>'#696969','dimgrey'=>'#696969','dodgerblue'=>'#1e90ff', + 'firebrick'=>'#b22222','floralwhite'=>'#fffaf0','forestgreen'=>'#228b22','fuchsia'=>'#ff00ff', + 'gainsboro'=>'#dcdcdc','ghostwhite'=>'#f8f8ff','gold'=>'#ffd700','goldenrod'=>'#daa520','gray'=>'#808080', + 'grey'=>'#808080','green'=>'#008000','greenyellow'=>'#adff2f','honeydew'=>'#f0fff0','hotpink'=>'#ff69b4', + 'indianred'=>'#cd5c5c','indigo'=>'#4b0082','ivory'=>'#fffff0','khaki'=>'#f0e68c','lavender'=>'#e6e6fa', + 'lavenderblush'=>'#fff0f5','lawngreen'=>'#7cfc00','lemonchiffon'=>'#fffacd','lightblue'=>'#add8e6', + 'lightcoral'=>'#f08080','lightcyan'=>'#e0ffff','lightgoldenrodyellow'=>'#fafad2','lightgray'=>'#d3d3d3', + 'lightgrey'=>'#d3d3d3','lightgreen'=>'#90ee90','lightpink'=>'#ffb6c1','lightsalmon'=>'#ffa07a', + 'lightseagreen'=>'#20b2aa','lightskyblue'=>'#87cefa','lightslategray'=>'#778899','lightslategrey'=>'#778899', + 'lightsteelblue'=>'#b0c4de','lightyellow'=>'#ffffe0','lime'=>'#00ff00','limegreen'=>'#32cd32','linen'=>'#faf0e6', + 'magenta'=>'#ff00ff','maroon'=>'#800000','mediumaquamarine'=>'#66cdaa','mediumblue'=>'#0000cd', + 'mediumorchid'=>'#ba55d3','mediumpurple'=>'#9370d8','mediumseagreen'=>'#3cb371','mediumslateblue'=>'#7b68ee', + 'mediumspringgreen'=>'#00fa9a','mediumturquoise'=>'#48d1cc','mediumvioletred'=>'#c71585', + 'midnightblue'=>'#191970','mintcream'=>'#f5fffa','mistyrose'=>'#ffe4e1','moccasin'=>'#ffe4b5', + 'navajowhite'=>'#ffdead','navy'=>'#000080','oldlace'=>'#fdf5e6','olive'=>'#808000','olivedrab'=>'#6b8e23', + 'orange'=>'#ffa500','orangered'=>'#ff4500','orchid'=>'#da70d6','palegoldenrod'=>'#eee8aa','palegreen'=>'#98fb98', + 'paleturquoise'=>'#afeeee','palevioletred'=>'#d87093','papayawhip'=>'#ffefd5','peachpuff'=>'#ffdab9', + 'peru'=>'#cd853f','pink'=>'#ffc0cb','plum'=>'#dda0dd','powderblue'=>'#b0e0e6','purple'=>'#800080', + 'red'=>'#ff0000','rosybrown'=>'#bc8f8f','royalblue'=>'#4169e1','saddlebrown'=>'#8b4513','salmon'=>'#fa8072', + 'sandybrown'=>'#f4a460','seagreen'=>'#2e8b57','seashell'=>'#fff5ee','sienna'=>'#a0522d','silver'=>'#c0c0c0', + 'skyblue'=>'#87ceeb','slateblue'=>'#6a5acd','slategray'=>'#708090','slategrey'=>'#708090','snow'=>'#fffafa', + 'springgreen'=>'#00ff7f','steelblue'=>'#4682b4','tan'=>'#d2b48c','teal'=>'#008080','thistle'=>'#d8bfd8', + 'tomato'=>'#ff6347','turquoise'=>'#40e0d0','violet'=>'#ee82ee','wheat'=>'#f5deb3','white'=>'#ffffff', + 'whitesmoke'=>'#f5f5f5','yellow'=>'#ffff00','yellowgreen'=>'#9acd32' + ); + + + protected function _parse( & $variable ) + { + if ( !self::_fits( $variable ) ) return false; + + $this->type = 'CSS color'; + $variants = self::_convert( $variable ); + $this->value = + "
{$variable}
" + . "hex : {$variants['hex']}\n" + . "rgb : {$variants['rgb']}\n" + . ( isset( $variants['name'] ) ? "name: {$variants['name']}\n" : '' ) + . "hsl : {$variants['hsl']}"; + } + + + private static function _fits( $variable ) + { + if ( !is_string( $variable ) ) return false; + + $var = strtolower( trim( $variable ) ); + + return isset( self::$_css3Named[$var] ) + || preg_match( + '/^(?:#[0-9A-Fa-f]{3}|#[0-9A-Fa-f]{6}|(?:rgb|hsl)a?\s*\((?:\s*[0-9.%]+\s*,?){3,4}\))$/', + $var + ); + } + + private static function _convert( $color ) + { + $color = strtolower( $color ); + $decimalColors = array(); + $variants = array( + 'hex' => null, + 'rgb' => null, + 'name' => null, + 'hsl' => null, + ); + + if ( isset( self::$_css3Named[ $color ] ) ) { + $variants['name'] = $color; + $color = self::$_css3Named[ $color ]; + } + + if ( $color{0} === '#' ) { + $variants['hex'] = $color; + $color = substr( $color, 1 ); + if ( strlen( $color ) === 6 ) { + $colors = str_split( $color, 2 ); + } else { + $colors = array( + $color{0} . $color{0}, + $color{1} . $color{1}, + $color{2} . $color{2}, + ); + } + + $decimalColors = array_map( 'hexdec', $colors ); + } elseif ( substr( $color, 0, 3 ) === 'rgb' ) { + $variants['rgb'] = $color; + preg_match_all( '#([0-9.%]+)#', $color, $matches ); + $decimalColors = $matches[1]; + foreach ( $decimalColors as &$color ) { + if ( strpos( $color, '%' ) !== false ) { + $color = str_replace( '%', '', $color ) * 2.55; + } + } + + + } elseif ( substr( $color, 0, 3 ) === 'hsl' ) { + $variants['hsl'] = $color; + preg_match_all( '#([0-9.%]+)#', $color, $matches ); + + $colors = $matches[1]; + $colors[0] /= 360; + $colors[1] = str_replace( '%', '', $colors[1] ) / 100; + $colors[2] = str_replace( '%', '', $colors[2] ) / 100; + + $decimalColors = self::_HSLtoRGB( $colors ); + if ( isset( $colors[3] ) ) { + $decimalColors[] = $colors[3]; + } + } + + if ( isset( $decimalColors[3] ) ) { + $alpha = $decimalColors[3]; + unset( $decimalColors[3] ); + } else { + $alpha = null; + } + foreach ( $variants as $type => &$variant ) { + if ( isset( $variant ) ) continue; + + switch ( $type ) { + case 'hex': + $variant = '#'; + foreach ( $decimalColors as &$color ) { + $variant .= str_pad( dechex( $color ), 2, "0", STR_PAD_LEFT ); + } + $variant .= isset( $alpha ) ? ' (alpha omitted)' : ''; + break; + case 'rgb': + $rgb = $decimalColors; + if ( isset( $alpha ) ) { + $rgb[] = $alpha; + $a = 'a'; + } else { + $a = ''; + } + $variant = "rgb{$a}( " . implode( ', ', $rgb ) . " )"; + break; + case 'hsl': + $rgb = self::_RGBtoHSL( $decimalColors ); + if ( $rgb === null ) { + unset( $variants[ $type ] ); + break; + } + if ( isset( $alpha ) ) { + $rgb[] = $alpha; + $a = 'a'; + } else { + $a = ''; + } + + $variant = "hsl{$a}( " . implode( ', ', $rgb ) . " )"; + break; + case 'name': + // [!] name in initial variants array must go after hex + if ( ( $key = array_search( $variants['hex'], self::$_css3Named, true ) ) !== false ) { + $variant = $key; + } else { + unset( $variants[ $type ] ); + } + break; + } + + } + + return $variants; + } + + + private static function _HSLtoRGB( array $hsl ) + { + list( $h, $s, $l ) = $hsl; + $m2 = ( $l <= 0.5 ) ? $l * ( $s + 1 ) : $l + $s - $l * $s; + $m1 = $l * 2 - $m2; + return array( + round( self::_hue2rgb( $m1, $m2, $h + 0.33333 ) * 255 ), + round( self::_hue2rgb( $m1, $m2, $h ) * 255 ), + round( self::_hue2rgb( $m1, $m2, $h - 0.33333 ) * 255 ), + ); + } + + + /** + * Helper function for _color_hsl2rgb(). + */ + private static function _hue2rgb( $m1, $m2, $h ) + { + $h = ( $h < 0 ) ? $h + 1 : ( ( $h > 1 ) ? $h - 1 : $h ); + if ( $h * 6 < 1 ) return $m1 + ( $m2 - $m1 ) * $h * 6; + if ( $h * 2 < 1 ) return $m2; + if ( $h * 3 < 2 ) return $m1 + ( $m2 - $m1 ) * ( 0.66666 - $h ) * 6; + return $m1; + } + + + private static function _RGBtoHSL( array $rgb ) + { + list( $clrR, $clrG, $clrB ) = $rgb; + + $clrMin = min( $clrR, $clrG, $clrB ); + $clrMax = max( $clrR, $clrG, $clrB ); + $deltaMax = $clrMax - $clrMin; + + $L = ( $clrMax + $clrMin ) / 510; + + if ( 0 == $deltaMax ) { + $H = 0; + $S = 0; + } else { + if ( 0.5 > $L ) { + $S = $deltaMax / ( $clrMax + $clrMin ); + } else { + $S = $deltaMax / ( 510 - $clrMax - $clrMin ); + } + + if ( $clrMax == $clrR ) { + $H = ( $clrG - $clrB ) / ( 6.0 * $deltaMax ); + } else if ( $clrMax == $clrG ) { + $H = 1 / 3 + ( $clrB - $clrR ) / ( 6.0 * $deltaMax ); + } else { + $H = 2 / 3 + ( $clrR - $clrG ) / ( 6.0 * $deltaMax ); + } + + if ( 0 > $H ) $H += 1; + if ( 1 < $H ) $H -= 1; + } + return array( + round( $H * 360 ), + round( $S * 100 ) . '%', + round( $L * 100 ) . '%' + ); + + } +} + +/* ************* + * TEST DATA + * +dd(array( +'hsl(0, 100%,50%)', +'hsl(30, 100%,50%)', +'hsl(60, 100%,50%)', +'hsl(90, 100%,50%)', +'hsl(120,100%,50%)', +'hsl(150,100%,50%)', +'hsl(180,100%,50%)', +'hsl(210,100%,50%)', +'hsl(240,100%,50%)', +'hsl(270,100%,50%)', +'hsl(300,100%,50%)', +'hsl(330,100%,50%)', +'hsl(360,100%,50%)', +'hsl(120,100%,25%)', +'hsl(120,100%,50%)', +'hsl(120,100%,75%)', +'hsl(120,100%,50%)', +'hsl(120, 67%,50%)', +'hsl(120, 33%,50%)', +'hsl(120, 0%,50%)', +'hsl(120, 60%,70%)', +'#f03', +'#F03', +'#ff0033', +'#FF0033', +'rgb(255,0,51)', +'rgb(255, 0, 51)', +'rgb(100%,0%,20%)', +'rgb(100%, 0%, 20%)', +'hsla(240,100%,50%,0.05)', +'hsla(240,100%,50%, 0.4)', +'hsla(240,100%,50%, 0.7)', +'hsla(240,100%,50%, 1)', +'rgba(255,0,0,0.1)', +'rgba(255,0,0,0.4)', +'rgba(255,0,0,0.7)', +'rgba(255,0,0, 1)', +'black', +'silver', +'gray', +'white', +'maroon', +'red', +'purple', +'fuchsia', +'green', +'lime', +'olive', +'yellow', +'navy', +'blue', +'teal', +'aqua', +'orange', +'aliceblue', +'antiquewhite', +'aquamarine', +'azure', +'beige', +'bisque', +'blanchedalmond', +'blueviolet', +'brown', +'burlywood', +'cadetblue', +'chartreuse', +'chocolate', +'coral', +'cornflowerblue', +'cornsilk', +'crimson', +'darkblue', +'darkcyan', +'darkgoldenrod', +'darkgray', +'darkgreen', +'darkgrey', +'darkkhaki', +'darkmagenta', +'darkolivegreen', +'darkorange', +'darkorchid', +'darkred', +'darksalmon', +'darkseagreen', +'darkslateblue', +'darkslategray', +'darkslategrey', +'darkturquoise', +'darkviolet', +'deeppink', +'deepskyblue', +'dimgray', +'dimgrey', +'dodgerblue', +'firebrick', +'floralwhite', +'forestgreen', +'gainsboro', +'ghostwhite', +'gold', +'goldenrod', +'greenyellow', +'grey', +'honeydew', +'hotpink', +'indianred', +'indigo', +'ivory', +'khaki', +'lavender', +'lavenderblush', +'lawngreen', +'lemonchiffon', +'lightblue', +'lightcoral', +'lightcyan', +'lightgoldenrodyellow', +'lightgray', +'lightgreen', +'lightgrey', +'lightpink', +'lightsalmon', +'lightseagreen', +'lightskyblue', +'lightslategray', +'lightslategrey', +'lightsteelblue', +'lightyellow', +'limegreen', +'linen', +'mediumaquamarine', +'mediumblue', +'mediumorchid', +'mediumpurple', +'mediumseagreen', +'mediumslateblue', +'mediumspringgreen', +'mediumturquoise', +'mediumvioletred', +'midnightblue', +'mintcream', +'mistyrose', +'moccasin', +'navajowhite', +'oldlace', +'olivedrab', +));*/ \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/fspath.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/fspath.php new file mode 100644 index 000000000..0610391cd --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/fspath.php @@ -0,0 +1,69 @@ + 2048 + || preg_match( '[[:?<>"*|]]', $variable ) + || !@is_readable( $variable ) # f@#! PHP and its random warnings + ) return false; + + try { + $fileInfo = new SplFileInfo( $variable ); + $flags = array(); + $perms = $fileInfo->getPerms(); + + if ( ( $perms & 0xC000 ) === 0xC000 ) { + $type = 'File socket'; + $flags[] = 's'; + } elseif ( ( $perms & 0xA000 ) === 0xA000 ) { + $type = 'File symlink'; + $flags[] = 'l'; + } elseif ( ( $perms & 0x8000 ) === 0x8000 ) { + $type = 'File'; + $flags[] = '-'; + } elseif ( ( $perms & 0x6000 ) === 0x6000 ) { + $type = 'Block special file'; + $flags[] = 'b'; + } elseif ( ( $perms & 0x4000 ) === 0x4000 ) { + $type = 'Directory'; + $flags[] = 'd'; + } elseif ( ( $perms & 0x2000 ) === 0x2000 ) { + $type = 'Character special file'; + $flags[] = 'c'; + } elseif ( ( $perms & 0x1000 ) === 0x1000 ) { + $type = 'FIFO pipe file'; + $flags[] = 'p'; + } else { + $type = 'Unknown file'; + $flags[] = 'u'; + } + + // owner + $flags[] = ( ( $perms & 0x0100 ) ? 'r' : '-' ); + $flags[] = ( ( $perms & 0x0080 ) ? 'w' : '-' ); + $flags[] = ( ( $perms & 0x0040 ) ? ( ( $perms & 0x0800 ) ? 's' : 'x' ) : ( ( $perms & 0x0800 ) ? 'S' : '-' ) ); + + // group + $flags[] = ( ( $perms & 0x0020 ) ? 'r' : '-' ); + $flags[] = ( ( $perms & 0x0010 ) ? 'w' : '-' ); + $flags[] = ( ( $perms & 0x0008 ) ? ( ( $perms & 0x0400 ) ? 's' : 'x' ) : ( ( $perms & 0x0400 ) ? 'S' : '-' ) ); + + // world + $flags[] = ( ( $perms & 0x0004 ) ? 'r' : '-' ); + $flags[] = ( ( $perms & 0x0002 ) ? 'w' : '-' ); + $flags[] = ( ( $perms & 0x0001 ) ? ( ( $perms & 0x0200 ) ? 't' : 'x' ) : ( ( $perms & 0x0200 ) ? 'T' : '-' ) ); + + $this->type = $type; + $this->size = sprintf( '%.2fK', $fileInfo->getSize() / 1024 ); + $this->value = implode( $flags ); + + } catch ( Exception $e ) { + return false; + } + + } +} diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/json.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/json.php new file mode 100644 index 000000000..f752a214c --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/json.php @@ -0,0 +1,19 @@ +value = kintParser::factory( $val )->extendedValue; + $this->type = 'JSON'; + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/microtime.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/microtime.php new file mode 100644 index 000000000..2c5fe68fa --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/microtime.php @@ -0,0 +1,60 @@ +value = @date( 'Y-m-d H:i:s', $sec ) . '.' . substr( $usec, 2, 4 ); + + $numberOfCalls = count( self::$_times ); + if ( $numberOfCalls > 0 ) { # meh, faster than count($times) > 1 + $lap = $time - end( self::$_times ); + self::$_laps[] = $lap; + + $this->value .= "\nSINCE LAST CALL: " . round( $lap, 4 ) . 's.'; + if ( $numberOfCalls > 1 ) { + $this->value .= "\nSINCE START: " . round( $time - self::$_times[0], 4 ) . 's.'; + $this->value .= "\nAVERAGE DURATION: " + . round( array_sum( self::$_laps ) / $numberOfCalls, 4 ) . 's.'; + } + } + + $unit = array( 'B', 'KB', 'MB', 'GB', 'TB' ); + if ( KINT_PHP53 ) { + $this->value .= "\nMEMORY USAGE: " . $size . " bytes (" + . round( $size / pow( 1024, ( $i = floor( log( $size, 1024 ) ) ) ), 3 ) . ' ' . $unit[ $i ] . ")"; + } + + self::$_times[] = $time; + $this->type = 'Stats'; + } + + /* + function test() { + d( 'start', microtime() ); + for ( $i = 0; $i < 10; $i++ ) { + d( + $duration = mt_rand( 0, 200000 ), // the reported duration will be larger because of Kint overhead + usleep( $duration ), + microtime() + ); + } + dd( ); + } + */ +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/objectiterateable.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/objectiterateable.php new file mode 100644 index 000000000..bbc0de070 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/objectiterateable.php @@ -0,0 +1,22 @@ +value = kintParser::factory( $arrayCopy )->extendedValue; + $this->type = 'Iterator contents'; + $this->size = count( $arrayCopy ); + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/splobjectstorage.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/splobjectstorage.php new file mode 100644 index 000000000..69da91fd9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/splobjectstorage.php @@ -0,0 +1,24 @@ +count(); + if ( $count === 0 ) return false; + + $variable->rewind(); + while ( $variable->valid() ) { + $current = $variable->current(); + $this->value[] = kintParser::factory( $current ); + $variable->next(); + } + + $this->type = 'Storage contents'; + $this->size = $count; + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/timestamp.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/timestamp.php new file mode 100644 index 000000000..d1a3ce323 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/timestamp.php @@ -0,0 +1,29 @@ +type = 'timestamp'; + # avoid dreaded "Timezone must be set" error + $this->value = @date( 'Y-m-d H:i:s', $var ); + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/xml.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/xml.php new file mode 100644 index 000000000..63283a0fa --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/custom/xml.php @@ -0,0 +1,25 @@ +value = kintParser::factory( $xml )->extendedValue; + $this->type = 'XML'; + } +} diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/closure.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/closure.php new file mode 100644 index 000000000..b050d3a70 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/closure.php @@ -0,0 +1,47 @@ +name = 'Closure'; + $reflection = new ReflectionFunction( $variable ); + $ret = array( + 'Parameters' => array() + ); + if ( $val = $reflection->getParameters() ) { + foreach ( $val as $parameter ) { + // todo http://php.net/manual/en/class.reflectionparameter.php + $ret['Parameters'][] = $parameter->name; + } + + } + if ( $val = $reflection->getStaticVariables() ) { + $ret['Uses'] = $val; + } + if ( method_exists($reflection, 'getClousureThis') && $val = $reflection->getClosureThis() ) { + $ret['Uses']['$this'] = $val; + } + if ( $val = $reflection->getFileName() ) { + $this->value = Kint::shortenPath( $val ) . ':' . $reflection->getStartLine(); + } + + return $ret; + } + + + public function isDefaultValueAvailable() + { + if ( PHP_VERSION_ID === 50316 ) { // PHP bug #62988 + try { + $this->getDefaultValue(); + return true; + } catch ( \ReflectionException $e ) { + return false; + } + } + return parent::isDefaultValueAvailable(); + } +} diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/smarty.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/smarty.php new file mode 100644 index 000000000..52b07505e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/smarty.php @@ -0,0 +1,35 @@ +name = 'object Smarty (v' . substr( Smarty::SMARTY_VERSION, 7 ) . ')'; # trim 'Smarty-' + + $assigned = $globalAssigns = array(); + foreach ( $variable->tpl_vars as $name => $var ) { + $assigned[ $name ] = $var->value; + } + foreach ( Smarty::$global_tpl_vars as $name => $var ) { + if ( $name === 'SCRIPT_NAME' ) continue; + + $globalAssigns[ $name ] = $var->value; + } + + return array( + 'Assigned' => $assigned, + 'Assigned globally' => $globalAssigns, + 'Configuration' => array( + 'Compiled files stored in' => isset($variable->compile_dir) + ? $variable->compile_dir + : $variable->getCompileDir(), + ) + ); + + } +} diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/splfileinfo.php b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/splfileinfo.php new file mode 100644 index 000000000..964dd6150 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/parsers/objects/splfileinfo.php @@ -0,0 +1,70 @@ +name = 'SplFileInfo'; + $this->value = $variable->getBasename(); + + + $flags = array(); + $perms = $variable->getPerms(); + + if ( ( $perms & 0xC000 ) === 0xC000 ) { + $type = 'File socket'; + $flags[] = 's'; + } elseif ( ( $perms & 0xA000 ) === 0xA000 ) { + $type = 'File symlink'; + $flags[] = 'l'; + } elseif ( ( $perms & 0x8000 ) === 0x8000 ) { + $type = 'File'; + $flags[] = '-'; + } elseif ( ( $perms & 0x6000 ) === 0x6000 ) { + $type = 'Block special file'; + $flags[] = 'b'; + } elseif ( ( $perms & 0x4000 ) === 0x4000 ) { + $type = 'Directory'; + $flags[] = 'd'; + } elseif ( ( $perms & 0x2000 ) === 0x2000 ) { + $type = 'Character special file'; + $flags[] = 'c'; + } elseif ( ( $perms & 0x1000 ) === 0x1000 ) { + $type = 'FIFO pipe file'; + $flags[] = 'p'; + } else { + $type = 'Unknown file'; + $flags[] = 'u'; + } + + // owner + $flags[] = ( ( $perms & 0x0100 ) ? 'r' : '-' ); + $flags[] = ( ( $perms & 0x0080 ) ? 'w' : '-' ); + $flags[] = ( ( $perms & 0x0040 ) ? ( ( $perms & 0x0800 ) ? 's' : 'x' ) : ( ( $perms & 0x0800 ) ? 'S' : '-' ) ); + + // group + $flags[] = ( ( $perms & 0x0020 ) ? 'r' : '-' ); + $flags[] = ( ( $perms & 0x0010 ) ? 'w' : '-' ); + $flags[] = ( ( $perms & 0x0008 ) ? ( ( $perms & 0x0400 ) ? 's' : 'x' ) : ( ( $perms & 0x0400 ) ? 'S' : '-' ) ); + + // world + $flags[] = ( ( $perms & 0x0004 ) ? 'r' : '-' ); + $flags[] = ( ( $perms & 0x0002 ) ? 'w' : '-' ); + $flags[] = ( ( $perms & 0x0001 ) ? ( ( $perms & 0x0200 ) ? 't' : 'x' ) : ( ( $perms & 0x0200 ) ? 'T' : '-' ) ); + + $size = sprintf( '%.2fK', $variable->getSize() / 1024 ); + $flags = implode( $flags ); + $path = $variable->getRealPath(); + + return array( + 'File information' => array( + 'Full path' => $path, + 'Type' => $type, + 'Size' => $size, + 'Flags' => $flags + ) + ); + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/base.js b/sites/all/modules/contrib/dev/devel/kint/kint/view/base.js new file mode 100644 index 000000000..45de721ba --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/base.js @@ -0,0 +1,437 @@ +/** + java -jar compiler.jar --js $FileName$ --js_output_file ../inc/kint.js --compilation_level ADVANCED_OPTIMIZATIONS --output_wrapper "(function(){%output%})()" + */ + +if ( typeof kintInitialized === 'undefined' ) { + kintInitialized = 1; + var kint = { + visiblePluses : [], // all visible toggle carets + currentPlus : -1, // currently selected caret + + selectText : function( element ) { + var selection = window.getSelection(), + range = document.createRange(); + + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + }, + + each : function( selector, callback ) { + Array.prototype.slice.call(document.querySelectorAll(selector), 0).forEach(callback) + }, + + hasClass : function( target, className ) { + if ( !target.classList ) return false; + + if ( typeof className === 'undefined' ) { + className = 'kint-show'; + } + return target.classList.contains(className); + }, + + addClass : function( target, className ) { + if ( typeof className === 'undefined' ) { + className = 'kint-show'; + } + target.classList.add(className); + }, + + removeClass : function( target, className ) { + if ( typeof className === 'undefined' ) { + className = 'kint-show'; + } + target.classList.remove(className); + return target; + }, + + next : function( element ) { + do { + element = element.nextElementSibling; + } while ( element.nodeName.toLowerCase() !== 'dd' ); + + return element; + }, + + toggle : function( element, hide ) { + var parent = kint.next(element); + + if ( typeof hide === 'undefined' ) { + hide = kint.hasClass(element); + } + + if ( hide ) { + kint.removeClass(element); + } else { + kint.addClass(element); + } + + if ( parent.childNodes.length === 1 ) { + parent = parent.childNodes[0].childNodes[0]; // reuse variable cause I can + + // parent is checked in case of empty
 when array("\n") is dumped
+				if ( parent && kint.hasClass(parent, 'kint-parent') ) {
+					kint.toggle(parent, hide)
+				}
+			}
+		},
+
+		toggleChildren : function( element, hide ) {
+			var parent = kint.next(element)
+				, nodes = parent.getElementsByClassName('kint-parent')
+				, i = nodes.length;
+
+			if ( typeof hide === 'undefined' ) {
+				hide = kint.hasClass(element);
+			}
+
+			while ( i-- ) {
+				kint.toggle(nodes[i], hide);
+			}
+			kint.toggle(element, hide);
+		},
+
+		toggleAll : function( caret ) {
+			var elements = document.getElementsByClassName('kint-parent')
+				, i = elements.length
+				, visible = kint.hasClass(caret.parentNode);
+
+			while ( i-- ) {
+				kint.toggle(elements[i], visible);
+			}
+		},
+
+		switchTab : function( target ) {
+			var lis, el = target, index = 0;
+
+			target.parentNode.getElementsByClassName('kint-active-tab')[0].className = '';
+			target.className = 'kint-active-tab';
+
+			// take the index of clicked title tab and make the same n-th content tab visible
+			while ( el = el.previousSibling ) el.nodeType === 1 && index++;
+			lis = target.parentNode.nextSibling.childNodes;
+			for ( var i = 0; i < lis.length; i++ ) {
+				if ( i === index ) {
+					lis[i].style.display = 'block';
+
+					if ( lis[i].childNodes.length === 1 ) {
+						el = lis[i].childNodes[0].childNodes[0];
+
+						if ( kint.hasClass(el, 'kint-parent') ) {
+							kint.toggle(el, false)
+						}
+					}
+				} else {
+					lis[i].style.display = 'none';
+				}
+			}
+		},
+
+		isSibling : function( el ) {
+			for ( ; ; ) {
+				el = el.parentNode;
+				if ( !el || kint.hasClass(el, 'kint') ) break;
+			}
+
+			return !!el;
+		},
+
+		fetchVisiblePluses : function() {
+			kint.visiblePluses = [];
+			kint.each('.kint nav, .kint-tabs>li:not(.kint-active-tab)', function( el ) {
+				if ( el.offsetWidth !== 0 || el.offsetHeight !== 0 ) {
+					kint.visiblePluses.push(el)
+				}
+			});
+		},
+
+		openInNewWindow : function( kintContainer ) {
+			var newWindow;
+
+			if ( newWindow = window.open() ) {
+				newWindow.document.open();
+				newWindow.document.write(
+					''
+					+ ''
+					+ 'Kint (' + new Date().toISOString() + ')'
+					+ ''
+					+ document.getElementsByClassName('-kint-js')[0].outerHTML
+					+ document.getElementsByClassName('-kint-css')[0].outerHTML
+					+ ''
+					+ ''
+					+ ''
+					+ '
' + + kintContainer.parentNode.outerHTML + + '
' + ); + newWindow.document.close(); + } + }, + + sortTable : function( table, column ) { + var tbody = table.tBodies[0]; + + var format = function( s ) { + var n = column === 1 ? s.replace(/^#/, '') : s; + if ( isNaN(n) ) { + return s.trim().toLocaleLowerCase(); + } else { + n = parseFloat(n); + return isNaN(n) ? s.trim() : n; + } + }; + + + [].slice.call(table.tBodies[0].rows) + .sort(function( a, b ) { + a = format(a.cells[column].textContent); + b = format(b.cells[column].textContent); + if ( a < b ) return -1; + if ( a > b ) return 1; + + return 0; + }) + .forEach(function( el ) { + tbody.appendChild(el); + }); + }, + + keyCallBacks : { + cleanup : function( i ) { + var focusedClass = 'kint-focused'; + var prevElement = document.querySelector('.' + focusedClass); + prevElement && kint.removeClass(prevElement, focusedClass); + + if ( i !== -1 ) { + var el = kint.visiblePluses[i]; + kint.addClass(el, focusedClass); + + + var offsetTop = function( el ) { + return el.offsetTop + ( el.offsetParent ? offsetTop(el.offsetParent) : 0 ); + }; + + var top = offsetTop(el) - (window.innerHeight / 2 ); + window.scrollTo(0, top); + } + + kint.currentPlus = i; + }, + + moveCursor : function( up, i ) { + // todo make the first VISIBLE plus active + if ( up ) { + if ( --i < 0 ) { + i = kint.visiblePluses.length - 1; + } + } else { + if ( ++i >= kint.visiblePluses.length ) { + i = 0; + } + } + + kint.keyCallBacks.cleanup(i); + return false; + } + } + }; + + window.addEventListener("click", function( e ) { + var target = e.target + , nodeName = target.nodeName.toLowerCase(); + + if ( !kint.isSibling(target) ) return; + + // auto-select name of variable + if ( nodeName === 'dfn' ) { + kint.selectText(target); + target = target.parentNode; + } else if ( nodeName === 'var' ) { // stupid workaround for misc elements + target = target.parentNode; // to not stop event from further propagating + nodeName = target.nodeName.toLowerCase() + } else if ( nodeName === 'th' ) { + if ( !e.ctrlKey ) { + kint.sortTable(target.parentNode.parentNode.parentNode, target.cellIndex) + } + return false; + } + + // switch tabs + if ( nodeName === 'li' && target.parentNode.className === 'kint-tabs' ) { + if ( target.className !== 'kint-active-tab' ) { + kint.switchTab(target); + if ( kint.currentPlus !== -1 ) kint.fetchVisiblePluses(); + } + return false; + } + + // handle clicks on the navigation caret + if ( nodeName === 'nav' ) { + // special case for nav in footer + if ( target.parentNode.nodeName.toLowerCase() === 'footer' ) { + target = target.parentNode; + if ( kint.hasClass(target) ) { + kint.removeClass(target) + } else { + kint.addClass(target) + } + } else { + // ensure doubleclick has different behaviour, see below + setTimeout(function() { + var timer = parseInt(target.kintTimer, 10); + if ( timer > 0 ) { + target.kintTimer--; + } else { + kint.toggleChildren(target.parentNode); //
+ if ( kint.currentPlus !== -1 ) kint.fetchVisiblePluses(); + } + }, 300); + } + + e.stopPropagation(); + return false; + } else if ( kint.hasClass(target, 'kint-parent') ) { + kint.toggle(target); + if ( kint.currentPlus !== -1 ) kint.fetchVisiblePluses(); + return false; + } else if ( kint.hasClass(target, 'kint-ide-link') ) { + e.preventDefault(); + var ajax = new XMLHttpRequest(); // add ajax call to contact editor but prevent link default action + ajax.open('GET', target.href); + ajax.send(null); + return false; + } else if ( kint.hasClass(target, 'kint-popup-trigger') ) { + var kintContainer = target.parentNode; + if ( kintContainer.nodeName.toLowerCase() === 'footer' ) { + kintContainer = kintContainer.previousSibling; + } else { + while ( kintContainer && !kint.hasClass(kintContainer, 'kint-parent') ) { + kintContainer = kintContainer.parentNode; + } + } + + kint.openInNewWindow(kintContainer); + } else if ( nodeName === 'pre' && e.detail === 3 ) { // triple click pre to select it all + kint.selectText(target); + } + }, false); + + window.addEventListener("dblclick", function( e ) { + var target = e.target; + if ( !kint.isSibling(target) ) return; + + if ( target.nodeName.toLowerCase() === 'nav' ) { + target.kintTimer = 2; + kint.toggleAll(target); + if ( kint.currentPlus !== -1 ) kint.fetchVisiblePluses(); + e.stopPropagation(); + } + }, false); + + // keyboard navigation + window.onkeydown = function( e ) { // direct assignment is used to have priority over ex FAYT + + // do nothing if alt/ctrl key is pressed or if we're actually typing somewhere + if ( e.target !== document.body || e.altKey || e.ctrlKey ) return; + + var keyCode = e.keyCode + , shiftKey = e.shiftKey + , i = kint.currentPlus; + + + if ( keyCode === 68 ) { // 'd' : toggles navigation on/off + if ( i === -1 ) { + kint.fetchVisiblePluses(); + return kint.keyCallBacks.moveCursor(false, i); + } else { + kint.keyCallBacks.cleanup(-1); + return false; + } + } else { + if ( i === -1 ) return; + + if ( keyCode === 9 ) { // TAB : moves up/down depending on shift key + return kint.keyCallBacks.moveCursor(shiftKey, i); + } else if ( keyCode === 38 ) { // ARROW UP : moves up + return kint.keyCallBacks.moveCursor(true, i); + } else if ( keyCode === 40 ) { // ARROW DOWN : down + return kint.keyCallBacks.moveCursor(false, i); + } + } + + + var kintNode = kint.visiblePluses[i]; + if ( kintNode.nodeName.toLowerCase() === 'li' ) { // we're on a trace tab + if ( keyCode === 32 || keyCode === 13 ) { // SPACE/ENTER + kint.switchTab(kintNode); + kint.fetchVisiblePluses(); + return kint.keyCallBacks.moveCursor(true, i); + } else if ( keyCode === 39 ) { // arrows + return kint.keyCallBacks.moveCursor(false, i); + } else if ( keyCode === 37 ) { + return kint.keyCallBacks.moveCursor(true, i); + } + } + + kintNode = kintNode.parentNode; // simple dump + if ( keyCode === 32 || keyCode === 13 ) { // SPACE/ENTER : toggles + kint.toggle(kintNode); + kint.fetchVisiblePluses(); + return false; + } else if ( keyCode === 39 || keyCode === 37 ) { // ARROW LEFT/RIGHT : respectively hides/shows and traverses + var visible = kint.hasClass(kintNode); + var hide = keyCode === 37; + + if ( visible ) { + kint.toggleChildren(kintNode, hide); // expand/collapse all children if immediate ones are showing + } else { + if ( hide ) { // LEFT + // traverse to parent and THEN hide + do {kintNode = kintNode.parentNode} while ( kintNode && kintNode.nodeName.toLowerCase() !== 'dd' ); + + if ( kintNode ) { + kintNode = kintNode.previousElementSibling; + + i = -1; + var parentPlus = kintNode.querySelector('nav'); + while ( parentPlus !== kint.visiblePluses[++i] ) {} + kint.keyCallBacks.cleanup(i) + } else { // we are at root + kintNode = kint.visiblePluses[i].parentNode; + } + } + kint.toggle(kintNode, hide); + } + kint.fetchVisiblePluses(); + return false; + } + }; + + window.addEventListener("load", function( e ) { // colorize microtime results relative to others + var elements = Array.prototype.slice.call(document.querySelectorAll('.kint-microtime'), 0); + elements.forEach(function( el ) { + var value = parseFloat(el.innerHTML) + , min = Infinity + , max = -Infinity + , ratio; + + elements.forEach(function( el ) { + var val = parseFloat(el.innerHTML); + + if ( min > val ) min = val; + if ( max < val ) max = val; + }); + + ratio = 1 - (value - min) / (max - min); + + el.style.background = 'hsl(' + Math.round(ratio * 120) + ',60%,70%)'; + }); + }); +} + +// debug purposes only, removed in minified source +function clg( i ) { + if ( !window.console )return; + var l = arguments.length, o = 0; + while ( o < l )console.log(arguments[o++]) +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/base.less b/sites/all/modules/contrib/dev/devel/kint/kint/view/base.less new file mode 100644 index 000000000..24c68f766 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/base.less @@ -0,0 +1,413 @@ +// +// VARIABLES FOR THEMES TO OVERRIDE +// -------------------------------------------------- + +@spacing : 4; + +// caret taken from solarized +@caret-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAA8CAMAAACO5hB7AAAA7VBMVEVYbnWToaH///9YbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnWToaGToaGToaGToaGToaGToaGToaGToaGToaFYbnWToaFYbnVYbnWToaGToaFYbnWToaFYbnWToaFYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnWToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaFYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnWToaGToaGToaGToaGToaGToaGToaGToaGToaFYbnWToaEeRtj1AAAATXRSTlMAAACLab0B7iP2R7CL9iPuaUewAb36F0/QnPoX0JxPYQaurepi6QV8FhV7MzFhM6586RYFMa3qYnsVBpNQ+ZGOGJ3PkM9QkZAY+Y6TnV7gnYsAAAEcSURBVHhebdHVbgMxEEBRu8sUZmqapBBoSuGUmWb+/3NayxtrLOe+Hck745XZgd6/eb3CVcKt7HmR+gKxlCGOETG5vFbOoahR6NxI5zHt6nuq+2dqnqfzzH3mfcz70vb8H6MJt5u6a96hS30E4PjEFgAEp2fKNojKUfVEOoS044ex7u3YPE/nmfvM+5j3pe17T9oe/77O41w+n4vnrbrwZxbTshVhvtx5Kb8vliRLRWmeSQSTjJq/El5x5fUXYmNN9hcQC5y4g/hGvVksNtT8451rns2IScb7mn567ll2GNpWr9YWfvQgzWsKs8HOA/m960g6rjTzA8HAV/NHwiOmPLwDKA/J/gggYsRVgFvqbr/fpWYv90zzZKKs9Qfx00Jx65oxLAAAAABJRU5ErkJggg=="); + +// +// SET UP HELPER VARIABLES +// -------------------------------------------------- + +@border: 1px solid @border-color; + +.selection() { + background : @border-color-hover; + color : @text-color; +} + +// +// BASE STYLES +// -------------------------------------------------- + +.kint::selection { .selection() } + +.kint::-moz-selection { .selection() } + +.kint::-webkit-selection { .selection() } + +.kint, +.kint::before, +.kint::after, +.kint *, +.kint *::before, +.kint *::after { + box-sizing : border-box; + border-radius : 0; + color : @text-color; + float : none !important; + font-family : Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif; + line-height : 15px; + margin : 0; + padding : 0; + text-align : left; +} + +.kint { + font-size : 13px; + margin : @spacing * 2px 0; + overflow-x : auto; + white-space : nowrap; + + dt { + background : @main-background; + border : @border; + color : @text-color; + display : block; + font-weight : bold; + list-style : none outside none; + overflow : auto; + padding : @spacing * 1px; + + &:hover { + border-color : @border-color-hover; + } + + > var { + margin-right : 5px; + } + } + + > dl dl { + padding : 0 0 0 @spacing * 3px; + } + + // + // DROPDOWN CARET + // -------------------------------------------------- + + nav { + background : @caret-image no-repeat scroll 0 0 transparent; + cursor : pointer; + display : inline-block; + height : 15px; + width : 15px; + margin-right : 3px; + vertical-align : middle; + } + + dt.kint-parent:hover nav { + background-position : 0 -15px; + } + + dt.kint-parent.kint-show:hover > nav { + background-position : 0 -45px; + } + + dt.kint-show > nav { + background-position : 0 -30px; + } + + dt.kint-parent + dd { + display : none; + border-left : 1px dashed @border-color; + } + + dt.kint-parent.kint-show + dd { + display : block; + } + + // + // INDIVIDUAL ITEMS + // -------------------------------------------------- + + var, + var a { + color : @variable-type-color; + font-style : normal; + } + + dt:hover var, + dt:hover var a { + color : @variable-type-color-hover; + } + + dfn { + font-style : normal; + font-family : monospace; + color : @variable-name-color; + } + + pre { + color : @text-color; + margin : 0 0 0 @spacing * 3px; + padding : 5px; + overflow-y : hidden; + border-top : 0; + border : @border; + background : @main-background; + display : block; + word-break : normal; + } + + .kint-popup-trigger { + float : right !important; + cursor : pointer; + color : @border-color-hover; + + &:hover { + color : @border-color; + } + } + + dt.kint-parent > .kint-popup-trigger { + font-size : 13px; + } + + footer { + padding : 0 3px 3px; + font-size : 9px; + + > .kint-popup-trigger { + font-size : 12px; + } + + nav { + background-size : 10px; + height : 10px; + width : 10px; + + &:hover { + background-position : 0 -10px; + } + } + + > ol { + display : none; + margin-left : 32px; + } + + &.kint-show { + > ol { + display : block; + } + + nav { + background-position : 0 -20px; + + &:hover { + background-position : 0 -30px; + } + } + } + } + + a { + color : @text-color; + text-shadow : none; + + &:hover { + color : @variable-name-color; + border-bottom : 1px dotted @variable-name-color; + } + } + + // + // TABS + // -------------------------------------------------- + + ul { + list-style : none; + padding-left : @spacing * 3px; + + &:not(.kint-tabs) { + li { + border-left : 1px dashed @border-color; + + > dl { + border-left : none; + } + } + } + + &.kint-tabs { + margin : 0 0 0 @spacing * 3px; + padding-left : 0; + background : @main-background; + border : @border; + border-top : 0; + + li { + background : @secondary-background; + border : @border; + cursor : pointer; + display : inline-block; + height : @spacing * 6px; + margin : round(@spacing / 2) * 1px; + padding : 0 2px + round(@spacing * 2.5px); + vertical-align : top; + + &:hover, + &.kint-active-tab:hover { + border-color : @border-color-hover; + color : @variable-type-color-hover; + } + + &.kint-active-tab { + background : @main-background; + border-top : 0; + margin-top : -1px; + height : 27px; + line-height : 24px; + } + + &:not(.kint-active-tab) { + line-height : @spacing * 5px; + } + } + + li + li { + margin-left : 0 + } + } + + &:not(.kint-tabs) > li:not(:first-child) { + display : none; + } + } + + dt:hover + dd > ul > li.kint-active-tab { + border-color : @border-color-hover; + color : @variable-type-color-hover; + } +} + +// +// REPORT +// -------------------------------------------------- + +.kint-report { + border-collapse : collapse; + empty-cells : show; + border-spacing : 0; + + * { + font-size : 12px; + } + + dt { + background : none; + padding : (@spacing/2) * 1px; + + .kint-parent { + min-width : 100%; + overflow : hidden; + text-overflow : ellipsis; + white-space : nowrap; + } + } + + td, + th { + border : @border; + padding : (@spacing/2) * 1px; + vertical-align : center; + } + + th { + cursor : alias; + } + + td:first-child, + th { + font-weight : bold; + background : @secondary-background; + color : @variable-name-color; + } + + td { + background : @main-background; + white-space : pre; + + > dl { + padding : 0; + } + } + + pre { + border-top : 0; + border-right : 0; + } + + th:first-child { + background : none; + border : 0; + } + + td.kint-empty { + background : #d33682 !important; + } + + tr:hover { + > td { + box-shadow : 0 0 1px 0 @border-color-hover inset; + } + + var { + color : @variable-type-color-hover; + } + } + ul.kint-tabs li.kint-active-tab { + height : 20px; + line-height : 17px; + } +} + +// +// TRACE +// -------------------------------------------------- +.kint-trace { + .kint-source { + line-height : round(@spacing * 3.5) * 1px; + + span { + padding-right : 1px; + border-right : 3px inset @variable-type-color; + } + + .kint-highlight { + background : @secondary-background; + } + } + + .kint-parent { + > b { + min-width : 18px; + display : inline-block; + text-align : right; + color : @variable-name-color; + } + + > var { + > a { + color : @variable-type-color; + } + } + } +} + +// +// MISC +// -------------------------------------------------- + +// keyboard navigation caret +.kint-focused { + .keyboard-caret +} + +.kint-microtime, +.kint-color-preview { + box-shadow : 0 0 2px 0 #b6cedb; + height : 16px; + text-align : center; + text-shadow : -1px 0 #839496, 0 1px #839496, 1px 0 #839496, 0 -1px #839496; + width : 230px; + color : #fdf6e3; +} + +// mini trace +.kint footer li { + color : #ddd; +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/aante-light.css b/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/aante-light.css new file mode 100644 index 000000000..b22faddc3 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/aante-light.css @@ -0,0 +1,397 @@ +/** + * @author Ante Aljinovic https://github.com/aljinovic + */ +.kint::selection { + background: #aaaaaa; + color: #1d1e1e; +} +.kint::-moz-selection { + background: #aaaaaa; + color: #1d1e1e; +} +.kint::-webkit-selection { + background: #aaaaaa; + color: #1d1e1e; +} +.kint, +.kint::before, +.kint::after, +.kint *, +.kint *::before, +.kint *::after { + box-sizing: border-box; + border-radius: 0; + color: #1d1e1e; + float: none !important; + font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif; + line-height: 15px; + margin: 0; + padding: 0; + text-align: left; +} +.kint { + font-size: 13px; + margin: 8px 0; + overflow-x: auto; + white-space: nowrap; +} +.kint dt { + background: #f8f8f8; + border: 1px solid #d7d7d7; + color: #1d1e1e; + display: block; + font-weight: bold; + list-style: none outside none; + overflow: auto; + padding: 4px; +} +.kint dt:hover { + border-color: #aaaaaa; +} +.kint dt > var { + margin-right: 5px; +} +.kint > dl dl { + padding: 0 0 0 12px; +} +.kint nav { + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAA8CAYAAAC5OOBJAAAAvklEQVR42mNgGMFAS0uLLSQk5HxoaOh/XDg4OLgdpwEBAQEGQAN+YtMIFH/i4ODAg9cFQNMbsWkOCgrKJMv5ID5Qipko/6M7PzAw0ImkAIQ5H0hvISv0gZpP+fn5qZAVfcBAkmEYBaNZcjRLjmbJUUCvqAIlDlAiASUWkjWDkiU0eTaSpBGUEZBy1E9QRiFWLzO2LEmU80GZHkcRhN/5oGIGVNzgKIbwOx9UwOErAIl2/igYzZKjWXI0S9IHAAASJijo0Ypj8AAAAABJRU5ErkJggg==") no-repeat scroll 0 0 transparent; + cursor: pointer; + display: inline-block; + height: 15px; + width: 15px; + margin-right: 3px; + vertical-align: middle; +} +.kint dt.kint-parent:hover nav { + background-position: 0 -15px; +} +.kint dt.kint-parent.kint-show:hover > nav { + background-position: 0 -45px; +} +.kint dt.kint-show > nav { + background-position: 0 -30px; +} +.kint dt.kint-parent + dd { + display: none; + border-left: 1px dashed #d7d7d7; +} +.kint dt.kint-parent.kint-show + dd { + display: block; +} +.kint var, +.kint var a { + color: #0066ff; + font-style: normal; +} +.kint dt:hover var, +.kint dt:hover var a { + color: #ff0000; +} +.kint dfn { + font-style: normal; + font-family: monospace; + color: #1d1e1e; +} +.kint pre { + color: #1d1e1e; + margin: 0 0 0 12px; + padding: 5px; + overflow-y: hidden; + border-top: 0; + border: 1px solid #d7d7d7; + background: #f8f8f8; + display: block; + word-break: normal; +} +.kint .kint-popup-trigger { + float: right !important; + cursor: pointer; + color: #aaaaaa; +} +.kint .kint-popup-trigger:hover { + color: #d7d7d7; +} +.kint dt.kint-parent > .kint-popup-trigger { + font-size: 13px; +} +.kint footer { + padding: 0 3px 3px; + font-size: 9px; +} +.kint footer > .kint-popup-trigger { + font-size: 12px; +} +.kint footer nav { + background-size: 10px; + height: 10px; + width: 10px; +} +.kint footer nav:hover { + background-position: 0 -10px; +} +.kint footer > ol { + display: none; + margin-left: 32px; +} +.kint footer.kint-show > ol { + display: block; +} +.kint footer.kint-show nav { + background-position: 0 -20px; +} +.kint footer.kint-show nav:hover { + background-position: 0 -30px; +} +.kint a { + color: #1d1e1e; + text-shadow: none; +} +.kint a:hover { + color: #1d1e1e; + border-bottom: 1px dotted #1d1e1e; +} +.kint ul { + list-style: none; + padding-left: 12px; +} +.kint ul:not(.kint-tabs) li { + border-left: 1px dashed #d7d7d7; +} +.kint ul:not(.kint-tabs) li > dl { + border-left: none; +} +.kint ul.kint-tabs { + margin: 0 0 0 12px; + padding-left: 0; + background: #f8f8f8; + border: 1px solid #d7d7d7; + border-top: 0; +} +.kint ul.kint-tabs li { + background: #f8f8f8; + border: 1px solid #d7d7d7; + cursor: pointer; + display: inline-block; + height: 24px; + margin: 2px; + padding: 0 12px; + vertical-align: top; +} +.kint ul.kint-tabs li:hover, +.kint ul.kint-tabs li.kint-active-tab:hover { + border-color: #aaaaaa; + color: #ff0000; +} +.kint ul.kint-tabs li.kint-active-tab { + background: #f8f8f8; + border-top: 0; + margin-top: -1px; + height: 27px; + line-height: 24px; +} +.kint ul.kint-tabs li:not(.kint-active-tab) { + line-height: 20px; +} +.kint ul.kint-tabs li + li { + margin-left: 0; +} +.kint ul:not(.kint-tabs) > li:not(:first-child) { + display: none; +} +.kint dt:hover + dd > ul > li.kint-active-tab { + border-color: #aaaaaa; + color: #ff0000; +} +.kint-report { + border-collapse: collapse; + empty-cells: show; + border-spacing: 0; +} +.kint-report * { + font-size: 12px; +} +.kint-report dt { + background: none; + padding: 2px; +} +.kint-report dt .kint-parent { + min-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.kint-report td, +.kint-report th { + border: 1px solid #d7d7d7; + padding: 2px; + vertical-align: center; +} +.kint-report th { + cursor: alias; +} +.kint-report td:first-child, +.kint-report th { + font-weight: bold; + background: #f8f8f8; + color: #1d1e1e; +} +.kint-report td { + background: #f8f8f8; + white-space: pre; +} +.kint-report td > dl { + padding: 0; +} +.kint-report pre { + border-top: 0; + border-right: 0; +} +.kint-report th:first-child { + background: none; + border: 0; +} +.kint-report td.kint-empty { + background: #d33682 !important; +} +.kint-report tr:hover > td { + box-shadow: 0 0 1px 0 #aaaaaa inset; +} +.kint-report tr:hover var { + color: #ff0000; +} +.kint-report ul.kint-tabs li.kint-active-tab { + height: 20px; + line-height: 17px; +} +.kint-trace .kint-source { + line-height: 14px; +} +.kint-trace .kint-source span { + padding-right: 1px; + border-right: 3px inset #0066ff; +} +.kint-trace .kint-source .kint-highlight { + background: #f8f8f8; +} +.kint-trace .kint-parent > b { + min-width: 18px; + display: inline-block; + text-align: right; + color: #1d1e1e; +} +.kint-trace .kint-parent > var > a { + color: #0066ff; +} +.kint-focused { + box-shadow: 0 0 3px 2px #ff0000; +} +.kint-microtime, +.kint-color-preview { + box-shadow: 0 0 2px 0 #b6cedb; + height: 16px; + text-align: center; + text-shadow: -1px 0 #839496, 0 1px #839496, 1px 0 #839496, 0 -1px #839496; + width: 230px; + color: #fdf6e3; +} +.kint footer li { + color: #ddd; +} +.kint dt { + font-weight: normal; +} +.kint dt.kint-parent { + margin-top: 4px; +} +.kint > dl { + background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0, #ffffff 15px); +} +.kint dl dl { + margin-top: 4px; + padding-left: 25px; + border-left: none; +} +.kint > dl > dt { + background: #f8f8f8; +} +.kint ul { + margin: 0; + padding-left: 0; +} +.kint ul:not(.kint-tabs) > li { + border-left: 0; +} +.kint ul.kint-tabs { + background: #f8f8f8; + border: 1px solid #d7d7d7; + border-width: 0 1px 1px 1px; + padding: 4px 0 0 12px; + margin-left: -1px; + margin-top: -1px; +} +.kint ul.kint-tabs li, +.kint ul.kint-tabs li + li { + margin: 0 0 0 4px; +} +.kint ul.kint-tabs li { + border-bottom-width: 0; + height: 25px; +} +.kint ul.kint-tabs li:first-child { + margin-left: 0; +} +.kint ul.kint-tabs li.kint-active-tab { + border-top: 1px solid #d7d7d7; + background: #fff; + font-weight: bold; + padding-top: 0; + border-bottom: 1px solid #fff !important; + margin-bottom: -1px; +} +.kint ul.kint-tabs li.kint-active-tab:hover { + border-bottom: 1px solid #fff; +} +.kint ul > li > pre { + border: 1px solid #d7d7d7; +} +.kint dt:hover + dd > ul { + border-color: #aaaaaa; +} +.kint pre { + background: #fff; + margin-top: 4px; + margin-left: 25px; +} +.kint .kint-popup-trigger:hover { + color: #ff0000; +} +.kint .kint-source .kint-highlight { + background: #cfc; +} +.kint-report td { + background: #fff; +} +.kint-report td > dl { + padding: 0; + margin: 0; +} +.kint-report td > dl > dt.kint-parent { + margin: 0; +} +.kint-report td:first-child, +.kint-report td, +.kint-report th { + padding: 2px 4px; +} +.kint-report td.kint-empty { + background: #d7d7d7 !important; +} +.kint-report dd, +.kint-report dt { + background: #fff; +} +.kint-report tr:hover > td { + box-shadow: none; + background: #cfc; +} diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/kint.js b/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/kint.js new file mode 100644 index 000000000..119518824 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/kint.js @@ -0,0 +1,10 @@ +(function(){if("undefined"===typeof kintInitialized){kintInitialized=1;var e=[],f=-1,g=function(a){var b=window.getSelection(),c=document.createRange();c.selectNodeContents(a);b.removeAllRanges();b.addRange(c)},h=function(a){Array.prototype.slice.call(document.querySelectorAll(".kint nav, .kint-tabs>li:not(.kint-active-tab)"),0).forEach(a)},k=function(a,b){if(!a.classList)return!1;"undefined"===typeof b&&(b="kint-show");return a.classList.contains(b)},l=function(a,b){"undefined"===typeof b&&(b="kint-show");a.classList.add(b)}, +m=function(a,b){"undefined"===typeof b&&(b="kint-show");a.classList.remove(b)},n=function(a){do a=a.nextElementSibling;while("dd"!==a.nodeName.toLowerCase());return a},q=function(a,b){var c=n(a);"undefined"===typeof b&&(b=k(a));b?m(a):l(a);1===c.childNodes.length&&(c=c.childNodes[0].childNodes[0])&&k(c,"kint-parent")&&q(c,b)},r=function(a,b){var c=n(a).getElementsByClassName("kint-parent"),d=c.length;for("undefined"===typeof b&&(b=k(a));d--;)q(c[d],b);q(a,b)},s=function(a){var b=a,c=0;a.parentNode.getElementsByClassName("kint-active-tab")[0].className= +"";for(a.className="kint-active-tab";b=b.previousSibling;)1===b.nodeType&&c++;a=a.parentNode.nextSibling.childNodes;for(var d=0;dKint ("+(new Date).toISOString()+')'+document.getElementsByClassName("-kint-js")[0].outerHTML+document.getElementsByClassName("-kint-css")[0].outerHTML+'
'+a.parentNode.outerHTML+"
"),b.document.close()},w=function(a,b){function c(a){var c=1===b?a.replace(/^#/,""):a;if(isNaN(c))return a.trim().toLocaleLowerCase();c=parseFloat(c);return isNaN(c)? +a.trim():c}var d=a.tBodies[0];[].slice.call(a.tBodies[0].rows).sort(function(a,d){a=c(a.cells[b].textContent);d=c(d.cells[b].textContent);return ad?1:0}).forEach(function(a){d.appendChild(a)})},x=function(a){var b=document.querySelector(".kint-focused");b&&m(b,"kint-focused");if(-1!==a){b=e[a];l(b,"kint-focused");var c=function(a){return a.offsetTop+(a.offsetParent?c(a.offsetParent):0)};window.scrollTo(0,c(b)-window.innerHeight/2)}f=a},y=function(a,b){a?0>--b&&(b=e.length-1):++b>=e.length&& +(b=0);x(b);return!1};window.addEventListener("click",function(a){var b=a.target,c=b.nodeName.toLowerCase();if(t(b)){if("dfn"===c)g(b),b=b.parentNode;else if("var"===c)b=b.parentNode,c=b.nodeName.toLowerCase();else if("th"===c)return a.ctrlKey||w(b.parentNode.parentNode.parentNode,b.cellIndex),!1;if("li"===c&&"kint-tabs"===b.parentNode.className)return"kint-active-tab"!==b.className&&(s(b),-1!==f&&u()),!1;if("nav"===c)return"footer"===b.parentNode.nodeName.toLowerCase()?(b=b.parentNode,k(b)?m(b):l(b)): +setTimeout(function(){0a&&(d=a);pvar{margin-right:5px}.kint>dl dl{padding:0 0 0 12px}.kint nav{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAA8CAMAAACO5hB7AAAACVBMVEX///82b4tctzBls0NMAAAAQklEQVR42rWRIQ4AAAgCwf8/2qgER2BeuwJsgAqIzekMfMryPL9XQSkobE6vwKcsz/N7FfPvh09lnPf/b+7673+f0uHuAZ/EdkNQAAAAAElFTkSuQmCC") no-repeat scroll 0 0 transparent;cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint dt.kint-parent:hover nav{background-position:0 -15px}.kint dt.kint-parent.kint-show:hover>nav{background-position:0 -45px}.kint dt.kint-show>nav{background-position:0 -30px}.kint dt.kint-parent+dd{display:none;border-left:1px dashed #b6cedb}.kint dt.kint-parent.kint-show+dd{display:block}.kint var,.kint var a{color:#0092db;font-style:normal}.kint dt:hover var,.kint dt:hover var a{color:#5cb730}.kint dfn{font-style:normal;font-family:monospace;color:#1d1e1e}.kint pre{color:#1d1e1e;margin:0 0 0 12px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #b6cedb;background:#e0eaef;display:block;word-break:normal}.kint .kint-popup-trigger{float:right !important;cursor:pointer;color:#0092db}.kint .kint-popup-trigger:hover{color:#b6cedb}.kint dt.kint-parent>.kint-popup-trigger{font-size:13px}.kint footer{padding:0 3px 3px;font-size:9px}.kint footer>.kint-popup-trigger{font-size:12px}.kint footer nav{background-size:10px;height:10px;width:10px}.kint footer nav:hover{background-position:0 -10px}.kint footer>ol{display:none;margin-left:32px}.kint footer.kint-show>ol{display:block}.kint footer.kint-show nav{background-position:0 -20px}.kint footer.kint-show nav:hover{background-position:0 -30px}.kint a{color:#1d1e1e;text-shadow:none}.kint a:hover{color:#1d1e1e;border-bottom:1px dotted #1d1e1e}.kint ul{list-style:none;padding-left:12px}.kint ul:not(.kint-tabs) li{border-left:1px dashed #b6cedb}.kint ul:not(.kint-tabs) li>dl{border-left:none}.kint ul.kint-tabs{margin:0 0 0 12px;padding-left:0;background:#e0eaef;border:1px solid #b6cedb;border-top:0}.kint ul.kint-tabs li{background:#c1d4df;border:1px solid #b6cedb;cursor:pointer;display:inline-block;height:24px;margin:2px;padding:0 12px;vertical-align:top}.kint ul.kint-tabs li:hover,.kint ul.kint-tabs li.kint-active-tab:hover{border-color:#0092db;color:#5cb730}.kint ul.kint-tabs li.kint-active-tab{background:#e0eaef;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint ul.kint-tabs li:not(.kint-active-tab){line-height:20px}.kint ul.kint-tabs li+li{margin-left:0}.kint ul:not(.kint-tabs)>li:not(:first-child){display:none}.kint dt:hover+dd>ul>li.kint-active-tab{border-color:#0092db;color:#5cb730}.kint-report{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-report *{font-size:12px}.kint-report dt{background:none;padding:2px}.kint-report dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-report td,.kint-report th{border:1px solid #b6cedb;padding:2px;vertical-align:center}.kint-report th{cursor:alias}.kint-report td:first-child,.kint-report th{font-weight:bold;background:#c1d4df;color:#1d1e1e}.kint-report td{background:#e0eaef;white-space:pre}.kint-report td>dl{padding:0}.kint-report pre{border-top:0;border-right:0}.kint-report th:first-child{background:none;border:0}.kint-report td.kint-empty{background:#d33682 !important}.kint-report tr:hover>td{box-shadow:0 0 1px 0 #0092db inset}.kint-report tr:hover var{color:#5cb730}.kint-report ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-trace .kint-source{line-height:14px}.kint-trace .kint-source span{padding-right:1px;border-right:3px inset #0092db}.kint-trace .kint-source .kint-highlight{background:#c1d4df}.kint-trace .kint-parent>b{min-width:18px;display:inline-block;text-align:right;color:#1d1e1e}.kint-trace .kint-parent>var>a{color:#0092db}.kint-focused{box-shadow:0 0 3px 2px #5cb730}.kint-microtime,.kint-color-preview{box-shadow:0 0 2px 0 #b6cedb;height:16px;text-align:center;text-shadow:-1px 0 #839496,0 1px #839496,1px 0 #839496,0 -1px #839496;width:230px;color:#fdf6e3}.kint footer li{color:#ddd}.kint>dl>dt{background:linear-gradient(to bottom, #e3ecf0 0, #c0d4df 100%)}.kint ul.kint-tabs{background:linear-gradient(to bottom, #9dbed0 0, #b2ccda 100%)}.kint>dl:not(.kint-trace)>dd>ul.kint-tabs li{background:#e0eaef}.kint>dl:not(.kint-trace)>dd>ul.kint-tabs li.kint-active-tab{background:#c1d4df}.kint>dl.kint-trace>dt{background:linear-gradient(to bottom, #c0d4df 0, #e3ecf0 100%)}.kint .kint-source .kint-highlight{background:#f0eb96} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/solarized-dark.css b/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/solarized-dark.css new file mode 100644 index 000000000..d7c68abaa --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/solarized-dark.css @@ -0,0 +1 @@ +.kint::selection{background:#268bd2;color:#839496}.kint::-moz-selection{background:#268bd2;color:#839496}.kint::-webkit-selection{background:#268bd2;color:#839496}.kint,.kint::before,.kint::after,.kint *,.kint *::before,.kint *::after{box-sizing:border-box;border-radius:0;color:#839496;float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint{font-size:13px;margin:10px 0;overflow-x:auto;white-space:nowrap}.kint dt{background:#002b36;border:1px solid #586e75;color:#839496;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:5px}.kint dt:hover{border-color:#268bd2}.kint dt>var{margin-right:5px}.kint>dl dl{padding:0 0 0 15px}.kint nav{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAA8CAMAAACO5hB7AAAA7VBMVEVYbnWToaH///9YbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnWToaGToaGToaGToaGToaGToaGToaGToaGToaFYbnWToaFYbnVYbnWToaGToaFYbnWToaFYbnWToaFYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnWToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaFYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnWToaGToaGToaGToaGToaGToaGToaGToaGToaFYbnWToaEeRtj1AAAATXRSTlMAAACLab0B7iP2R7CL9iPuaUewAb36F0/QnPoX0JxPYQaurepi6QV8FhV7MzFhM6586RYFMa3qYnsVBpNQ+ZGOGJ3PkM9QkZAY+Y6TnV7gnYsAAAEcSURBVHhebdHVbgMxEEBRu8sUZmqapBBoSuGUmWb+/3NayxtrLOe+Hck745XZgd6/eb3CVcKt7HmR+gKxlCGOETG5vFbOoahR6NxI5zHt6nuq+2dqnqfzzH3mfcz70vb8H6MJt5u6a96hS30E4PjEFgAEp2fKNojKUfVEOoS044ex7u3YPE/nmfvM+5j3pe17T9oe/77O41w+n4vnrbrwZxbTshVhvtx5Kb8vliRLRWmeSQSTjJq/El5x5fUXYmNN9hcQC5y4g/hGvVksNtT8451rns2IScb7mn567ll2GNpWr9YWfvQgzWsKs8HOA/m960g6rjTzA8HAV/NHwiOmPLwDKA/J/gggYsRVgFvqbr/fpWYv90zzZKKs9Qfx00Jx65oxLAAAAABJRU5ErkJggg==") no-repeat scroll 0 0 transparent;cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint dt.kint-parent:hover nav{background-position:0 -15px}.kint dt.kint-parent.kint-show:hover>nav{background-position:0 -45px}.kint dt.kint-show>nav{background-position:0 -30px}.kint dt.kint-parent+dd{display:none;border-left:1px dashed #586e75}.kint dt.kint-parent.kint-show+dd{display:block}.kint var,.kint var a{color:#268bd2;font-style:normal}.kint dt:hover var,.kint dt:hover var a{color:#2aa198}.kint dfn{font-style:normal;font-family:monospace;color:#93a1a1}.kint pre{color:#839496;margin:0 0 0 15px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #586e75;background:#002b36;display:block;word-break:normal}.kint .kint-popup-trigger{float:right !important;cursor:pointer;color:#268bd2}.kint .kint-popup-trigger:hover{color:#586e75}.kint dt.kint-parent>.kint-popup-trigger{font-size:13px}.kint footer{padding:0 3px 3px;font-size:9px}.kint footer>.kint-popup-trigger{font-size:12px}.kint footer nav{background-size:10px;height:10px;width:10px}.kint footer nav:hover{background-position:0 -10px}.kint footer>ol{display:none;margin-left:32px}.kint footer.kint-show>ol{display:block}.kint footer.kint-show nav{background-position:0 -20px}.kint footer.kint-show nav:hover{background-position:0 -30px}.kint a{color:#839496;text-shadow:none}.kint a:hover{color:#93a1a1;border-bottom:1px dotted #93a1a1}.kint ul{list-style:none;padding-left:15px}.kint ul:not(.kint-tabs) li{border-left:1px dashed #586e75}.kint ul:not(.kint-tabs) li>dl{border-left:none}.kint ul.kint-tabs{margin:0 0 0 15px;padding-left:0;background:#002b36;border:1px solid #586e75;border-top:0}.kint ul.kint-tabs li{background:#073642;border:1px solid #586e75;cursor:pointer;display:inline-block;height:30px;margin:3px;padding:0 15px;vertical-align:top}.kint ul.kint-tabs li:hover,.kint ul.kint-tabs li.kint-active-tab:hover{border-color:#268bd2;color:#2aa198}.kint ul.kint-tabs li.kint-active-tab{background:#002b36;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint ul.kint-tabs li:not(.kint-active-tab){line-height:25px}.kint ul.kint-tabs li+li{margin-left:0}.kint ul:not(.kint-tabs)>li:not(:first-child){display:none}.kint dt:hover+dd>ul>li.kint-active-tab{border-color:#268bd2;color:#2aa198}.kint-report{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-report *{font-size:12px}.kint-report dt{background:none;padding:2.5px}.kint-report dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-report td,.kint-report th{border:1px solid #586e75;padding:2.5px;vertical-align:top}.kint-report th{cursor:alias}.kint-report td{background:#002b36;white-space:pre}.kint-report td>dl{padding:0}.kint-report pre{border-top:0;border-right:0}.kint-report th:first-child{background:none;border:0}.kint-report td:first-child,.kint-report th{font-weight:bold;background:#073642;color:#93a1a1;padding:2.5px 0}.kint-report td.kint-empty{background:#d33682 !important}.kint-report tr:hover>td{box-shadow:0 0 1px 0 #268bd2 inset}.kint-report tr:hover var{color:#2aa198}.kint-report ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-trace .kint-source{line-height:18px}.kint-trace .kint-source span{padding-right:1px;border-right:3px inset #268bd2}.kint-trace .kint-source .kint-highlight{background:#073642}.kint-trace .kint-parent>b{min-width:18px;display:inline-block;text-align:right;color:#93a1a1}.kint-trace .kint-parent>var>a{color:#268bd2}.kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-microtime,.kint-color-preview{box-shadow:0 0 2px 0 #b6cedb;height:16px;text-align:center;text-shadow:-1px 0 #839496,0 1px #839496,1px 0 #839496,0 -1px #839496;width:230px;color:#fdf6e3}.kint footer li{color:#ddd}body{background:#073642;color:#fff}.kint{background:#073642;box-shadow:0 0 5px 3px #073642}.kint>dl>dt,.kint ul.kint-tabs{box-shadow:4px 0 2px -3px #268bd2 inset}.kint ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/solarized.css b/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/solarized.css new file mode 100644 index 000000000..118daf36b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/compiled/solarized.css @@ -0,0 +1 @@ +.kint::selection{background:#268bd2;color:#657b83}.kint::-moz-selection{background:#268bd2;color:#657b83}.kint::-webkit-selection{background:#268bd2;color:#657b83}.kint,.kint::before,.kint::after,.kint *,.kint *::before,.kint *::after{box-sizing:border-box;border-radius:0;color:#657b83;float:none !important;font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;line-height:15px;margin:0;padding:0;text-align:left}.kint{font-size:13px;margin:10px 0;overflow-x:auto;white-space:nowrap}.kint dt{background:#fdf6e3;border:1px solid #93a1a1;color:#657b83;display:block;font-weight:bold;list-style:none outside none;overflow:auto;padding:5px}.kint dt:hover{border-color:#268bd2}.kint dt>var{margin-right:5px}.kint>dl dl{padding:0 0 0 15px}.kint nav{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAA8CAMAAACO5hB7AAAA7VBMVEVYbnWToaH///9YbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnWToaGToaGToaGToaGToaGToaGToaGToaGToaFYbnWToaFYbnVYbnWToaGToaFYbnWToaFYbnWToaFYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnWToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaGToaFYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnVYbnWToaGToaGToaGToaGToaGToaGToaGToaGToaFYbnWToaEeRtj1AAAATXRSTlMAAACLab0B7iP2R7CL9iPuaUewAb36F0/QnPoX0JxPYQaurepi6QV8FhV7MzFhM6586RYFMa3qYnsVBpNQ+ZGOGJ3PkM9QkZAY+Y6TnV7gnYsAAAEcSURBVHhebdHVbgMxEEBRu8sUZmqapBBoSuGUmWb+/3NayxtrLOe+Hck745XZgd6/eb3CVcKt7HmR+gKxlCGOETG5vFbOoahR6NxI5zHt6nuq+2dqnqfzzH3mfcz70vb8H6MJt5u6a96hS30E4PjEFgAEp2fKNojKUfVEOoS044ex7u3YPE/nmfvM+5j3pe17T9oe/77O41w+n4vnrbrwZxbTshVhvtx5Kb8vliRLRWmeSQSTjJq/El5x5fUXYmNN9hcQC5y4g/hGvVksNtT8451rns2IScb7mn567ll2GNpWr9YWfvQgzWsKs8HOA/m960g6rjTzA8HAV/NHwiOmPLwDKA/J/gggYsRVgFvqbr/fpWYv90zzZKKs9Qfx00Jx65oxLAAAAABJRU5ErkJggg==") no-repeat scroll 0 0 transparent;cursor:pointer;display:inline-block;height:15px;width:15px;margin-right:3px;vertical-align:middle}.kint dt.kint-parent:hover nav{background-position:0 -15px}.kint dt.kint-parent.kint-show:hover>nav{background-position:0 -45px}.kint dt.kint-show>nav{background-position:0 -30px}.kint dt.kint-parent+dd{display:none;border-left:1px dashed #93a1a1}.kint dt.kint-parent.kint-show+dd{display:block}.kint var,.kint var a{color:#268bd2;font-style:normal}.kint dt:hover var,.kint dt:hover var a{color:#2aa198}.kint dfn{font-style:normal;font-family:monospace;color:#586e75}.kint pre{color:#657b83;margin:0 0 0 15px;padding:5px;overflow-y:hidden;border-top:0;border:1px solid #93a1a1;background:#fdf6e3;display:block;word-break:normal}.kint .kint-popup-trigger{float:right !important;cursor:pointer;color:#268bd2}.kint .kint-popup-trigger:hover{color:#93a1a1}.kint dt.kint-parent>.kint-popup-trigger{font-size:13px}.kint footer{padding:0 3px 3px;font-size:9px}.kint footer>.kint-popup-trigger{font-size:12px}.kint footer nav{background-size:10px;height:10px;width:10px}.kint footer nav:hover{background-position:0 -10px}.kint footer>ol{display:none;margin-left:32px}.kint footer.kint-show>ol{display:block}.kint footer.kint-show nav{background-position:0 -20px}.kint footer.kint-show nav:hover{background-position:0 -30px}.kint a{color:#657b83;text-shadow:none}.kint a:hover{color:#586e75;border-bottom:1px dotted #586e75}.kint ul{list-style:none;padding-left:15px}.kint ul:not(.kint-tabs) li{border-left:1px dashed #93a1a1}.kint ul:not(.kint-tabs) li>dl{border-left:none}.kint ul.kint-tabs{margin:0 0 0 15px;padding-left:0;background:#fdf6e3;border:1px solid #93a1a1;border-top:0}.kint ul.kint-tabs li{background:#eee8d5;border:1px solid #93a1a1;cursor:pointer;display:inline-block;height:30px;margin:3px;padding:0 15px;vertical-align:top}.kint ul.kint-tabs li:hover,.kint ul.kint-tabs li.kint-active-tab:hover{border-color:#268bd2;color:#2aa198}.kint ul.kint-tabs li.kint-active-tab{background:#fdf6e3;border-top:0;margin-top:-1px;height:27px;line-height:24px}.kint ul.kint-tabs li:not(.kint-active-tab){line-height:25px}.kint ul.kint-tabs li+li{margin-left:0}.kint ul:not(.kint-tabs)>li:not(:first-child){display:none}.kint dt:hover+dd>ul>li.kint-active-tab{border-color:#268bd2;color:#2aa198}.kint-report{border-collapse:collapse;empty-cells:show;border-spacing:0}.kint-report *{font-size:12px}.kint-report dt{background:none;padding:2.5px}.kint-report dt .kint-parent{min-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kint-report td,.kint-report th{border:1px solid #93a1a1;padding:2.5px;vertical-align:top}.kint-report th{cursor:alias}.kint-report td{background:#fdf6e3;white-space:pre}.kint-report td>dl{padding:0}.kint-report pre{border-top:0;border-right:0}.kint-report th:first-child{background:none;border:0}.kint-report td:first-child,.kint-report th{font-weight:bold;background:#eee8d5;color:#586e75;padding:2.5px 0}.kint-report td.kint-empty{background:#d33682 !important}.kint-report tr:hover>td{box-shadow:0 0 1px 0 #268bd2 inset}.kint-report tr:hover var{color:#2aa198}.kint-report ul.kint-tabs li.kint-active-tab{height:20px;line-height:17px}.kint-trace .kint-source{line-height:18px}.kint-trace .kint-source span{padding-right:1px;border-right:3px inset #268bd2}.kint-trace .kint-source .kint-highlight{background:#eee8d5}.kint-trace .kint-parent>b{min-width:18px;display:inline-block;text-align:right;color:#586e75}.kint-trace .kint-parent>var>a{color:#268bd2}.kint-focused{box-shadow:0 0 3px 2px #859900 inset;border-radius:7px}.kint-microtime,.kint-color-preview{box-shadow:0 0 2px 0 #b6cedb;height:16px;text-align:center;text-shadow:-1px 0 #839496,0 1px #839496,1px 0 #839496,0 -1px #839496;width:230px;color:#fdf6e3}.kint footer li{color:#ddd}.kint>dl>dt,.kint ul.kint-tabs{box-shadow:4px 0 2px -3px #268bd2 inset}.kint ul.kint-tabs li.kint-active-tab{padding-top:7px;height:34px} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/aante-light.less b/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/aante-light.less new file mode 100644 index 000000000..f342d757d --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/aante-light.less @@ -0,0 +1,154 @@ +/** + * @author Ante Aljinovic https://github.com/aljinovic + */ +@import "../base"; + +@main-background: #f8f8f8; +@secondary-background : #f8f8f8; + +@text-color: #1d1e1e; +@variable-name-color: #1d1e1e; +@variable-type-color: #06f; +@variable-type-color-hover: #f00; + +@border-color: #d7d7d7; +@border-color-hover: #aaa; + +@caret-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAA8CAYAAAC5OOBJAAAAvklEQVR42mNgGMFAS0uLLSQk5HxoaOh/XDg4OLgdpwEBAQEGQAN+YtMIFH/i4ODAg9cFQNMbsWkOCgrKJMv5ID5Qipko/6M7PzAw0ImkAIQ5H0hvISv0gZpP+fn5qZAVfcBAkmEYBaNZcjRLjmbJUUCvqAIlDlAiASUWkjWDkiU0eTaSpBGUEZBy1E9QRiFWLzO2LEmU80GZHkcRhN/5oGIGVNzgKIbwOx9UwOErAIl2/igYzZKjWXI0S9IHAAASJijo0Ypj8AAAAABJRU5ErkJggg=="); + +.keyboard-caret() { + box-shadow : 0 0 3px 2px @variable-type-color-hover; +} + +.kint { + dt { + font-weight : normal; + + &.kint-parent { + margin-top : 4px; + } + } + + > dl { + background : linear-gradient(90deg, rgba(255,255,255,0) 0, #fff 15px); + } + + dl dl { + margin-top : 4px; + padding-left : 25px; + border-left : none; + } + + > dl > dt { + background : @secondary-background; + } + + // + // TABS + // -------------------------------------------------- + + ul { + margin : 0; + padding-left : 0; + + &:not(.kint-tabs) > li { + border-left : 0; + } + + &.kint-tabs { + background : @secondary-background; + border : @border; + border-width : 0 1px 1px 1px; + padding : 4px 0 0 12px; + margin-left : -1px; + margin-top : -1px; + + li, + li + li { + margin : 0 0 0 4px; + } + + li { + border-bottom-width : 0; + height : @spacing * 6px + 1px; + + + &:first-child { + margin-left : 0; + } + + &.kint-active-tab { + border-top : @border; + background : #fff; + font-weight : bold; + padding-top : 0; + border-bottom : 1px solid #fff !important; + margin-bottom : -1px; + } + + &.kint-active-tab:hover { + border-bottom : 1px solid #fff; + } + } + } + + > li > pre { + border : @border; + } + } + + dt:hover + dd > ul { + border-color : @border-color-hover; + } + + pre { + background : #fff; + margin-top : 4px; + margin-left : 25px; + } + + .kint-popup-trigger:hover { + color : @variable-type-color-hover; + } + + .kint-source .kint-highlight { + background : #cfc; + } +} + +// +// REPORT +// -------------------------------------------------- + +.kint-report { + td { + background : #fff; + + > dl { + padding : 0; + margin : 0; + > dt.kint-parent { + margin: 0; + } + } + } + + td:first-child, + td, + th { + padding : 2px 4px; + } + + td.kint-empty { + background : @border-color !important; + } + + dd, dt { + background: #fff; + } + + tr:hover > td { + box-shadow : none; + background : #cfc; + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/original.less b/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/original.less new file mode 100644 index 000000000..839a9de47 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/original.less @@ -0,0 +1,43 @@ +@import "../base"; + +@main-background: #e0eaef; +@secondary-background: #c1d4df; + +@text-color: #1d1e1e; +@variable-name-color: #1d1e1e; +@variable-type-color: #0092db; +@variable-type-color-hover: #5cb730; + +@border-color: #b6cedb; +@border-color-hover: #0092db; + +@caret-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAA8CAMAAACO5hB7AAAACVBMVEX///82b4tctzBls0NMAAAAQklEQVR42rWRIQ4AAAgCwf8/2qgER2BeuwJsgAqIzekMfMryPL9XQSkobE6vwKcsz/N7FfPvh09lnPf/b+7673+f0uHuAZ/EdkNQAAAAAElFTkSuQmCC"); + +.keyboard-caret() { + box-shadow : 0 0 3px 2px #5cb730; +} + +.kint { + > dl > dt { + background : linear-gradient(to bottom, #e3ecf0 0, #c0d4df 100%); + } + + ul.kint-tabs { + background : linear-gradient(to bottom, #9dbed0 0px, #b2ccda 100%); + } + + & > dl:not(.kint-trace) > dd > ul.kint-tabs li { + background : @main-background; + &.kint-active-tab { + background : @secondary-background; + } + } + + & > dl.kint-trace > dt { + background : linear-gradient(to bottom, #c0d4df 0px, #e3ecf0 100%); + } + + .kint-source .kint-highlight { + background : #f0eb96; + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/solarized-dark.less b/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/solarized-dark.less new file mode 100644 index 000000000..f8467e13a --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/solarized-dark.less @@ -0,0 +1,39 @@ +@import "../base"; + +@spacing : 5; + +@main-background: #002b36; // base 03 +@secondary-background : #073642; // base 02 + +@text-color: #839496; // base 0 +@variable-name-color: #93a1a1; // base 1 +@variable-type-color: #268bd2; // blue +@variable-type-color-hover: #2aa198; // cyan + +@border-color: #586e75; // base 01 +@border-color-hover: #268bd2; // blue + +.keyboard-caret() { + box-shadow : 0 0 3px 2px #859900 inset; // green + border-radius : 7px; +} + +body { + background : @secondary-background; + color : #fff; // for non-kint elements to remain at least semi-readable +} + +.kint { + background : @secondary-background; + box-shadow : 0 0 5px 3px @secondary-background; + + > dl > dt, + ul.kint-tabs { + box-shadow : 4px 0 2px -3px @variable-type-color inset; + } +} + +.kint ul.kint-tabs li.kint-active-tab { + padding-top: 7px; + height: 34px; +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/solarized.less b/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/solarized.less new file mode 100644 index 000000000..ded8748ba --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/kint/view/themes/solarized.less @@ -0,0 +1,29 @@ +@import "../base"; + +@spacing : 5; + +@main-background: #fdf6e3; // base 3 +@secondary-background : #eee8d5; // base 2 + +@text-color: #657b83; // base 00 +@variable-name-color: #586e75; // base 01 +@variable-type-color: #268bd2; // blue +@variable-type-color-hover: #2aa198; // cyan + +@border-color: #93a1a1; // base 1 +@border-color-hover: #268bd2; // blue + +.keyboard-caret() { + box-shadow : 0 0 3px 2px #859900 inset; // green + border-radius : 7px; +} + +.kint > dl > dt, +.kint ul.kint-tabs { + box-shadow : 4px 0 2px -3px @variable-type-color inset; +} + +.kint ul.kint-tabs li.kint-active-tab { + padding-top: 7px; + height: 34px; +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/kint/src/Plugin/Devel/Dumper/Kint.php b/sites/all/modules/contrib/dev/devel/kint/src/Plugin/Devel/Dumper/Kint.php new file mode 100644 index 000000000..9ee2de409 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/src/Plugin/Devel/Dumper/Kint.php @@ -0,0 +1,58 @@ +Kint debugging tool."), + * ) + */ +class Kint extends DevelDumperBase { + + /** + * {@inheritdoc} + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + // @TODO find another solution for kint class inclusion! + kint_require(); + } + + /** + * {@inheritdoc} + */ + public function dump($input, $name = NULL) { + echo (string) $this->export($input, $name); + } + + /** + * {@inheritdoc} + */ + public function export($input, $name = NULL) { + ob_start(); + \Kint::dump($input); + $dump = ob_get_clean(); + + // Kint does't allow to assign a title to the dump. Workaround to use the + // passed in name as dump title. + if ($name) { + $dump = preg_replace('/\$input/', $name, $dump, 1); + } + + return $this->setSafeMarkup($dump); + } + + /** + * {@inheritdoc} + */ + public static function checkRequirements() { + return kint_require(); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/kint/src/Twig/KintExtension.php b/sites/all/modules/contrib/dev/devel/kint/src/Twig/KintExtension.php new file mode 100644 index 000000000..4f177f9bf --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/kint/src/Twig/KintExtension.php @@ -0,0 +1,154 @@ + array('html'), + 'needs_environment' => TRUE, + 'needs_context' => TRUE, + 'is_variadic' => TRUE, + )), + ); + } + + /** + * Provides Kint function to Twig templates. + * + * Handles 0, 1, or multiple arguments. + * + * Code derived from https://github.com/barelon/CgKintBundle. + * + * @param \Twig_Environment $env + * The twig environment instance. + * @param array $context + * An array of parameters passed to the template. + * @param array $args + * An array of parameters passed the function. + * + * @return string + * String representation of the input variables. + */ + public function kint(\Twig_Environment $env, array $context, array $args = []) { + // Don't do anything unless twig_debug is enabled. This reads from the Twig + // environment, not Drupal Settings, so a container rebuild is necessary + // when toggling twig_debug on and off. We can consider injecting Settings. + if (!$env->isDebug()) { + return; + } + kint_require(); + // Don't display where Kint was called from. + // @todo Can we add information about which template Kint was called from? + \Kint::$displayCalledFrom = FALSE; + + // No arguments passed to kint(), display full Twig context. + if (empty($args)) { + $kint_variable = array(); + foreach ($context as $key => $value) { + if (!$value instanceof \Twig_Template) { + $kint_variable[$key] = $value; + } + } + + $result = @\Kint::dump($kint_variable); + $output = str_replace('$kint_variable', 'Twig context', $result); + } + else { + // Try to get the names of variables from the Twig template. + $parameters = $this->getTwigFunctionParameters(); + + // If there is only one argument, pass to Kint without too much hassle. + if (count($args) == 1) { + $kint_variable = reset($args); + $variable_name = reset($parameters); + $result = @\Kint::dump($kint_variable); + // Replace $kint_variable with the name of the variable in the Twig + // template. + $output = str_replace('$kint_variable', $variable_name, $result); + } + else { + $kint_args = []; + // Build an array of variable to pass to Kint. + // @todo Can we just call_user_func_array while still retaining the + // variable names? + foreach ($args as $index => $arg) { + // Prepend a unique index to allow debugging the same variable more + // than once in the same Kint dump. + $name = !empty($parameters[$index]) ? $parameters[$index] : $index; + $kint_args['_index_' . $index . '_' . $name] = $arg; + } + + $result = @\Kint::dump($kint_args); + // Display a comma separated list of the variables contained in this group. + $output = str_replace('$kint_args', implode(', ', $parameters), $result); + // Remove unique indexes from output. + $output = preg_replace('/_index_([0-9]+)_/', '', $output); + } + } + + return $output; + } + + /** + * Gets the twig function parameters for the current invocation. + * + * @return array + * The detected twig function parameters. + */ + protected function getTwigFunctionParameters() { + $callee = NULL; + $template = NULL; + + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); + + foreach ($backtrace as $index => $trace) { + if (isset($trace['object']) && $trace['object'] instanceof \Twig_Template && 'Twig_Template' !== get_class($trace['object'])) { + $template = $trace['object']; + $callee = $backtrace[$index - 1]; + break; + } + } + + $parameters = []; + + /** @var \Twig_Template $template */ + if (NULL !== $template && NULL !== $callee) { + $line_number = $callee['line']; + $debug_infos = $template->getDebugInfo(); + + if (isset($debug_infos[$line_number])) { + $source_line = $debug_infos[$line_number]; + $source_file_name = $template->getTemplateName(); + + if (is_readable($source_file_name)) { + $source = file($source_file_name, FILE_IGNORE_NEW_LINES); + $line = $source[$source_line - 1]; + + preg_match('/kint\((.+)\)/', $line, $matches); + if (isset($matches[1])) { + $parameters = array_map('trim', explode(',', $matches[1])); + } + } + } + } + + return $parameters; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/run-tests-drush.sh b/sites/all/modules/contrib/dev/devel/run-tests-drush.sh new file mode 100755 index 000000000..4dcd4fb4b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/run-tests-drush.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env sh + +# This script will run phpunit-based test classes using Drush's +# test framework. First, the Drush executable is located, and +# then phpunit is invoked, pointing to Drush's phpunit.xml as +# the configuration. +# +# Any parameters that may be passed to `phpunit` may also be used +# with this script. + +DRUSH_PATH="`which drush`" +DRUSH_DIRNAME="`dirname -- "$DRUSH_PATH"`" +# The following line is needed is you use a `drush` that differs from `which drush` +# export UNISH_DRUSH=$DRUSH_PATH + +if [ $# = 0 ] ; then + phpunit --configuration="$DRUSH_DIRNAME/tests" drush +else + # Pass along any arguments. + phpunit --configuration="$DRUSH_DIRNAME/tests" $@ +fi diff --git a/sites/all/modules/contrib/dev/devel/src/Annotation/DevelDumper.php b/sites/all/modules/contrib/dev/devel/src/Annotation/DevelDumper.php new file mode 100644 index 000000000..582aace5b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Annotation/DevelDumper.php @@ -0,0 +1,44 @@ +redirect(''); + } + + /** + * Returns a dump of a route object. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * Page request object. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. + * + * @return array + * A render array containing the route object. + */ + public function menuItem(Request $request, RouteMatchInterface $route_match) { + $output = []; + + // Get the route object from the path query string if available. + if ($path = $request->query->get('path')) { + try { + /* @var \Symfony\Cmf\Component\Routing\ChainRouter $router */ + $router = \Drupal::service('router'); + $route = $router->match($path); + $output['route'] = ['#markup' => kpr($route, TRUE)]; + } + catch (\Exception $e) { + drupal_set_message($this->t("Unable to load route for url '%url'", ['%url' => $path]), 'warning'); + } + } + // No path specified, get the current route. + else { + $route = $route_match->getRouteObject(); + $output['route'] = ['#markup' => kpr($route, TRUE)]; + } + + return $output; + } + + public function themeRegistry() { + $hooks = theme_get_registry(); + ksort($hooks); + return array('#markup' => kprint_r($hooks, TRUE)); + } + + /** + * Builds the elements info overview page. + * + * @return array + * Array of page elements to render. + */ + public function elementsPage() { + $element_info_manager = \Drupal::service('element_info'); + + $elements_info = array(); + foreach ($element_info_manager->getDefinitions() as $element_type => $definition) { + $elements_info[$element_type] = $definition + $element_info_manager->getInfo($element_type); + } + + ksort($elements_info); + + return array('#markup' => kdevel_print_object($elements_info)); + } + + /** + * Builds the fields info overview page. + * + * @return array + * Array of page elements to render. + */ + public function fieldInfoPage() { + $fields = FieldStorageConfig::loadMultiple(); + ksort($fields); + $output['fields'] = array('#markup' => kprint_r($fields, TRUE, $this->t('Fields'))); + + $field_instances = FieldConfig::loadMultiple(); + ksort($field_instances); + $output['instances'] = array('#markup' => kprint_r($field_instances, TRUE, $this->t('Instances'))); + + $bundles = \Drupal::service('entity_type.bundle.info')->getAllBundleInfo(); + ksort($bundles); + $output['bundles'] = array('#markup' => kprint_r($bundles, TRUE, $this->t('Bundles'))); + + $field_types = \Drupal::service('plugin.manager.field.field_type')->getUiDefinitions(); + ksort($field_types); + $output['field_types'] = array('#markup' => kprint_r($field_types, TRUE, $this->t('Field types'))); + + $formatter_types = \Drupal::service('plugin.manager.field.formatter')->getDefinitions(); + ksort($formatter_types); + $output['formatter_types'] = array('#markup' => kprint_r($formatter_types, TRUE, $this->t('Formatter types'))); + + $widget_types = \Drupal::service('plugin.manager.field.widget')->getDefinitions(); + ksort($widget_types); + $output['widget_types'] = array('#markup' => kprint_r($widget_types, TRUE, $this->t('Widget types'))); + + return $output; + } + + /** + * Builds the entity types overview page. + * + * @return array + * Array of page elements to render. + */ + public function entityInfoPage() { + $types = $this->entityTypeManager()->getDefinitions(); + ksort($types); + return array('#markup' => kprint_r($types, TRUE)); + } + + /** + * Builds the state variable overview page. + * + * @return array + * Array of page elements to render. + */ + public function stateSystemPage() { + $output['#attached']['library'][] = 'system/drupal.system.modules'; + + $output['filters'] = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array('table-filter', 'js-show'), + ), + ); + + $output['filters']['text'] = array( + '#type' => 'search', + '#title' => $this->t('Search'), + '#size' => 30, + '#placeholder' => $this->t('Enter state name'), + '#attributes' => array( + 'class' => array('table-filter-text'), + 'data-table' => '.devel-state-list', + 'autocomplete' => 'off', + 'title' => $this->t('Enter a part of the state name to filter by.'), + ), + ); + + $can_edit = $this->currentUser()->hasPermission('administer site configuration'); + + $header = array( + 'name' => $this->t('Name'), + 'value' => $this->t('Value'), + ); + + if ($can_edit) { + $header['edit'] = $this->t('Operations'); + } + + $rows = array(); + // State class doesn't have getAll method so we get all states from the + // KeyValueStorage. + foreach ($this->keyValue('state')->getAll() as $state_name => $state) { + $rows[$state_name] = array( + 'name' => array( + 'data' => $state_name, + 'class' => 'table-filter-text-source', + ), + 'value' => array( + 'data' => kprint_r($state, TRUE), + ), + ); + + if ($can_edit) { + $operations['edit'] = array( + 'title' => $this->t('Edit'), + 'url' => Url::fromRoute('devel.system_state_edit', array('state_name' => $state_name)), + ); + $rows[$state_name]['edit'] = array( + 'data' => array('#type' => 'operations', '#links' => $operations), + ); + } + } + + $output['states'] = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + '#empty' => $this->t('No state variables found.'), + '#attributes' => array( + 'class' => array('devel-state-list'), + ), + ); + + return $output; + } + + /** + * Builds the session overview page. + * + * @return array + * Array of page elements to render. + */ + public function session() { + $output['description'] = array( + '#markup' => '

' . $this->t('Here are the contents of your $_SESSION variable.') . '

', + ); + $output['session'] = array( + '#type' => 'table', + '#header' => array($this->t('Session name'), $this->t('Session ID')), + '#rows' => array(array(session_name(), session_id())), + '#empty' => $this->t('No session available.'), + ); + $output['data'] = array( + '#markup' => kprint_r($_SESSION, TRUE), + ); + + return $output; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Controller/EntityDebugController.php b/sites/all/modules/contrib/dev/devel/src/Controller/EntityDebugController.php new file mode 100644 index 000000000..3235e9f62 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Controller/EntityDebugController.php @@ -0,0 +1,122 @@ +dumper = $dumper; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('devel.dumper')); + } + + /** + * Returns the loaded structure of the current entity. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * A RouteMatch object. + * + * @return array + * Array of page elements to render. + */ + public function entityLoad(RouteMatchInterface $route_match) { + $output = []; + + $entity = $this->getEntityFromRouteMatch($route_match); + + if ($entity instanceof EntityInterface) { + // Field definitions are lazy loaded and are populated only when needed. + // By calling ::getFieldDefinitions() we are sure that field definitions + // are populated and available in the dump output. + // @see https://www.drupal.org/node/2311557 + if($entity instanceof FieldableEntityInterface) { + $entity->getFieldDefinitions(); + } + + $output = $this->dumper->exportAsRenderable($entity); + } + + return $output; + } + + /** + * Returns the render structure of the current entity. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * A RouteMatch object. + * + * @return array + * Array of page elements to render. + */ + public function entityRender(RouteMatchInterface $route_match) { + $output = []; + + $entity = $this->getEntityFromRouteMatch($route_match); + + if ($entity instanceof EntityInterface) { + $entity_type_id = $entity->getEntityTypeId(); + $view_hook = $entity_type_id . '_view'; + + $build = []; + // If module implements own {entity_type}_view() hook use it, otherwise + // fallback to the entity view builder if available. + if (function_exists($view_hook)) { + $build = $view_hook($entity); + } + elseif ($this->entityTypeManager()->hasHandler($entity_type_id, 'view_builder')) { + $build = $this->entityTypeManager()->getViewBuilder($entity_type_id)->view($entity); + } + + $output = $this->dumper->exportAsRenderable($build); + } + + return $output; + } + + /** + * Retrieves entity from route match. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. + * + * @return \Drupal\Core\Entity\EntityInterface|null + * The entity object as determined from the passed-in route match. + */ + protected function getEntityFromRouteMatch(RouteMatchInterface $route_match) { + $parameter_name = $route_match->getRouteObject()->getOption('_devel_entity_type_id'); + $entity = $route_match->getParameter($parameter_name); + return $entity; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Controller/SwitchUserController.php b/sites/all/modules/contrib/dev/devel/src/Controller/SwitchUserController.php new file mode 100644 index 000000000..e5da73c22 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Controller/SwitchUserController.php @@ -0,0 +1,120 @@ +account = $account; + $this->userStorage = $user_storage; + $this->moduleHandler = $module_handler; + $this->sessionManager = $session_manager; + $this->session = $session; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('current_user'), + $container->get('entity.manager')->getStorage('user'), + $container->get('module_handler'), + $container->get('session_manager'), + $container->get('session') + ); + } + + /** + * Switches to a different user. + * + * We don't call session_save_session() because we really want to change users. + * Usually unsafe! + * + * @param string $name + * The username to switch to, or NULL to log out. + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + * A redirect response object. + * + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + public function switchUser($name = NULL) { + if (empty($name) || !($account = $this->userStorage->loadByProperties(['name' => $name]))) { + throw new AccessDeniedHttpException(); + } + $account = reset($account); + + // Call logout hooks when switching from original user. + $this->moduleHandler->invokeAll('user_logout', [$this->account]); + + // Regenerate the session ID to prevent against session fixation attacks. + $this->sessionManager->regenerate(); + + // Based off masquarade module as: + // https://www.drupal.org/node/218104 doesn't stick and instead only + // keeps context until redirect. + $this->account->setAccount($account); + $this->session->set('uid', $account->id()); + + // Call all login hooks when switching to masquerading user. + $this->moduleHandler->invokeAll('user_login', [$account]); + + return $this->redirect(''); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/DevelDumperBase.php b/sites/all/modules/contrib/dev/devel/src/DevelDumperBase.php new file mode 100644 index 000000000..ae5ad3c54 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/DevelDumperBase.php @@ -0,0 +1,38 @@ + $this->export($input, $name)]; + } + + /** + * Wrapper for \Drupal\Core\Render\Markup::create(). + * + * @param string $input + * The input string to mark as safe. + * + * @return string + * The unaltered input value. + */ + protected function setSafeMarkup($input) { + return Markup::create($input); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/DevelDumperInterface.php b/sites/all/modules/contrib/dev/devel/src/DevelDumperInterface.php new file mode 100644 index 000000000..1236dd0be --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/DevelDumperInterface.php @@ -0,0 +1,59 @@ +config = $config_factory->get('devel.settings'); + $this->account = $account; + $this->dumperManager = $dumper_manager; + } + + /** + * Instances a new dumper plugin. + * + * @param string $plugin_id + * (optional) The plugin ID, defaults to NULL. + * + * @return \Drupal\devel\DevelDumperInterface + * Returns the devel dumper plugin instance. + */ + protected function createInstance($plugin_id = NULL) { + if (!$plugin_id || !$this->dumperManager->isPluginSupported($plugin_id)) { + $plugin_id = $this->config->get('devel_dumper'); + } + return $this->dumperManager->createInstance($plugin_id); + } + + /** + * {@inheritdoc} + */ + public function dump($input, $name = NULL, $plugin_id = NULL) { + if ($this->hasAccessToDevelInformation()) { + $this->createInstance($plugin_id)->dump($input, $name); + } + } + + /** + * {@inheritdoc} + */ + public function export($input, $name = NULL, $plugin_id = NULL) { + if ($this->hasAccessToDevelInformation()) { + return $this->createInstance($plugin_id)->export($input, $name); + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function message($input, $name = NULL, $type = 'status', $plugin_id = NULL) { + if ($this->hasAccessToDevelInformation()) { + $output = $this->export($input, $name, $plugin_id); + drupal_set_message($output, $type, TRUE); + } + } + + /** + * {@inheritdoc} + */ + public function debug($input, $name = NULL, $plugin_id = NULL) { + $output = $this->createInstance($plugin_id)->export($input, $name) . "\n"; + // The temp directory does vary across multiple simpletest instances. + $file = file_directory_temp() . '/drupal_debug.txt'; + if (file_put_contents($file, $output, FILE_APPEND) === FALSE && $this->hasAccessToDevelInformation()) { + drupal_set_message(t('Devel was unable to write to %file.', ['%file' => $file]), 'error'); + return FALSE; + } + } + + /** + * {@inheritdoc} + */ + public function dumpOrExport($input, $name = NULL, $export = TRUE, $plugin_id = NULL) { + if ($this->hasAccessToDevelInformation()) { + $dumper = $this->createInstance($plugin_id); + if ($export) { + return $dumper->export($input, $name); + } + $dumper->dump($input, $name); + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function exportAsRenderable($input, $name = NULL, $plugin_id = NULL) { + if ($this->hasAccessToDevelInformation()) { + return $this->createInstance($plugin_id)->exportAsRenderable($input, $name); + } + return []; + } + + /** + * Checks whether a user has access to devel information. + * + * @return bool + * TRUE if the user has the permission, FALSE otherwise. + */ + protected function hasAccessToDevelInformation() { + return $this->account && $this->account->hasPermission('access devel information'); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/DevelDumperManagerInterface.php b/sites/all/modules/contrib/dev/devel/src/DevelDumperManagerInterface.php new file mode 100644 index 000000000..71ac6c531 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/DevelDumperManagerInterface.php @@ -0,0 +1,102 @@ +setCacheBackend($cache_backend, 'devel_dumper_plugins'); + $this->alterInfo('devel_dumper_info'); + } + + /** + * {@inheritdoc} + */ + public function processDefinition(&$definition, $plugin_id) { + parent::processDefinition($definition, $plugin_id); + + $definition['supported'] = (bool) call_user_func([$definition['class'], 'checkRequirements']); + } + + /** + * {@inheritdoc} + */ + public function isPluginSupported($plugin_id) { + $definition = $this->getDefinition($plugin_id, FALSE); + return $definition && $definition['supported']; + } + + /** + * {@inheritdoc} + */ + public function createInstance($plugin_id, array $configuration = []) { + if (!$this->isPluginSupported($plugin_id)) { + $plugin_id = $this->getFallbackPluginId($plugin_id); + } + return parent::createInstance($plugin_id, $configuration); + } + + /** + * {@inheritdoc} + */ + public function getFallbackPluginId($plugin_id, array $configuration = []) { + return 'default'; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/DevelDumperPluginManagerInterface.php b/sites/all/modules/contrib/dev/devel/src/DevelDumperPluginManagerInterface.php new file mode 100644 index 000000000..8f42ed2a3 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/DevelDumperPluginManagerInterface.php @@ -0,0 +1,24 @@ +config = $config->get('devel.settings'); + $this->account = $account; + $this->moduleHandler = $module_handler; + $this->urlGenerator = $url_generator; + } + + /** + * Register the devel error handler. + * + * @param \Symfony\Component\EventDispatcher\Event $event + * The event to process. + */ + public function registerErrorHandler(Event $event = NULL) { + if ($this->account && $this->account->hasPermission('access devel information')) { + devel_set_handler(devel_get_handlers()); + } + } + + /** + * Initializes devel module requirements. + */ + public function onRequest(GetResponseEvent $event) { + if ($this->config->get('rebuild_theme')) { + drupal_theme_rebuild(); + + // Ensure that the active theme object is cleared. + $theme_name = \Drupal::theme()->getActiveTheme()->getName(); + \Drupal::state()->delete('theme.active_theme.' . $theme_name); + \Drupal::theme()->resetActiveTheme(); + + /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler*/ + $theme_handler = \Drupal::service('theme_handler'); + $theme_handler->refreshInfo(); + // @todo This is not needed after https://www.drupal.org/node/2330755 + $list = $theme_handler->listInfo(); + $theme_handler->addTheme($list[$theme_name]); + + if (\Drupal::service('flood')->isAllowed('devel.rebuild_theme_warning', 1)) { + \Drupal::service('flood')->register('devel.rebuild_theme_warning'); + if ($this->account->hasPermission('access devel information')) { + drupal_set_message(t('The theme information is being rebuilt on every request. Remember to turn off this feature on production websites.', array(':url' => $this->urlGenerator->generateFromRoute('devel.admin_settings')))); + } + } + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + // Set a low value to start as early as possible. + $events[KernelEvents::REQUEST][] = ['onRequest', -100]; + + // Runs as soon as possible in the request but after + // AuthenticationSubscriber (priority 300) because you need to access to + // the current user for determine whether register the devel error handler + // or not. + $events[KernelEvents::REQUEST][] = ['registerErrorHandler', 256]; + + return $events; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Form/ConfigEditor.php b/sites/all/modules/contrib/dev/devel/src/Form/ConfigEditor.php new file mode 100644 index 000000000..f8c985867 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Form/ConfigEditor.php @@ -0,0 +1,146 @@ +config($config_name); + + if ($config === FALSE || $config->isNew()) { + drupal_set_message(t('Config @name does not exist in the system.', array('@name' => $config_name)), 'error'); + return; + } + + $data = $config->get(); + + if (empty($data)) { + drupal_set_message(t('Config @name exists but has no data.', array('@name' => $config_name)), 'warning'); + return; + } + + try { + $output = Yaml::encode($data); + } + catch (InvalidDataTypeException $e) { + drupal_set_message(t('Invalid data detected for @name : %error', array('@name' => $config_name, '%error' => $e->getMessage())), 'error'); + return; + } + + $form['current'] = array( + '#type' => 'details', + '#title' => $this->t('Current value for %variable', array('%variable' => $config_name)), + '#attributes' => array('class' => array('container-inline')), + ); + $form['current']['value'] = array( + '#type' => 'item', + '#markup' => dpr($output, TRUE), + ); + + $form['name'] = array( + '#type' => 'value', + '#value' => $config_name, + ); + $form['new'] = array( + '#type' => 'textarea', + '#title' => $this->t('New value'), + '#default_value' => $output, + '#rows' => 24, + '#required' => TRUE, + ); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Save'), + ); + $form['actions']['cancel'] = array( + '#type' => 'link', + '#title' => $this->t('Cancel'), + '#url' => $this->buildCancelLinkUrl(), + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + $value = $form_state->getValue('new'); + // try to parse the new provided value + try { + $parsed_value = Yaml::decode($value); + // Config::setData needs array for the new configuration and + // a simple string is valid YAML for any reason. + if (is_array($parsed_value)) { + $form_state->setValue('parsed_value', $parsed_value); + } + else { + $form_state->setErrorByName('new', $this->t('Invalid input')); + } + } + catch (InvalidDataTypeException $e) { + $form_state->setErrorByName('new', $this->t('Invalid input: %error', array('%error' => $e->getMessage()))); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $values = $form_state->getValues(); + try { + $this->configFactory()->getEditable($values['name'])->setData($values['parsed_value'])->save(); + drupal_set_message($this->t('Configuration variable %variable was successfully saved.', array('%variable' => $values['name']))); + $this->logger('devel')->info('Configuration variable %variable was successfully saved.', array('%variable' => $values['name'])); + + $form_state->setRedirectUrl(Url::fromRoute('devel.configs_list')); + } + catch (\Exception $e) { + drupal_set_message($e->getMessage(), 'error'); + $this->logger('devel')->error('Error saving configuration variable %variable : %error.', array('%variable' => $values['name'], '%error' => $e->getMessage())); + } + } + + /** + * Builds the cancel link url for the form. + * + * @return Url + * Cancel url + */ + private function buildCancelLinkUrl() { + $query = $this->getRequest()->query; + + if ($query->has('destination')) { + $options = UrlHelper::parse($query->get('destination')); + $url = Url::fromUri('internal:/' . $options['path'], $options); + } + else { + $url = Url::fromRoute('devel.configs_list'); + } + + return $url; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Form/ConfigsList.php b/sites/all/modules/contrib/dev/devel/src/Form/ConfigsList.php new file mode 100644 index 000000000..f0993e77b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Form/ConfigsList.php @@ -0,0 +1,85 @@ + 'details', + '#title' => t('Filter variables'), + '#attributes' => array('class' => array('container-inline')), + '#open' => isset($filter) && trim($filter) != '', + ); + $form['filter']['name'] = array( + '#type' => 'textfield', + '#title' => $this->t('Variable name'), + '#title_display' => 'invisible', + '#default_value' => $filter, + ); + $form['filter']['show'] = array( + '#type' => 'submit', + '#value' => $this->t('Filter'), + ); + + $header = array( + 'name' => array('data' => $this->t('Name')), + 'edit' => array('data' => $this->t('Operations')), + ); + + $rows = array(); + + $destination = $this->getDestinationArray(); + + // List all the variables filtered if any filter was provided. + $names = $this->configFactory()->listAll($filter); + + foreach ($names as $config_name) { + $operations['edit'] = array( + 'title' => $this->t('Edit'), + 'url' => Url::fromRoute('devel.config_edit', array('config_name' => $config_name)), + 'query' => $destination + ); + $rows[] = array( + 'name' => $config_name, + 'operation' => array('data' => array('#type' => 'operations', '#links' => $operations)), + ); + } + + $form['variables'] = array( + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + '#empty' => $this->t('No variables found') + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $filter = $form_state->getValue('name'); + $form_state->setRedirectUrl(Url::FromRoute('devel.configs_list', array('filter' => Html::escape($filter)))); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Form/DevelRebuildMenus.php b/sites/all/modules/contrib/dev/devel/src/Form/DevelRebuildMenus.php new file mode 100644 index 000000000..0fb26e626 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Form/DevelRebuildMenus.php @@ -0,0 +1,86 @@ +routeBuilder = $route_builder; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('router.builder') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'devel_menu_rebuild'; + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->t('Are you sure you want to rebuild menus?'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url(''); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return $this->t('Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.'); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Rebuild'); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $this->routeBuilder->rebuild(); + drupal_set_message($this->t('The menu router has been rebuilt.')); + $form_state->setRedirect(''); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Form/DevelReinstall.php b/sites/all/modules/contrib/dev/devel/src/Form/DevelReinstall.php new file mode 100644 index 000000000..109e40b75 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Form/DevelReinstall.php @@ -0,0 +1,154 @@ +moduleInstaller = $module_installer; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('module_installer') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'devel_reinstall_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + // Get a list of all available modules. + $modules = system_rebuild_module_data(); + + $uninstallable = array_filter($modules, function ($module) use ($modules) { + return empty($modules[$module->getName()]->info['required']) && drupal_get_installed_schema_version($module->getName()) > SCHEMA_UNINSTALLED && $module->getName() !== 'devel'; + }); + + $form['filters'] = array( + '#type' => 'container', + '#attributes' => array( + 'class' => array('table-filter', 'js-show'), + ), + ); + $form['filters']['text'] = array( + '#type' => 'search', + '#title' => $this->t('Search'), + '#size' => 30, + '#placeholder' => $this->t('Enter module name'), + '#attributes' => array( + 'class' => array('table-filter-text'), + 'data-table' => '#devel-reinstall-form', + 'autocomplete' => 'off', + 'title' => $this->t('Enter a part of the module name or description to filter by.'), + ), + ); + + // Only build the rest of the form if there are any modules available to + // uninstall; + if (empty($uninstallable)) { + return $form; + } + + $header = array( + 'name' => $this->t('Name'), + 'description' => $this->t('Description'), + ); + + $rows = array(); + + foreach ($uninstallable as $module) { + $name = $module->info['name'] ? : $module->getName(); + + $rows[$module->getName()] = array( + 'name' => array( + 'data' => array( + '#type' => 'inline_template', + '#template' => '', + '#context' => array('module_name' => $name), + ) + ), + 'description' => array( + 'data' => $module->info['description'], + 'class' => array('description'), + ), + ); + } + + $form['reinstall'] = array( + '#type' => 'tableselect', + '#header' => $header, + '#options' => $rows, + '#js_select' => FALSE, + '#empty' => $this->t('No modules are available to uninstall.'), + ); + + $form['#attached']['library'][] = 'system/drupal.system.modules'; + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Reinstall'), + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + // Form submitted, but no modules selected. + if (!array_filter($form_state->getValue('reinstall'))) { + $form_state->setErrorByName('reinstall', $this->t('No modules selected.')); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + try { + $modules = $form_state->getValue('reinstall'); + $reinstall = array_keys(array_filter($modules)); + $this->moduleInstaller->uninstall($reinstall, FALSE); + $this->moduleInstaller->install($reinstall, FALSE); + drupal_set_message($this->t('Uninstalled and installed: %names.', array('%names' => implode(', ', $reinstall)))); + } + catch (\Exception $e) { + drupal_set_message($this->t('Unable to reinstall modules. Error: %error.', array('%error' => $e->getMessage())), 'error'); + } + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Form/ExecutePHP.php b/sites/all/modules/contrib/dev/devel/src/Form/ExecutePHP.php new file mode 100644 index 000000000..cd66bf480 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Form/ExecutePHP.php @@ -0,0 +1,55 @@ + $this->t('Execute PHP Code'), + '#description' => $this->t('Execute some PHP code'), + ); + $form['execute']['code'] = array( + '#type' => 'textarea', + '#title' => t('PHP code to execute'), + '#description' => t('Enter some code. Do not use <?php ?> tags.'), + '#default_value' => (isset($_SESSION['devel_execute_code']) ? $_SESSION['devel_execute_code'] : ''), + '#rows' => 20, + ); + $form['execute']['op'] = array('#type' => 'submit', '#value' => t('Execute')); + $form['#redirect'] = FALSE; + if (isset($_SESSION['devel_execute_code'])) { + unset($_SESSION['devel_execute_code']); + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + ob_start(); + $code = $form_state->getValue('code'); + print eval($code); + $_SESSION['devel_execute_code'] = $code; + dpm(ob_get_clean()); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Form/SettingsForm.php b/sites/all/modules/contrib/dev/devel/src/Form/SettingsForm.php new file mode 100644 index 000000000..81a9bbec2 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Form/SettingsForm.php @@ -0,0 +1,192 @@ +dumperManager = $devel_dumper_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.devel_dumper') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'devel_admin_settings_form'; + } + + /** + * {@inheritdoc} + */ + protected function getEditableConfigNames() { + return [ + 'devel.settings', + ]; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL) { + $current_url = Url::createFromRequest($request); + $devel_config = $this->config('devel.settings'); + + $form['page_alter'] = array('#type' => 'checkbox', + '#title' => t('Display $page array'), + '#default_value' => $devel_config->get('page_alter'), + '#description' => t('Display $page array from hook_page_alter() in the messages area of each page.'), + ); + $form['raw_names'] = array('#type' => 'checkbox', + '#title' => t('Display machine names of permissions and modules'), + '#default_value' => $devel_config->get('raw_names'), + '#description' => t('Display the language-independent machine names of the permissions in mouse-over hints on the Permissions page and the module base file names on the Permissions and Modules pages.', array(':permissions_url' => Url::fromRoute('user.admin_permissions')->toString(), ':modules_url' => Url::fromRoute('system.modules_list')->toString())), + ); + + $error_handlers = devel_get_handlers(); + $form['error_handlers'] = array( + '#type' => 'select', + '#title' => t('Error handlers'), + '#options' => array( + DEVEL_ERROR_HANDLER_NONE => t('None'), + DEVEL_ERROR_HANDLER_STANDARD => t('Standard Drupal'), + DEVEL_ERROR_HANDLER_BACKTRACE_DPM => t('Kint backtrace in the message area'), + DEVEL_ERROR_HANDLER_BACKTRACE_KINT => t('Kint backtrace above the rendered page'), + ), + '#multiple' => TRUE, + '#default_value' => empty($error_handlers) ? DEVEL_ERROR_HANDLER_NONE : $error_handlers, + '#description' => [ + [ + '#markup' => $this->t('Select the error handler(s) to use, in case you choose to show errors on screen.', [':choose' => $this->url('system.logging_settings')]) + ], + [ + '#theme' => 'item_list', + '#items' => [ + $this->t('None is a good option when stepping through the site in your debugger.'), + $this->t('Standard Drupal does not display all the information that is often needed to resolve an issue.'), + $this->t('Kint backtrace displays nice debug information when any type of error is noticed, but only to users with the %perm permission.', ['%perm' => t('Access developer information')]), + ], + ], + [ + '#markup' => $this->t('Depending on the situation, the theme, the size of the call stack and the arguments, etc., some handlers may not display their messages, or display them on the subsequent page. Select Standard Drupal and Kint backtrace above the rendered page to maximize your chances of not missing any messages.') . '
' . + $this->t('Demonstrate the current error handler(s):') . ' ' . + $this->l('notice', $current_url->setOption('query', ['demo' => 'notice'])) . ', ' . + $this->l('notice+warning', $current_url->setOption('query', ['demo' => 'warning'])). ', ' . + $this->l('notice+warning+error', $current_url->setOption('query', ['demo' => 'error'])) . ' (' . + $this->t('The presentation of the @error is determined by PHP.', ['@error' => 'error']) . ')' + ], + ], + ); + $form['error_handlers']['#size'] = count($form['error_handlers']['#options']); + if ($request->query->has('demo')) { + if ($request->getMethod() == 'GET') { + $this->demonstrateErrorHandlers($request->query->get('demo')); + } + $request->query->remove('demo'); + } + + $form['rebuild_theme'] = array( + '#type' => 'checkbox', + '#title' => t('Rebuild the theme information like the registry'), + '#description' => t('While creating new templates, change the $theme.info.yml and theme_ overrides the theme information needs to be rebuilt.'), + '#default_value' => $devel_config->get('rebuild_theme'), + ); + + $dumper = $devel_config->get('devel_dumper'); + $default = $this->dumperManager->isPluginSupported($dumper) ? $dumper : $this->dumperManager->getFallbackPluginId(NULL); + + $form['dumper'] = array( + '#type' => 'radios', + '#title' => $this->t('Variables Dumper'), + '#options' => [], + '#default_value' => $default, + '#description' => $this->t('Select the debugging tool used for formatting and displaying the variables inspected through the debug functions of Devel. You can enable the Kint module (shipped with Devel) and select the Kint debugging tool for an improved debugging experience. NOTE: Some of these plugins require external libraries for to be enabled. Learn how install external libraries with Composer.', [':url' => 'https://www.drupal.org/node/2404989', ':kint_install' => Url::fromRoute('system.modules_list')->toString()]), + ); + + foreach ($this->dumperManager->getDefinitions() as $id => $definition) { + $form['dumper']['#options'][$id] = $definition['label']; + + $supported = $this->dumperManager->isPluginSupported($id); + $form['dumper'][$id]['#disabled'] = !$supported; + + $form['dumper'][$id]['#description'] = [ + '#type' => 'inline_template', + '#template' => '{{ description }}{% if not supported %}
{% trans %}Not available. You may need to install external dependencies for use this plugin.{% endtrans %}
{% endif %}', + '#context' => [ + 'description' => $definition['description'], + 'supported' => $supported, + ] + ]; + } + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $values = $form_state->getValues(); + $this->config('devel.settings') + ->set('page_alter', $values['page_alter']) + ->set('raw_names', $values['raw_names']) + ->set('error_handlers', $values['error_handlers']) + ->set('rebuild_theme', $values['rebuild_theme']) + ->set('devel_dumper', $values['dumper']) + ->save(); + + parent::submitForm($form, $form_state); + } + + /** + * @param string $severity + */ + protected function demonstrateErrorHandlers($severity) { + switch ($severity) { + case 'notice': + $undefined = $undefined; + break; + case 'warning': + $undefined = $undefined; + 1/0; + break; + case 'error': + $undefined = $undefined; + 1/0; + devel_undefined_function(); + break; + } + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Form/SwitchUserForm.php b/sites/all/modules/contrib/dev/devel/src/Form/SwitchUserForm.php new file mode 100644 index 000000000..8fbb02345 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Form/SwitchUserForm.php @@ -0,0 +1,108 @@ +csrfToken = $csrf_token_generator; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('csrf_token') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'devel_switchuser_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $form['autocomplete'] = [ + '#type' => 'container', + '#attributes' => [ + 'class' => ['container-inline'], + ], + ]; + $form['autocomplete']['userid'] = [ + '#type' => 'entity_autocomplete', + '#title' => $this->t('Username'), + '#placeholder' => $this->t('Enter username'), + '#target_type' => 'user', + '#selection_settings' => [ + 'include_anonymous' => FALSE + ], + '#process_default_value' => FALSE, + '#maxlength' => USERNAME_MAX_LENGTH, + '#title_display' => 'invisible', + '#required' => TRUE, + '#size' => '28', + ]; + + $form['autocomplete']['actions'] = ['#type' => 'actions']; + $form['autocomplete']['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Switch'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + if (!$account = User::load($form_state->getValue('userid'))) { + $form_state->setErrorByName('userid', $this->t('Username not found')); + } + else { + $form_state->setValue('username', $account->getAccountName()); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // We cannot rely on automatic token creation, since the csrf seed changes + // after the redirect and the generated token is not more valid. + // TODO find another way to do this. + $url = Url::fromRoute('devel.switch', ['name' => $form_state->getValue('username')]); + $url->setOption('query', ['token' => $this->csrfToken->get($url->getInternalPath())]); + + $form_state->setRedirectUrl($url); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Form/SystemStateEdit.php b/sites/all/modules/contrib/dev/devel/src/Form/SystemStateEdit.php new file mode 100644 index 000000000..ff2147420 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Form/SystemStateEdit.php @@ -0,0 +1,189 @@ +state = $state; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('state') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'devel_state_system_edit_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, $state_name = '') { + // Get the old value + $old_value = $this->state->get($state_name); + + if (!isset($old_value)) { + drupal_set_message(t('State @name does not exist in the system.', array('@name' => $state_name)), 'warning'); + return; + } + + // Only simple structures are allowed to be edited. + $disabled = !$this->checkObject($old_value); + + if ($disabled) { + drupal_set_message(t('Only simple structures are allowed to be edited. State @name contains objects.', array('@name' => $state_name)), 'warning'); + } + + // First we will show the user the content of the variable about to be edited. + $form['value'] = array( + '#type' => 'item', + '#title' => $this->t('Current value for %name', array('%name' => $state_name)), + '#markup' => kprint_r($old_value, TRUE), + ); + + $transport = 'plain'; + + if (!$disabled && is_array($old_value)) { + try { + $old_value = Yaml::encode($old_value); + $transport = 'yaml'; + } + catch (InvalidDataTypeException $e) { + drupal_set_message(t('Invalid data detected for @name : %error', array('@name' => $state_name, '%error' => $e->getMessage())), 'error'); + return; + } + } + + // Store in the form the name of the state variable + $form['state_name'] = array( + '#type' => 'value', + '#value' => $state_name, + ); + // Set the transport format for the new value. Values: + // - plain + // - yaml + $form['transport'] = array( + '#type' => 'value', + '#value' => $transport, + ); + + $form['new_value'] = array( + '#type' => 'textarea', + '#title' => $this->t('New value'), + '#default_value' => $disabled ? '' : $old_value, + '#disabled' => $disabled, + '#rows' => 15, + ); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Save'), + '#disabled' => $disabled, + ); + $form['actions']['cancel'] = array( + '#type' => 'link', + '#title' => $this->t('Cancel'), + '#url' => Url::fromRoute('devel.state_system_page') + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + $values = $form_state->getValues(); + + if ($values['transport'] == 'yaml') { + // try to parse the new provided value + try { + $parsed_value = Yaml::decode($values['new_value']); + $form_state->setValue('parsed_value', $parsed_value); + } + catch (InvalidDataTypeException $e) { + $form_state->setErrorByName('new_value', $this->t('Invalid input: %error', array('%error' => $e->getMessage()))); + } + } + else { + $form_state->setValue('parsed_value', $values['new_value']); + } + + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // Save the state + $values = $form_state->getValues(); + $this->state->set($values['state_name'], $values['parsed_value']); + + $form_state->setRedirectUrl(Url::fromRoute('devel.state_system_page')); + + drupal_set_message($this->t('Variable %variable was successfully edited.', array('%variable' => $values['state_name']))); + $this->logger('devel')->info('Variable %variable was successfully edited.', array('%variable' => $values['state_name'])); + } + + /** + * Helper function to determine if a variable is or contains an object. + * + * @param $data + * Input data to check + * + * @return bool + * TRUE if the variable is not an object and does not contain one. + */ + protected function checkObject($data) { + if (is_object($data)) { + return FALSE; + } + if (is_array($data)) { + // If the current object is an array, then check recursively. + foreach ($data as $value) { + // If there is an object the whole container is "contaminated" + if (!$this->checkObject($value)) { + return FALSE; + } + } + } + + // All checks pass + return TRUE; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Block/DevelExecutePHP.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Block/DevelExecutePHP.php new file mode 100644 index 000000000..23076bfa0 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Block/DevelExecutePHP.php @@ -0,0 +1,33 @@ +getForm('Drupal\devel\Form\ExecutePHP'); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Block/SwitchUserBlock.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Block/SwitchUserBlock.php new file mode 100644 index 000000000..c427fed44 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Block/SwitchUserBlock.php @@ -0,0 +1,286 @@ +formBuilder = $form_builder; + $this->currentUser = $current_user; + $this->userStorage = $user_storage; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('current_user'), + $container->get('entity.manager')->getStorage('user'), + $container->get('form_builder') + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'list_size' => 12, + 'include_anon' => FALSE, + 'show_form' => TRUE, + ]; + } + + /** + * {@inheritdoc} + */ + public function blockAccess(AccountInterface $account) { + return AccessResult::allowedIfHasPermission($account, 'switch users'); + } + + /** + * {@inheritdoc} + */ + public function blockForm($form, FormStateInterface $form_state) { + $anononymous = new AnonymousUserSession(); + $form['list_size'] = [ + '#type' => 'number', + '#title' => $this->t('Number of users to display in the list'), + '#default_value' => $this->configuration['list_size'], + '#min' => 1, + '#max' => 50, + ]; + $form['include_anon'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Include %anonymous', ['%anonymous' => $anononymous->getAccountName()]), + '#default_value' => $this->configuration['include_anon'], + ]; + $form['show_form'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Allow entering any user name'), + '#default_value' => $this->configuration['show_form'], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function blockSubmit($form, FormStateInterface $form_state) { + $this->configuration['list_size'] = $form_state->getValue('list_size'); + $this->configuration['include_anon'] = $form_state->getValue('include_anon'); + $this->configuration['show_form'] = $form_state->getValue('show_form'); + } + + /** + * {@inheritdoc} + */ + public function getCacheMaxAge() { + return 0; + } + + /** + * {@inheritdoc} + */ + public function build() { + $build = []; + if ($accounts = $this->getUsers()) { + $build['devel_links'] = $this->buildUserList($accounts); + + if ($this->configuration['show_form']) { + $build['devel_form'] = $this->formBuilder->getForm('\Drupal\devel\Form\SwitchUserForm'); + } + } + + return $build; + } + + /** + * Provides the list of accounts that can be used for the user switch. + * + * Inactive users are omitted from all of the following db selects. Users + * with 'switch users' permission and anonymous user if include_anon property + * is set to TRUE, are prioritized. + * + * @return \Drupal\core\Session\AccountInterface[] + * List of accounts to be used for the switch. + */ + protected function getUsers() { + $list_size = $this->configuration['list_size']; + $include_anonymous = $this->configuration['include_anon']; + + $list_size = $include_anonymous ? $list_size - 1 : $list_size; + + // Users with 'switch users' permission are prioritized so + // we try to load first users with this permission. + $query = $this->userStorage->getQuery() + ->condition('uid', 0, '>') + ->condition('status', 0, '>') + ->sort('access', 'DESC') + ->range(0, $list_size); + + $roles = user_roles(TRUE, 'switch users'); + + if (!empty($roles) && !isset($roles[Role::AUTHENTICATED_ID])) { + $query->condition('roles', array_keys($roles), 'IN'); + } + + $user_ids = $query->execute(); + + // If we don't have enough users with 'switch users' permission, add + // uids until we hit $list_size. + if (count($user_ids) < $list_size) { + $query = $this->userStorage->getQuery() + ->condition('uid', 0, '>') + ->condition('status', 0, '>') + ->sort('access', 'DESC') + ->range(0, $list_size); + + // Excludes the prioritized user ids only if the previous query return + // some records. + if (!empty($user_ids)) { + $query->condition('uid', array_keys($user_ids), 'NOT IN'); + $query->range(0, $list_size - count($user_ids)); + } + + $user_ids += $query->execute(); + } + + $accounts = $this->userStorage->loadMultiple($user_ids); + + if ($include_anonymous) { + $anonymous = new AnonymousUserSession(); + $accounts[$anonymous->id()] = $anonymous; + } + + uasort($accounts, 'static::sortUserList'); + + return $accounts; + } + + /** + * Builds the user listing as renderable array. + * + * @param \Drupal\core\Session\AccountInterface[] $accounts + * The accounts to be rendered in the list. + * + * @return array + * A renderable array. + */ + protected function buildUserList(array $accounts) { + $links = []; + + foreach ($accounts as $account) { + $links[$account->id()] = [ + 'title' => $account->getDisplayName(), + 'url' => Url::fromRoute('devel.switch', ['name' => $account->getAccountName()]), + 'query' => $this->getDestinationArray(), + 'attributes' => [ + 'title' => $account->hasPermission('switch users') ? $this->t('This user can switch back.') : $this->t('Caution: this user will be unable to switch back.'), + ], + ]; + + if ($account->isAnonymous()) { + $links[$account->id()]['url'] = Url::fromRoute('user.logout'); + } + + if ($this->currentUser->id() === $account->id()) { + $links[$account->id()]['title'] = new FormattableMarkup('%user', ['%user' => $account->getDisplayName()]); + } + } + + return [ + '#theme' => 'links', + '#links' => $links, + '#attached' => ['library' => ['devel/devel']], + ]; + } + + /** + * Helper callback for uasort() to sort accounts by last access. + */ + public static function sortUserList(AccountInterface $a, AccountInterface $b) { + $a_access = (int) $a->getLastAccessedTime(); + $b_access = (int) $b->getLastAccessedTime(); + + if ($a_access === $b_access) { + return 0; + } + + // User never access to site. + if ($a_access === 0) { + return 1; + } + + return ($a_access > $b_access) ? -1 : 1; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Derivative/DevelLocalTask.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Derivative/DevelLocalTask.php new file mode 100644 index 000000000..038360c10 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Derivative/DevelLocalTask.php @@ -0,0 +1,99 @@ +entityTypeManager = $entity_type_manager; + $this->stringTranslation = $string_translation; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $container->get('entity_type.manager'), + $container->get('string_translation') + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + $this->derivatives = array(); + + foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) { + + $has_edit_path = $entity_type->hasLinkTemplate('devel-load'); + $has_canonical_path = $entity_type->hasLinkTemplate('devel-render'); + + if ($has_edit_path || $has_canonical_path) { + + $this->derivatives["$entity_type_id.devel_tab"] = array( + 'route_name' => "entity.$entity_type_id." . ($has_edit_path ? 'devel_load' : 'devel_render'), + 'title' => $this->t('Devel'), + 'base_route' => "entity.$entity_type_id." . ($has_canonical_path ? "canonical" : "edit_form"), + 'weight' => 100, + ); + + if ($has_canonical_path) { + $this->derivatives["$entity_type_id.devel_render_tab"] = array( + 'route_name' => "entity.$entity_type_id.devel_render", + 'weight' => 100, + 'title' => $this->t('Render'), + 'parent_id' => "devel.entities:$entity_type_id.devel_tab", + ); + } + + if ($has_edit_path) { + $this->derivatives["$entity_type_id.devel_load_tab"] = array( + 'route_name' => "entity.$entity_type_id.devel_load", + 'weight' => 100, + 'title' => $this->t('Load'), + 'parent_id' => "devel.entities:$entity_type_id.devel_tab", + ); + } + } + } + + foreach ($this->derivatives as &$entry) { + $entry += $base_plugin_definition; + } + + return $this->derivatives; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/ChromePhp.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/ChromePhp.php new file mode 100644 index 000000000..df84cab27 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/ChromePhp.php @@ -0,0 +1,40 @@ +ChromePhp debugging tool.") + * ) + */ +class ChromePhp extends DevelDumperBase { + + /** + * {@inheritdoc} + */ + public function dump($input, $name = NULL) { + \ChromePhp::log($input); + } + + /** + * {@inheritdoc} + */ + public function export($input, $name = NULL) { + $this->dump($input); + return $this->t('Dump was redirected to the console.'); + } + + /** + * {@inheritdoc} + */ + public static function checkRequirements() { + return class_exists('ChromePhp', TRUE); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/DoctrineDebug.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/DoctrineDebug.php new file mode 100644 index 000000000..5e1a39b49 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/DoctrineDebug.php @@ -0,0 +1,74 @@ +Doctrine debugging tool.") + * ) + */ +class DoctrineDebug extends DevelDumperBase { + + /** + * {@inheritdoc} + */ + public function dump($input, $name = NULL) { + if ($name) { + echo $name . ' => '; + } + Debug::dump($input); + } + + /** + * {@inheritdoc} + */ + public function export($input, $name = NULL) { + $name = $name ? $name . ' => ' : ''; + $variable = Debug::export($input, 6); + + ob_start(); + print_r($variable); + $dump = ob_get_contents(); + ob_end_clean(); + + $dump = '
' . $name . $dump . '
'; + + return $this->setSafeMarkup($dump); + } + + /** + * {@inheritdoc} + */ + public function exportAsRenderable($input, $name = NULL) { + $output['container'] = [ + '#type' => 'details', + '#title' => $name ? : $this->t('Variable'), + '#attached' => [ + 'library' => ['devel/devel'] + ], + '#attributes' => [ + 'class' => ['container-inline', 'devel-dumper', 'devel-selectable'], + ], + 'export' => [ + '#markup' => $this->export($input), + ], + ]; + + return $output; + } + + /** + * {@inheritdoc} + */ + public static function checkRequirements() { + return TRUE; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/DrupalVariable.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/DrupalVariable.php new file mode 100644 index 000000000..1b3f8f6d1 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/DrupalVariable.php @@ -0,0 +1,65 @@ +Drupal Variable class.") + * ) + */ +class DrupalVariable extends DevelDumperBase { + + /** + * {@inheritdoc} + */ + public function dump($input, $name = NULL) { + $name = $name ? $name . ' => ' : ''; + $output = Variable::export($input); + echo '
' . $name . print_r($output) . '
'; + } + + /** + * {@inheritdoc} + */ + public function export($input, $name = NULL) { + $name = $name ? $name . ' => ' : ''; + $dump = '
' . $name . Variable::export($input) . '
'; + return $this->setSafeMarkup($dump); + } + + /** + * {@inheritdoc} + */ + public function exportAsRenderable($input, $name = NULL) { + $output['container'] = [ + '#type' => 'details', + '#title' => $name ? : $this->t('Variable'), + '#attached' => [ + 'library' => ['devel/devel'] + ], + '#attributes' => [ + 'class' => ['container-inline', 'devel-dumper', 'devel-selectable'], + ], + 'export' => [ + '#markup' => $this->export($input), + ], + ]; + + return $output; + } + + /** + * {@inheritdoc} + */ + public static function checkRequirements() { + return TRUE; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/FirePhp.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/FirePhp.php new file mode 100644 index 000000000..9140d00e6 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/FirePhp.php @@ -0,0 +1,41 @@ +FirePhp debugging tool.") + * ) + */ +class FirePhp extends DevelDumperBase { + + /** + * {@inheritdoc} + */ + public function dump($input, $name = NULL) { + $fb = new \FB(); + $fb->dump($name, $input); + } + + /** + * {@inheritdoc} + */ + public function export($input, $name = NULL) { + $this->dump($input); + return $this->t('Dump was redirected to the console.'); + } + + /** + * {@inheritdoc} + */ + public static function checkRequirements() { + return class_exists('FirePHP', TRUE); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/VarDumper.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/VarDumper.php new file mode 100644 index 000000000..e8485e883 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Devel/Dumper/VarDumper.php @@ -0,0 +1,50 @@ +Symfony var-dumper debugging tool."), + * ) + * + */ +class VarDumper extends DevelDumperBase { + + /** + * {@inheritdoc} + */ + public function dump($input, $name = NULL) { + echo (string) $this->export($input, $name); + } + + /** + * {@inheritdoc} + */ + public function export($input, $name = NULL) { + $cloner = new VarCloner(); + $dumper = 'cli' === PHP_SAPI ? new CliDumper() : new HtmlDumper(); + + $output = fopen('php://memory', 'r+b'); + $dumper->dump($cloner->cloneVar($input), $output); + $output = stream_get_contents($output, -1, 0); + + return $this->setSafeMarkup($output); + } + + /** + * {@inheritdoc} + */ + public static function checkRequirements() { + return class_exists('Symfony\Component\VarDumper\Cloner\VarCloner', TRUE); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Mail/DevelMailLog.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Mail/DevelMailLog.php new file mode 100644 index 000000000..d9d2dc971 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Mail/DevelMailLog.php @@ -0,0 +1,98 @@ + $value) { + $mimeheaders[] = $name . ': ' . Unicode::mimeHeaderEncode($value); + } + + $line_endings = Settings::get('mail_line_endings', PHP_EOL); + $output = join($line_endings, $mimeheaders) . $line_endings; + // 'Subject:' is a mail header and should not be translated. + $output .= 'Subject: ' . $message['subject'] . $line_endings; + // Blank line to separate headers from body. + $output .= $line_endings; + $output .= preg_replace('@\r?\n@', $line_endings, $message['body']); + return $output; + } + + public function getFileName($message) { + $output_directory = $this->getOutputDirectory(); + $this->makeOutputDirectory($output_directory); + $output_file_format = \Drupal::config('devel.settings')->get('debug_mail_file_format'); + + $tokens = array( + '%to' => $message['to'], + '%subject' => $message['subject'], + '%datetime' => date('y-m-d_his'), + ); + return $output_directory . '/' . $this->dirify(str_replace(array_keys($tokens), array_values($tokens), $output_file_format)); + } + + private function dirify($string) { + return preg_replace('/[^a-zA-Z0-9_\-\.@]/', '_', $string); + } + + /** + * {@inheritdoc} + */ + public function mail(array $message) { + $output = $this->composeMessage($message); + $output_file = $this->getFileName($message); + + return file_put_contents($output_file, $output); + } + + protected function makeOutputDirectory($output_directory) { + if (!file_prepare_directory($output_directory, FILE_CREATE_DIRECTORY)) { + throw new Exception("Unable to continue sending mail, $output_directory is not writable"); + } + } + + public function getOutputDirectory() { + return \Drupal::config('devel.settings')->get('debug_mail_directory'); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Menu/DestinationMenuLink.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Menu/DestinationMenuLink.php new file mode 100644 index 000000000..4edf1c329 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Menu/DestinationMenuLink.php @@ -0,0 +1,32 @@ +')->toString(); + return $options; + } + + /** + * {@inheritdoc} + * + * @todo Make cacheable once https://www.drupal.org/node/2582797 lands. + */ + public function getCacheMaxAge() { + return 0; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Plugin/Menu/MenuItemMenuLink.php b/sites/all/modules/contrib/dev/devel/src/Plugin/Menu/MenuItemMenuLink.php new file mode 100644 index 000000000..73faab64a --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Plugin/Menu/MenuItemMenuLink.php @@ -0,0 +1,29 @@ +')->getInternalPath(); + return $options; + } + + /** + * {@inheritdoc} + */ + public function getCacheMaxAge() { + return 0; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Routing/RouteSubscriber.php b/sites/all/modules/contrib/dev/devel/src/Routing/RouteSubscriber.php new file mode 100644 index 000000000..d9867fdd9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Routing/RouteSubscriber.php @@ -0,0 +1,122 @@ +entityTypeManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + protected function alterRoutes(RouteCollection $collection) { + foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) { + if ($route = $this->getEntityLoadRoute($entity_type)) { + $collection->add("entity.$entity_type_id.devel_load", $route); + } + if ($route = $this->getEntityRenderRoute($entity_type)) { + $collection->add("entity.$entity_type_id.devel_render", $route); + } + } + } + + /** + * Gets the devel load route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getEntityLoadRoute(EntityTypeInterface $entity_type) { + if ($devel_load = $entity_type->getLinkTemplate('devel-load')) { + $entity_type_id = $entity_type->id(); + $route = new Route($devel_load); + $route + ->addDefaults([ + '_controller' => '\Drupal\devel\Controller\EntityDebugController::entityLoad', + '_title' => 'Devel Load', + ]) + ->addRequirements([ + '_permission' => 'access devel information', + ]) + ->setOption('_admin_route', TRUE) + ->setOption('_devel_entity_type_id', $entity_type_id) + ->setOption('parameters', [ + $entity_type_id => ['type' => 'entity:' . $entity_type_id], + ]); + + return $route; + } + } + + /** + * Gets the devel render route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getEntityRenderRoute(EntityTypeInterface $entity_type) { + if ($devel_render = $entity_type->getLinkTemplate('devel-render')) { + $entity_type_id = $entity_type->id(); + $route = new Route($devel_render); + $route + ->addDefaults([ + '_controller' => '\Drupal\devel\Controller\EntityDebugController::entityRender', + '_title' => 'Devel Render', + ]) + ->addRequirements([ + '_permission' => 'access devel information' + ]) + ->setOption('_admin_route', TRUE) + ->setOption('_devel_entity_type_id', $entity_type_id) + ->setOption('parameters', [ + $entity_type_id => ['type' => 'entity:' . $entity_type_id], + ]); + + return $route; + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events = parent::getSubscribedEvents(); + $events[RoutingEvents::ALTER] = ['onAlterRoutes', 100]; + return $events; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Tests/DevelControllerTest.php b/sites/all/modules/contrib/dev/devel/src/Tests/DevelControllerTest.php new file mode 100644 index 000000000..59cc1743a --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Tests/DevelControllerTest.php @@ -0,0 +1,109 @@ +randomMachineName(); + $data = array('type' => 'entity_test', 'name' => $random_label); + $this->entity = entity_create('entity_test', $data); + $this->entity->save(); + + // Create a test entity with only canonical route. + $random_label = $this->randomMachineName(); + $data = array('type' => 'devel_entity_test_canonical', 'name' => $random_label); + $this->entity_canonical = entity_create('devel_entity_test_canonical', $data); + $this->entity_canonical->save(); + + // Create a test entity with only edit route. + $random_label = $this->randomMachineName(); + $data = array('type' => 'devel_entity_test_edit', 'name' => $random_label); + $this->entity_edit = entity_create('devel_entity_test_edit', $data); + $this->entity_edit->save(); + + // Create a test entity with no routes. + $random_label = $this->randomMachineName(); + $data = array('type' => 'devel_entity_test_no_links', 'name' => $random_label); + $this->entity_no_links = entity_create('devel_entity_test_no_links', $data); + $this->entity_no_links->save(); + + $this->drupalPlaceBlock('local_tasks_block'); + + $web_user = $this->drupalCreateUser(array( + 'view test entity', + 'administer entity_test content', + 'access devel information', + )); + $this->drupalLogin($web_user); + } + + function testRouteGeneration() { + // Test Devel load and render routes for entities with both route + // definitions. + $this->drupalGet('entity_test/' . $this->entity->id()); + $this->assertText('Devel', 'Devel tab is present'); + $this->drupalGet('devel/entity_test/' . $this->entity->id()); + $this->assertResponse(200); + $this->assertText('Load', 'Devel load tab is present'); + $this->assertText('Render', 'Devel load tab is present'); + $this->assertLinkByHref('devel/entity_test/' . $this->entity->id() . '/render'); + $this->drupalGet('devel/entity_test/' . $this->entity->id() . '/render'); + $this->assertResponse(200); + + // Test Devel load and render routes for entities with only canonical route + // definitions. + $this->drupalGet('devel_entity_test_canonical/' . $this->entity_canonical->id()); + $this->assertText('Devel', 'Devel tab is present'); + //TODO this fail since assertNoLinkByHref search by partial value. + //$this->assertNoLinkByHref('devel/devel_entity_test_canonical/' . $this->entity_canonical->id()); + $this->assertLinkByHref('devel/devel_entity_test_canonical/' . $this->entity_canonical->id() . '/render'); + $this->drupalGet('devel/devel_entity_test_canonical/' . $this->entity_canonical->id()); + $this->assertResponse(404); + $this->drupalGet('devel/devel_entity_test_canonical/' . $this->entity_canonical->id() . '/render'); + $this->assertResponse(200); + + // Test Devel load and render routes for entities with only edit route + // definitions. + $this->drupalGet('devel_entity_test_edit/manage/' . $this->entity_edit->id()); + $this->assertText('Devel', 'Devel tab is present'); + $this->assertLinkByHref('devel/devel_entity_test_edit/' . $this->entity_edit->id()); + $this->assertNoLinkByHref('devel/devel_entity_test_edit/' . $this->entity_edit->id() . '/render'); + $this->drupalGet('devel/devel_entity_test_edit/' . $this->entity_edit->id()); + $this->assertResponse(200); + $this->drupalGet('devel/devel_entity_test_edit/' . $this->entity_edit->id() . '/render'); + $this->assertResponse(404); + + // Test Devel load and render routes for entities with no route + // definitions. + $this->drupalGet('devel_entity_test_no_links/' . $this->entity_edit->id()); + $this->assertNoText('Devel', 'Devel tab is not present'); + $this->assertNoLinkByHref('devel/devel_entity_test_no_links/' . $this->entity_no_links->id()); + $this->assertNoLinkByHref('devel/devel_entity_test_no_links/' . $this->entity_no_links->id() . '/render'); + $this->drupalGet('devel/devel_entity_test_no_links/' . $this->entity_no_links->id()); + $this->assertResponse(404); + $this->drupalGet('devel/devel_entity_test_no_links/' . $this->entity_no_links->id() . '/render'); + $this->assertResponse(404); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Tests/DevelDumperTest.php b/sites/all/modules/contrib/dev/devel/src/Tests/DevelDumperTest.php new file mode 100644 index 000000000..bf214a0ca --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Tests/DevelDumperTest.php @@ -0,0 +1,159 @@ +drupalCreateUser(['administer site configuration', 'access devel information']); + $this->drupalLogin($admin_user); + } + + /** + * Test dumpers configuration page. + */ + public function testDumpersConfiguration() { + $this->drupalGet('admin/config/development/devel'); + + // Ensures that the dumper input is present on the config page. + $this->assertFieldByName('dumper'); + + // Ensures that the 'default' dumper is enabled by default. + $this->assertFieldChecked('edit-dumper-default'); + + // Ensures that all dumpers declared by devel are present on the config page + // and that only the available dumpers are selectable. + $dumpers = ['default', 'drupal_variable', 'firephp', 'chromephp', 'var_dumper']; + $available_dumpers = ['default', 'drupal_variable']; + + foreach ($dumpers as $dumper) { + $this->assertFieldByXPath('//input[@type="radio" and @name="dumper"]', $dumper, new FormattableMarkup('Radio button for @dumper found.', ['@dumper' => $dumper])); + if (in_array($dumper, $available_dumpers)) { + $this->assertFieldByXPath('//input[@name="dumper" and not(@disabled="disabled")]', $dumper, new FormattableMarkup('Dumper @dumper is available.', ['@dumper' => $dumper])); + } + else { + $this->assertFieldByXPath('//input[@name="dumper" and @disabled="disabled"]', $dumper, new FormattableMarkup('Dumper @dumper is disabled.', ['@dumper' => $dumper])); + } + } + + // Ensures that dumper plugins declared by other modules are present on the + // config page and that only the available dumpers are selectable. + $this->assertFieldByName('dumper', 'available_test_dumper'); + $this->assertText('Available test dumper.', 'Available dumper label is present'); + $this->assertText('Drupal dumper for testing purposes (available).', 'Available dumper description is present'); + $this->assertFieldByXPath('//input[@name="dumper" and not(@disabled="disabled")]', 'available_test_dumper', 'Available dumper input not is disabled.'); + + $this->assertFieldByName('dumper', 'not_available_test_dumper'); + $this->assertText('Not available test dumper.', 'Non available dumper label is present'); + $this->assertText('Drupal dumper for testing purposes (not available).Not available. You may need to install external dependencies for use this plugin.', 'Non available dumper description is present'); + $this->assertFieldByXPath('//input[@name="dumper" and @disabled="disabled"]', 'not_available_test_dumper', 'Non available dumper input is disabled.'); + + // Ensures that saving of the dumpers configuration works as expected. + $edit = [ + 'dumper' => 'drupal_variable', + ]; + $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + + $config = \Drupal::config('devel.settings')->get('devel_dumper'); + $this->assertEqual('drupal_variable', $config, 'The configuration options have been properly saved'); + + // Ensure that if the chosen dumper is not available (e.g. the module that + // provide it is uninstalled) the 'default' dumper appears selected in the + // config page. + \Drupal::service('module_installer')->install(['kint']); + + $this->drupalGet('admin/config/development/devel'); + $this->assertFieldByName('dumper', 'kint'); + + $edit = [ + 'dumper' => 'kint', + ]; + $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + + $config = \Drupal::config('devel.settings')->get('devel_dumper'); + $this->assertEqual('kint', $config, 'The configuration options have been properly saved'); + + \Drupal::service('module_installer')->uninstall(['kint']); + + $this->drupalGet('admin/config/development/devel'); + $this->assertNoFieldByName('dumper', 'kint'); + $this->assertFieldChecked('edit-dumper-default'); + } + + /** + * Test variable is dumped in page. + */ + function testDumpersOutput() { + $edit = [ + 'dumper' => 'available_test_dumper', + ]; + $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + + $this->drupalGet('devel_dumper_test/dump'); + $elements = $this->xpath('//body/pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::dump() Test output']); + $this->assertTrue(!empty($elements), 'Dumped message is present.'); + + $this->drupalGet('devel_dumper_test/message'); + $elements = $this->xpath('//div[contains(@class, "messages")]/pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::export() Test output']); + $this->assertTrue(!empty($elements), 'Dumped message is present.'); + + $this->drupalGet('devel_dumper_test/export'); + $elements = $this->xpath('//div[@class="layout-content"]//pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::export() Test output']); + $this->assertTrue(!empty($elements), 'Dumped message is present.'); + + $this->drupalGet('devel_dumper_test/export_renderable'); + $elements = $this->xpath('//div[@class="layout-content"]//pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::exportAsRenderable() Test output']); + $this->assertTrue(!empty($elements), 'Dumped message is present.'); + // Ensures that plugins can add libraries to the page when the + // ::exportAsRenderable() method is used. + $this->assertRaw('devel_dumper_test/css/devel_dumper_test.css'); + $this->assertRaw('devel_dumper_test/js/devel_dumper_test.js'); + + $debug_filename = file_directory_temp() . '/drupal_debug.txt'; + + $this->drupalGet('devel_dumper_test/debug'); + $file_content = file_get_contents($debug_filename); + $expected = <<AvailableTestDumper::export() Test output
+ +EOF; + $this->assertEqual($file_content, $expected, 'Dumped message is present.'); + + // Ensures that the DevelDumperManager::debug() is not access checked and + // that the dump is written in the debug file even if the user has not the + // 'access devel information' permission. + file_put_contents($debug_filename, ''); + $this->drupalLogout(); + $this->drupalGet('devel_dumper_test/debug'); + $file_content = file_get_contents($debug_filename); + $expected = <<AvailableTestDumper::export() Test output + +EOF; + $this->assertEqual($file_content, $expected, 'Dumped message is present.'); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Tests/DevelErrorHandlerTest.php b/sites/all/modules/contrib/dev/devel/src/Tests/DevelErrorHandlerTest.php new file mode 100644 index 000000000..9a8e9c6b5 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Tests/DevelErrorHandlerTest.php @@ -0,0 +1,213 @@ + 'Notice', + '@message' => 'Undefined variable: undefined', + '%function' => 'Drupal\devel\Form\SettingsForm->demonstrateErrorHandlers()', + ]; + $error_warning = [ + '%type' => 'Warning', + '@message' => 'Division by zero', + '%function' => 'Drupal\devel\Form\SettingsForm->demonstrateErrorHandlers()', + ]; + + $config = $this->config('system.logging'); + $config->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save(); + + $admin_user = $this->drupalCreateUser(['administer site configuration', 'access devel information']); + $this->drupalLogin($admin_user); + + // Ensures that the error handler config is present on the config page and + // by default the standard error handler is selected. + $error_handlers = \Drupal::config('devel.settings')->get('error_handlers'); + $this->assertEqual($error_handlers, [DEVEL_ERROR_HANDLER_STANDARD => DEVEL_ERROR_HANDLER_STANDARD]); + $this->drupalGet('admin/config/development/devel'); + $this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_STANDARD); + + // Ensures that selecting the DEVEL_ERROR_HANDLER_NONE option no error + // (raw or message) is shown on the site in case of php errors. + $edit = [ + 'error_handlers[]' => DEVEL_ERROR_HANDLER_NONE, + ]; + $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + $error_handlers = \Drupal::config('devel.settings')->get('error_handlers'); + $this->assertEqual($error_handlers, [DEVEL_ERROR_HANDLER_NONE => DEVEL_ERROR_HANDLER_NONE]); + $this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_NONE); + + $this->clickLink('notice+warning'); + $this->assertResponse(200, 'Received expected HTTP status code.'); + $this->assertNoRawErrorMessage($error_notice); + $this->assertNoRawErrorMessage($error_warning); + $this->assertNoErrorMessage($error_notice); + $this->assertNoErrorMessage($error_warning); + + // Ensures that selecting the DEVEL_ERROR_HANDLER_BACKTRACE_KINT option a + // backtrace above the rendered page is shown on the site in case of php + // errors. + $edit = [ + 'error_handlers[]' => DEVEL_ERROR_HANDLER_BACKTRACE_KINT, + ]; + $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + $error_handlers = \Drupal::config('devel.settings')->get('error_handlers'); + $this->assertEqual($error_handlers, [DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT]); + $this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_KINT); + + $this->clickLink('notice+warning'); + $this->assertResponse(200, 'Received expected HTTP status code.'); + $this->assertRawErrorMessage($error_notice); + $this->assertRawErrorMessage($error_warning); + + // Ensures that selecting the DEVEL_ERROR_HANDLER_BACKTRACE_DPM option a + // backtrace in the message area is shown on the site in case of php errors. + $edit = [ + 'error_handlers[]' => DEVEL_ERROR_HANDLER_BACKTRACE_DPM, + ]; + $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + $error_handlers = \Drupal::config('devel.settings')->get('error_handlers'); + $this->assertEqual($error_handlers, [DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM]); + $this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_DPM); + + $this->clickLink('notice+warning'); + $this->assertResponse(200, 'Received expected HTTP status code.'); + $this->assertErrorMessage($error_notice); + $this->assertErrorMessage($error_warning); + + // Ensures that when multiple handlers are selected, the output produced by + // every handler is shown on the site in case of php errors. + $edit = [ + 'error_handlers[]' => [ + DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM, + DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT, + ] + ]; + $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + $error_handlers = \Drupal::config('devel.settings')->get('error_handlers'); + $this->assertEqual($error_handlers, [ + DEVEL_ERROR_HANDLER_BACKTRACE_DPM => DEVEL_ERROR_HANDLER_BACKTRACE_DPM, + DEVEL_ERROR_HANDLER_BACKTRACE_KINT => DEVEL_ERROR_HANDLER_BACKTRACE_KINT, + ]); + $this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_DPM); + $this->assertOptionSelected('edit-error-handlers', DEVEL_ERROR_HANDLER_BACKTRACE_KINT); + + $this->clickLink('notice+warning'); + $this->assertResponse(200, 'Received expected HTTP status code.'); + $this->assertRawErrorMessage($error_notice); + $this->assertRawErrorMessage($error_warning); + $this->assertErrorMessage($error_notice); + $this->assertErrorMessage($error_warning); + + // Ensures that setting the error reporting to all the output produced by + // handlers is shown on the site in case of php errors. + $config->set('error_level', ERROR_REPORTING_DISPLAY_ALL)->save(); + $this->clickLink('notice+warning'); + $this->assertResponse(200, 'Received expected HTTP status code.'); + $this->assertRawErrorMessage($error_notice); + $this->assertRawErrorMessage($error_warning); + $this->assertErrorMessage($error_notice); + $this->assertErrorMessage($error_warning); + + // Ensures that setting the error reporting to some the output produced by + // handlers is shown on the site in case of php errors. + $config->set('error_level', ERROR_REPORTING_DISPLAY_SOME)->save(); + $this->assertResponse(200, 'Received expected HTTP status code.'); + $this->clickLink('notice+warning'); + $this->assertRawErrorMessage($error_notice); + $this->assertRawErrorMessage($error_warning); + $this->assertErrorMessage($error_notice); + $this->assertErrorMessage($error_warning); + + // Ensures that setting the error reporting to none the output produced by + // handlers is not shown on the site in case of php errors. + $config->set('error_level', ERROR_REPORTING_HIDE)->save(); + $this->clickLink('notice+warning'); + $this->assertResponse(200, 'Received expected HTTP status code.'); + $this->assertNoRawErrorMessage($error_notice); + $this->assertNoRawErrorMessage($error_warning); + $this->assertNoErrorMessage($error_notice); + $this->assertNoErrorMessage($error_warning); + + // The errors are expected. Do not interpret them as a test failure. + // Not using File API; a potential error must trigger a PHP warning. + unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log'); + } + + /** + * Helper function: assert that the error message is found. + * + * @param array $error + * The error to check. + */ + protected function assertRawErrorMessage(array $error) { + $message = new FormattableMarkup('%type: @message in %function (line ', $error); + $this->assertRaw($message, new FormattableMarkup('Found raw error message: @message.', ['@message' => $message])); + } + + /** + * Helper function: assert that the error message is not found. + * + * + * @param array $error + * The error to check. + */ + protected function assertNoRawErrorMessage(array $error) { + $message = new FormattableMarkup('%type: @message in %function (line ', $error); + $this->assertNoRaw($message, new FormattableMarkup('Did not find raw error message: @message.', ['@message' => $message])); + } + + /** + * Helper function: assert that the error message is found. + * + * @param array $error + * The error to check. + */ + protected function assertErrorMessage(array $error) { + $pattern = '//div[contains(@class, "messages--warning")]//pre[contains(., :content)]'; + $message = new FormattableMarkup('%type: @message in %function (line ', $error); + $message = html_entity_decode(strip_tags((string) $message)); + $xpath = $this->xpath($pattern, [':content' => $message]); + $this->assertTrue(!empty($xpath), new FormattableMarkup('Found error message: @message.', ['@message' => $message])); + } + + /** + * Helper function: assert that the error message is not found. + * + * @param array $error + * The error to check. + */ + protected function assertNoErrorMessage(array $error) { + $pattern = '//div[contains(@class, "messages--warning")]//pre[contains(., :content)]'; + $message = new FormattableMarkup('%type: @message in %function (line ', $error); + $message = html_entity_decode(strip_tags((string) $message)); + $xpath = $this->xpath($pattern, [':content' => $message]); + $this->assertTrue(empty($xpath), new FormattableMarkup('Found error message: @message.', ['@message' => $message])); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Tests/DevelMailTest.php b/sites/all/modules/contrib/dev/devel/src/Tests/DevelMailTest.php new file mode 100644 index 000000000..9e285c922 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Tests/DevelMailTest.php @@ -0,0 +1,48 @@ + 'postmaster@example.com', + 'X-stupid' => 'dumb', + ); + $message['body'] = "I am the body of this message"; + $d = new DevelMailLog(); + + $filename = $d->getFileName($message); + $content = $d->composeMessage($message); + $expected_filename = $d->getOutputDirectory() . '/drupal@example.com-Test_mail-' . date('y-m-d_his') . '.mail.txt'; + $this->assertEqual($filename, $expected_filename); + $content = str_replace("\r", '', $content); + $this->assertEqual($content, 'From: postmaster@example.com +X-stupid: dumb +To: drupal@example.com +Subject: Test mail + +I am the body of this message'); + } +} diff --git a/sites/all/modules/contrib/dev/devel/src/Tests/DevelMenuLinksTest.php b/sites/all/modules/contrib/dev/devel/src/Tests/DevelMenuLinksTest.php new file mode 100644 index 000000000..fdf54ebd7 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Tests/DevelMenuLinksTest.php @@ -0,0 +1,147 @@ +drupalPlaceBlock('system_menu_block:devel'); + $this->drupalPlaceBlock('page_title_block'); + + $this->develUser = $this->drupalCreateUser(['access devel information', 'administer site configuration']); + $this->drupalLogin($this->develUser); + } + + /** + * Tests CSFR protected links. + */ + public function testCsrfProtectedLinks() { + // Ensure CSRF link are not accessible directly. + $this->drupalGet('devel/run-cron'); + $this->assertResponse(403); + $this->drupalGet('devel/cache/clear'); + $this->assertResponse(403); + + // Ensure clear cache link works properly. + $this->assertLink('Cache clear'); + $this->clickLink('Cache clear'); + $this->assertText('Cache cleared.'); + + // Ensure run cron link works properly. + $this->assertLink('Run cron'); + $this->clickLink('Run cron'); + $this->assertText('Cron ran successfully.'); + + // Ensure CSRF protected links work properly after change session. + $this->drupalLogout(); + $this->drupalLogin($this->develUser); + + $this->assertLink('Cache clear'); + $this->clickLink('Cache clear'); + $this->assertText('Cache cleared.'); + + $this->assertLink('Run cron'); + $this->clickLink('Run cron'); + $this->assertText('Cron ran successfully.'); + } + + /** + * Tests redirect destination links. + */ + public function testRedirectDestinationLinks() { + // By default, in the testing profile, front page is the user canonical URI. + // For better testing do not use the default frontpage. + $url = Url::fromRoute('devel.simple_page'); + $destination = Url::fromRoute('devel.simple_page', [], ['absolute' => FALSE]); + + $this->drupalGet($url); + $this->assertLink(t('Reinstall Modules')); + $this->clickLink(t('Reinstall Modules')); + $this->assertUrl('devel/reinstall', ['query' => ['destination' => $destination->toString()]]); + + $this->drupalGet($url); + $this->assertLink(t('Rebuild Menu')); + $this->clickLink(t('Rebuild Menu')); + $this->assertUrl('devel/menu/reset', ['query' => ['destination' => $destination->toString()]]); + + $this->drupalGet($url); + $this->assertLink(t('Cache clear')); + $this->clickLink(t('Cache clear')); + $this->assertText('Cache cleared.'); + $this->assertUrl($url); + + $this->drupalGet($url); + $this->assertLink(t('Run cron')); + $this->clickLink(t('Run cron')); + $this->assertText(t('Cron ran successfully.')); + $this->assertUrl($url); + } + + /** + * Tests menu item link. + */ + public function testMenuItemLink() { + // Ensures that devel menu item works properly. + $url = $this->develUser->toUrl(); + $path = '/' . $url->getInternalPath(); + + $this->drupalGet($url); + $this->clickLink(t('Menu Item')); + $this->assertResponse(200); + $this->assertText('Menu item'); + $this->assertUrl('devel/menu/item', ['query' => ['path' => $path]]); + + // Ensures that devel menu item works properly even when dynamic cache is + // enabled. + $url = Url::fromRoute('devel.simple_page'); + $path = '/' . $url->getInternalPath(); + + $this->drupalGet($url); + $this->clickLink(t('Menu Item')); + $this->assertResponse(200); + $this->assertText('Menu item'); + $this->assertUrl('devel/menu/item', ['query' => ['path' => $path]]); + + // Ensures that if no 'path' query string is passed devel menu item does + // not return errors. + $this->drupalGet('devel/menu/item'); + $this->assertResponse(200); + $this->assertText('Menu item'); + + // Ensures that devel menu item is accessible ony to users with the + // adequate permissions. + $this->drupalLogout(); + $this->drupalGet('devel/menu/item'); + $this->assertResponse(403); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Tests/DevelRebuildMenusTest.php b/sites/all/modules/contrib/dev/devel/src/Tests/DevelRebuildMenusTest.php new file mode 100644 index 000000000..95abf19d2 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Tests/DevelRebuildMenusTest.php @@ -0,0 +1,41 @@ +drupalCreateUser(array('administer site configuration')); + $this->drupalLogin($web_user); + } + + /** + * Test routes rebuild. + */ + public function testDevelRebuildMenus() { + $this->drupalGet('devel/menu/reset'); + $this->assertResponse(200); + $this->drupalPostForm('devel/menu/reset', array(), t('Rebuild')); + $this->assertText(t('The menu router has been rebuilt.')); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Tests/DevelReinstallTest.php b/sites/all/modules/contrib/dev/devel/src/Tests/DevelReinstallTest.php new file mode 100644 index 000000000..9ff5efa03 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Tests/DevelReinstallTest.php @@ -0,0 +1,60 @@ +drupalCreateUser(array('administer site configuration')); + $this->drupalLogin($web_user); + } + + /** + * Reinstall modules. + */ + public function testDevelReinstallModules() { + // Minimal profile enables only dblog, block and node. + $modules = array('dblog', 'block'); + + // Needed for compare correctly the message. + sort($modules); + + $this->drupalGet('devel/reinstall'); + + // Prepare field data in an associative array + $edit = array(); + foreach ($modules as $module) { + $edit["reinstall[$module]"] = TRUE; + } + + $this->drupalPostForm('devel/reinstall', $edit, t('Reinstall')); + $this->assertText(t('Uninstalled and installed: @names.', array('@names' => implode(', ', $modules)))); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Tests/DevelStateEditorTest.php b/sites/all/modules/contrib/dev/devel/src/Tests/DevelStateEditorTest.php new file mode 100644 index 000000000..16c2bc4a8 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Tests/DevelStateEditorTest.php @@ -0,0 +1,210 @@ +state = $this->container->get('state'); + + $this->develUser = $this->drupalCreateUser(['access devel information']); + $this->adminUser = $this->drupalCreateUser(['access devel information', 'administer site configuration']); + } + + /** + * Tests state listing. + */ + public function testStateListing() { + // Ensure that state listing page is accessible only by users with the + // adequate permissions. + $this->drupalGet('devel/state'); + $this->assertResponse(403); + + $this->drupalLogin($this->develUser); + $this->drupalGet('devel/state'); + $this->assertResponse(200); + $this->assertText(t('State editor')); + + // Ensure that the state variables table is visible. + $table = $this->xpath('//table[contains(@class, "devel-state-list")]'); + $this->assertTrue($table, 'State list table found.'); + + // Ensure that all state variables are listed in the table. + $states = \Drupal::keyValue('state')->getAll(); + $rows = $this->xpath('//table[contains(@class, "devel-state-list")]//tbody//tr'); + $this->assertEqual(count($rows), count($states), 'All states are listed in the table.'); + + // Ensure that the added state variables are listed in the table. + $this->state->set('devel.simple', 'Hello!'); + $this->drupalGet('devel/state'); + $this->assertFieldByXpath('//table[contains(@class, "devel-state-list")]//tbody//td', 'devel.simple', 'Label found for the added state.'); + + $thead_xpath = '//table[contains(@class, "devel-state-list")]/thead/tr/th'; + $action_xpath = '//table[contains(@class, "devel-state-list")]//ul[@class="dropbutton"]/li/a'; + + // Ensure that the operations column and the actions buttons are not + // available for user without 'administer site configuration' permission. + $elements = $this->xpath($thead_xpath); + $this->assertEqual(count($elements), 2, 'Correct number of table header cells found.'); + + $expected_items = ['Name', 'Value']; + foreach ($elements as $key => $element) { + $this->assertIdentical((string) $element[0], $expected_items[$key]); + } + + $this->assertFalse($this->xpath($action_xpath), 'Action buttons are not visible.'); + + // Ensure that the operations column and the actions buttons are + // available for user with 'administer site configuration' permission. + $this->drupalLogin($this->adminUser); + $this->drupalGet('devel/state'); + + $elements = $this->xpath($thead_xpath); + $this->assertEqual(count($elements), 3, 'Correct number of table header cells found.'); + + $expected_items = ['Name', 'Value', 'Operations']; + foreach ($elements as $key => $element) { + $this->assertIdentical((string) $element[0], $expected_items[$key]); + } + + $this->assertTrue($this->xpath($action_xpath), 'Action buttons are visible.'); + + // Test that the edit button works properly. + $this->clickLink(t('Edit')); + $this->assertResponse(200); + } + + /** + * Tests state edit. + */ + public function testStateEdit() { + // Create some state variables for the test. + $this->state->set('devel.simple', 0); + $this->state->set('devel.array', ['devel' => 'value']); + $this->state->set('devel.object', $this->randomObject()); + + // Ensure that state edit form is accessible only by users with the + // adequate permissions. + $this->drupalLogin($this->develUser); + $this->drupalGet('devel/state/edit/devel.simple'); + $this->assertResponse(403); + + $this->drupalLogin($this->adminUser); + + // Ensure that accessing an un-existent state variable cause a warning + // message. + $this->drupalGet('devel/state/edit/devel.unknown'); + $this->assertText(t('State @name does not exist in the system.', ['@name' => 'devel.unknown'])); + + // Ensure that state variables that contain simple type can be edited and + // saved. + $this->drupalGet('devel/state/edit/devel.simple'); + $this->assertResponse(200); + $this->assertText(t('Edit state variable: @name', ['@name' => 'devel.simple'])); + $this->assertInputNotDisabledById('edit-new-value'); + $this->assertInputNotDisabledById('edit-submit'); + + $edit = ['new_value' => 1]; + $this->drupalPostForm('devel/state/edit/devel.simple', $edit, t('Save')); + $this->assertText(t('Variable @name was successfully edited.', ['@name' => 'devel.simple'])); + $this->assertEqual(1, $this->state->get('devel.simple')); + + // Ensure that state variables that contain array can be edited and saved + // and the new value is properly validated. + $this->drupalGet('devel/state/edit/devel.array'); + $this->assertResponse(200); + $this->assertText(t('Edit state variable: @name', ['@name' => 'devel.array'])); + $this->assertInputNotDisabledById('edit-new-value'); + $this->assertInputNotDisabledById('edit-submit'); + + // Try to save an invalid yaml input. + $edit = ['new_value' => 'devel: \'value updated']; + $this->drupalPostForm('devel/state/edit/devel.array', $edit, t('Save')); + $this->assertText(t('Invalid input:')); + + $edit = ['new_value' => 'devel: \'value updated\'']; + $this->drupalPostForm('devel/state/edit/devel.array', $edit, t('Save')); + $this->assertText(t('Variable @name was successfully edited.', ['@name' => 'devel.array'])); + $this->assertEqual(['devel' => 'value updated'], $this->state->get('devel.array')); + + // Ensure that state variables that contain objects cannot be edited. + $this->drupalGet('devel/state/edit/devel.object'); + $this->assertResponse(200); + $this->assertText(t('Edit state variable: @name', ['@name' => 'devel.object'])); + $this->assertText(t('Only simple structures are allowed to be edited. State @name contains objects.', ['@name' => 'devel.object'])); + $this->assertInputDisabledById('edit-new-value'); + $this->assertInputDisabledById('edit-submit'); + + // Ensure that the cancel link works as expected. + $this->clickLink(t('Cancel')); + $this->assertUrl('devel/state'); + } + + /** + * Helper function for check if an input is disabled. + * + * @param string $id + * The ID of the input. + */ + protected function assertInputDisabledById($id) { + $message = new FormattableMarkup('The input %id is disabled.', ['%id' => $id]); + $xpath = '//textarea[@id=:id and @disabled="disabled"]|//input[@id=:id and @disabled="disabled"]|//select[@id=:id and @disabled="disabled"]'; + $query = $this->buildXPathQuery($xpath, [':id' => $id]); + $this->assertFieldByXPath($query, NULL, $message); + } + + /** + * Helper function for check if an input is not disabled. + * + * @param string $id + * The ID of the input. + */ + protected function assertInputNotDisabledById($id) { + $message = new FormattableMarkup('The input %id is not disabled.', ['%id' => $id]); + $xpath = '//textarea[@id=:id and not(@disabled="disabled")]|//input[@id=:id and not(@disabled="disabled")]|//select[@id=:id and not(@disabled="disabled")]'; + $query = $this->buildXPathQuery($xpath, [':id' => $id]); + $this->assertFieldByXPath($query, NULL, $message); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Tests/DevelSwitchUserTest.php b/sites/all/modules/contrib/dev/devel/src/Tests/DevelSwitchUserTest.php new file mode 100644 index 000000000..68f6136db --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Tests/DevelSwitchUserTest.php @@ -0,0 +1,313 @@ +block = $this->drupalPlaceBlock('devel_switch_user', ['id' => 'switch-user']); + + $this->develUser = $this->drupalCreateUser(['access devel information', 'switch users']); + $this->switchUser = $this->drupalCreateUser(['switch users']); + $this->webUser = $this->drupalCreateUser(); + } + + /** + * Tests switch user. + */ + public function testSwitchUser() { + $this->drupalLogin($this->webUser); + + $this->drupalGet(''); + $this->assertNoText($this->block->label(), 'Block title was not found.'); + + // Ensure that a token is required to switch user. + $this->drupalGet('/devel/switch/' . $this->webUser->getUsername()); + $this->assertResponse(403); + + $this->drupalLogin($this->develUser); + + $this->drupalGet(''); + $this->assertText($this->block->label(), 'Block title was found.'); + + // Ensure that if name in not passed the controller returns access denied. + $this->drupalGet('/devel/switch'); + $this->assertResponse(403); + + // Ensure that a token is required to switch user. + $this->drupalGet('/devel/switch/' . $this->switchUser->getUsername()); + $this->assertResponse(403); + + // Switch to another user account. + $this->drupalGet('/user/' . $this->switchUser->id()); + $this->clickLink($this->switchUser->getUsername()); + $this->assertSessionByUid($this->switchUser->id()); + $this->assertNoSessionByUid($this->develUser->id()); + + // Switch back to initial account. + $this->clickLink($this->develUser->getUsername()); + $this->assertNoSessionByUid($this->switchUser->id()); + $this->assertSessionByUid($this->develUser->id()); + + // Use the search form to switch to another account. + $edit = ['userid' => $this->switchUser->getUsername()]; + $this->drupalPostForm(NULL, $edit, t('Switch')); + $this->assertSessionByUid($this->switchUser->id()); + $this->assertNoSessionByUid($this->develUser->id()); + } + + /** + * Tests the switch user block configuration. + */ + public function testSwitchUserBlockConfiguration() { + $anonymous = \Drupal::config('user.settings')->get('anonymous'); + + // Create some users for the test. + for ($i = 0; $i < 12; $i++) { + $this->drupalCreateUser(); + } + + $this->drupalLogin($this->develUser); + + $this->drupalGet(''); + $this->assertText($this->block->label(), 'Block title was found.'); + + // Ensure that block default configuration is effectively used. The block + // default configuration is the following: + // - list_size : 12 + // - include_anon : FALSE + // - show_form : TRUE + $this->assertSwitchUserSearchForm(); + $this->assertSwitchUserListCount(12); + $this->assertSwitchUserListNoContainsUser($anonymous); + + // Ensure that changing the list_size configuration property the number of + // user displayed in the list change. + $this->setBlockConfiguration('list_size', 4); + $this->drupalGet(''); + $this->assertSwitchUserListCount(4); + + // Ensure that changing the include_anon configuration property the + // anonymous user is displayed in the list. + $this->setBlockConfiguration('include_anon', TRUE); + $this->drupalGet(''); + $this->assertSwitchUserListContainsUser($anonymous); + + // Ensure that changing the show_form configuration property the + // form is not displayed. + $this->setBlockConfiguration('show_form', FALSE); + $this->drupalGet(''); + $this->assertSwitchUserNoSearchForm(); + } + + /** + * Test the user list items. + */ + public function testSwitchUserListItems() { + $anonymous = \Drupal::config('user.settings')->get('anonymous'); + + $this->setBlockConfiguration('list_size', 2); + + // Login as web user so we are sure that this account is prioritized + // in the list if not enougth user with 'switch users' permission are + // present. + $this->drupalLogin($this->webUser); + + $this->drupalLogin($this->develUser); + $this->drupalGet(''); + + // Ensure that user with 'switch users' permission are prioritized. + $this->assertSwitchUserListCount(2); + $this->assertSwitchUserListContainsUser($this->develUser->getUsername()); + $this->assertSwitchUserListContainsUser($this->switchUser->getUsername()); + + // Ensure that blocked users are not shown in the list. + $this->switchUser->set('status', 0)->save(); + $this->drupalGet(''); + $this->assertSwitchUserListCount(2); + $this->assertSwitchUserListContainsUser($this->develUser->getUsername()); + $this->assertSwitchUserListContainsUser($this->webUser->getUsername()); + $this->assertSwitchUserListNoContainsUser($this->switchUser->getUsername()); + + // Ensure that anonymous user are prioritized if include_anon is set to true. + $this->setBlockConfiguration('include_anon', TRUE); + $this->drupalGet(''); + $this->assertSwitchUserListCount(2); + $this->assertSwitchUserListContainsUser($this->develUser->getUsername()); + $this->assertSwitchUserListContainsUser($anonymous); + + // Ensure that the switch user block works properly even if no prioritized + // users are found (special handling for user 1). + $this->drupalLogout(); + $this->develUser->delete(); + + $this->drupalLogin($this->rootUser); + $this->drupalGet(''); + $this->assertSwitchUserListCount(2); + $this->assertSwitchUserListContainsUser($this->rootUser->getUsername()); + $this->assertSwitchUserListContainsUser($anonymous); + + // Ensure that the switch user block works properly even if no roles have + // the 'switch users' permission associated (special handling for user 1). + $roles = user_roles(TRUE, 'switch users'); + \Drupal::entityTypeManager()->getStorage('user_role')->delete($roles); + + $this->drupalGet(''); + $this->assertSwitchUserListCount(2); + $this->assertSwitchUserListContainsUser($this->rootUser->getUsername()); + $this->assertSwitchUserListContainsUser($anonymous); + } + + /** + * Helper function for verify the number of items shown in the user list. + * + * @param int $number + * The expected numer of items. + */ + public function assertSwitchUserListCount($number) { + $result = $this->xpath('//div[@id=:block]//ul/li/a', [':block' => 'block-switch-user']); + $this->assert(count($result) == $number, 'The number of users shown in switch user is correct.'); + } + + /** + * Helper function for verify if the user list contains a username. + * + * @param string $username + * The username to check. + */ + public function assertSwitchUserListContainsUser($username) { + $result = $this->xpath('//div[@id=:block]//ul/li/a[normalize-space()=:user]', [':block' => 'block-switch-user', ':user' => $username]); + $this->assert(count($result) > 0, new FormattableMarkup('User "%user" is included in the switch user list.', ['%user' => $username])); + } + + /** + * Helper function for verify if the user list not contains a username. + * + * @param string $username + * The username to check. + */ + public function assertSwitchUserListNoContainsUser($username) { + $result = $this->xpath('//div[@id=:block]//ul/li/a[normalize-space()=:user]', [':block' => 'block-switch-user', ':user' => $username]); + $this->assert(count($result) == 0, new FormattableMarkup('User "%user" is not included in the switch user list.', ['%user' => $username])); + } + + /** + * Helper function for verify if the search form is shown. + */ + public function assertSwitchUserSearchForm() { + $result = $this->xpath('//div[@id=:block]//form[contains(@class, :form)]', [':block' => 'block-switch-user', ':form' => 'devel-switchuser-form']); + $this->assert(count($result) > 0, 'The search form is shown.'); + } + + /** + * Helper function for verify if the search form is not shown. + */ + public function assertSwitchUserNoSearchForm() { + $result = $this->xpath('//div[@id=:block]//form[contains(@class, :form)]', [':block' => 'block-switch-user', ':form' => 'devel-switchuser-form']); + $this->assert(count($result) == 0, 'The search form is not shown.'); + } + + /** + * Protected helper method to set the test block's configuration. + */ + protected function setBlockConfiguration($key, $value) { + $block = $this->block->getPlugin(); + $block->setConfigurationValue($key, $value); + $this->block->save(); + } + + /** + * Asserts that there is a session for a given user ID. + * + * Based off masquarade module. + * + * @param int $uid + * The user ID for which to find a session record. + * + * TODO find a cleaner way to do this check. + */ + protected function assertSessionByUid($uid) { + $query = \Drupal::database()->select('sessions'); + $query->fields('sessions', array('uid')); + $query->condition('uid', $uid); + $result = $query->execute()->fetchAll(); + + if (empty($result)) { + $this->fail(new FormattableMarkup('No session found for uid @uid', array('@uid' => $uid))); + } + elseif (count($result) > 1) { + // If there is more than one session, then that must be unexpected. + $this->fail("Found more than 1 session for uid $uid."); + } + else { + $this->pass("Found session for uid $uid."); + } + } + + /** + * Asserts that no session exists for a given uid. + * + * Based off masquarade module. + * + * @param int $uid + * The user ID to assert. + * + * TODO find a cleaner way to do this check. + */ + protected function assertNoSessionByUid($uid) { + $query = \Drupal::database()->select('sessions'); + $query->fields('sessions', array('uid')); + $query->condition('uid', $uid); + $result = $query->execute()->fetchAll(); + $this->assert(empty($result), "No session for uid $uid found."); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/src/Twig/Extension/Debug.php b/sites/all/modules/contrib/dev/devel/src/Twig/Extension/Debug.php new file mode 100644 index 000000000..9922e6106 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/src/Twig/Extension/Debug.php @@ -0,0 +1,201 @@ +dumper = $dumper; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'devel_debug'; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() { + $functions = []; + + foreach (['devel_dump', 'kpr'] as $function) { + $functions[] = new \Twig_SimpleFunction($function, [$this, 'dump'], [ + 'is_safe' => ['html'], + 'needs_environment' => TRUE, + 'needs_context' => TRUE, + 'is_variadic' => TRUE, + ]); + } + + foreach (['devel_message', 'dpm', 'dsm'] as $function) { + $functions[] = new \Twig_SimpleFunction($function, [$this, 'message'], [ + 'is_safe' => ['html'], + 'needs_environment' => TRUE, + 'needs_context' => TRUE, + 'is_variadic' => TRUE, + ]); + } + + foreach (['devel_breakpoint'] as $function) { + $functions[] = new \Twig_SimpleFunction($function, [$this, 'breakpoint'], [ + 'needs_environment' => TRUE, + 'needs_context' => TRUE, + 'is_variadic' => TRUE, + ]); + } + + return $functions; + } + + /** + * Provides debug function to Twig templates. + * + * Handles 0, 1, or multiple arguments. + * + * @param \Twig_Environment $env + * The twig environment instance. + * @param array $context + * An array of parameters passed to the template. + * @param array $args + * An array of parameters passed the function. + * + * @return string + * String representation of the input variables. + * + * @see \Drupal\devel\DevelDumperManager::dump() + */ + public function dump(\Twig_Environment $env, array $context, array $args = []) { + if (!$env->isDebug()) { + return; + } + + ob_start(); + + // No arguments passed, display full Twig context. + if (empty($args)) { + $context_variables = $this->getContextVariables($context); + $this->dumper->dump($context_variables, 'Twig context'); + } + else { + foreach ($args as $variable) { + $this->dumper->dump($variable); + } + } + + return ob_get_clean(); + } + + /** + * Provides debug function to Twig templates. + * + * Handles 0, 1, or multiple arguments. + * + * @param \Twig_Environment $env + * The twig environment instance. + * @param array $context + * An array of parameters passed to the template. + * @param array $args + * An array of parameters passed the function. + * + * @return void + * + * @see \Drupal\devel\DevelDumperManager::message() + */ + public function message(\Twig_Environment $env, array $context, array $args = []) { + if (!$env->isDebug()) { + return; + } + + // No arguments passed, display full Twig context. + if (empty($args)) { + $context_variables = $this->getContextVariables($context); + $this->dumper->message($context_variables, 'Twig context'); + } + else { + foreach ($args as $variable) { + $this->dumper->message($variable); + } + } + + } + + /** + * Provides XDebug integration for Twig templates. + * + * To use this features simply put the following statement in the template + * of interest: + * + * @code + * {{ devel_breakpoint() }} + * @endcode + * + * When the template is evaluated is made a call to a dedicated method in + * devel twig debug extension in which is used xdebug_break(), that emits a + * breakpoint to the debug client (the debugger break on the specific line as + * if a normal file/line breakpoint was set on this line). + * In this way you'll be able to inspect any variables available in the + * template (environment, context, specific variables etc..) in your IDE. + * + * @param \Twig_Environment $env + * The twig environment instance. + * @param array $context + * An array of parameters passed to the template. + * @param array $args + * An array of parameters passed the function. + */ + public function breakpoint(\Twig_Environment $env, array $context, array $args = []) { + if (!$env->isDebug()) { + return; + } + + if (function_exists('xdebug_break')) { + xdebug_break(); + } + } + + /** + * Filters the Twig context variable. + * + * @param array $context + * The Twig context. + * + * @return array + * An array Twig context variables. + */ + protected function getContextVariables(array $context) { + $context_variables = []; + foreach ($context as $key => $value) { + if (!$value instanceof \Twig_Template) { + $context_variables[$key] = $value; + } + } + return $context_variables; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/css/devel_dumper_test.css b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/css/devel_dumper_test.css new file mode 100644 index 000000000..e69de29bb diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.info.yml b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.info.yml new file mode 100644 index 000000000..b68932872 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.info.yml @@ -0,0 +1,12 @@ +name: 'Devel dumper test module' +type: module +description: 'Test pluggable dumpers.' +package: Testing +# version: VERSION +# core: 8.x + +# Information added by Drupal.org packaging script on 2016-09-03 +version: '8.x-1.0-alpha1+23-dev' +core: '8.x' +project: 'devel' +datestamp: 1472897643 diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.libraries.yml b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.libraries.yml new file mode 100644 index 000000000..8e8c2ad88 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.libraries.yml @@ -0,0 +1,7 @@ +devel_dumper_test: + version: 0 + css: + theme: + css/devel_dumper_test.css: {} + js: + js/devel_dumper_test.js: {} diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.routing.yml b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.routing.yml new file mode 100644 index 000000000..650b3370a --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.routing.yml @@ -0,0 +1,39 @@ +devel_dumper_test.dump: + path: '/devel_dumper_test/dump' + defaults: + _controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::dump' + _title: 'Devel Dumper Test' + requirements: + _permission: 'access devel information' + +devel_dumper_test.message: + path: '/devel_dumper_test/message' + defaults: + _controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::message' + _title: 'Devel Dumper Test' + requirements: + _permission: 'access devel information' + +devel_dumper_test.export: + path: '/devel_dumper_test/export' + defaults: + _controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::export' + _title: 'Devel Dumper Test' + requirements: + _permission: 'access devel information' + +devel_dumper_test.export_renderable: + path: '/devel_dumper_test/export_renderable' + defaults: + _controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::exportRenderable' + _title: 'Devel Dumper Test' + requirements: + _permission: 'access devel information' + +devel_dumper_test.debug: + path: '/devel_dumper_test/debug' + defaults: + _controller: '\Drupal\devel_dumper_test\Controller\DumperTestController::debug' + _title: 'Devel Dumper Test' + requirements: + _access: 'TRUE' diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/js/devel_dumper_test.js b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/js/devel_dumper_test.js new file mode 100644 index 000000000..e69de29bb diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Controller/DumperTestController.php b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Controller/DumperTestController.php new file mode 100644 index 000000000..2a78e6e37 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Controller/DumperTestController.php @@ -0,0 +1,89 @@ +dumper = $devel_dumper_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('devel.dumper') + ); + } + + /** + * @return array + */ + public function dump() { + $this->dumper->dump('Test output'); + + return [ + '#markup' => 'test', + ]; + } + + /** + * @return array + */ + public function message() { + $this->dumper->message('Test output'); + + return [ + '#markup' => 'test', + ]; + } + + /** + * @return array + */ + public function debug() { + $this->dumper->debug('Test output'); + + return [ + '#markup' => 'test', + ]; + } + + /** + * @return array + */ + public function export() { + return [ + '#markup' => $this->dumper->export('Test output'), + ]; + } + + /** + * @return array + */ + public function exportRenderable() { + return $this->dumper->exportAsRenderable('Test output'); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Plugin/Devel/Dumper/AvailableTestDumper.php b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Plugin/Devel/Dumper/AvailableTestDumper.php new file mode 100644 index 000000000..b15f5673a --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Plugin/Devel/Dumper/AvailableTestDumper.php @@ -0,0 +1,60 @@ +' . 'AvailableTestDumper::dump() ' . $input . ''; + echo $input; + } + + /** + * {@inheritdoc} + */ + public function export($input, $name = NULL) { + // Add a predetermined string to $input to check if this dumper has been + // selected successfully. + $input = '
' . 'AvailableTestDumper::export() ' . $input . '
'; + return $this->setSafeMarkup($input); + } + + /** + * {@inheritdoc} + */ + public function exportAsRenderable($input, $name = NULL) { + // Add a predetermined string to $input to check if this dumper has been + // selected successfully. + $input = '
' . 'AvailableTestDumper::exportAsRenderable() ' . $input . '
'; + + return [ + '#attached' => [ + 'library' => ['devel_dumper_test/devel_dumper_test'] + ], + '#markup' => $this->setSafeMarkup($input), + ]; + } + + /** + * {@inheritdoc} + */ + public static function checkRequirements() { + return TRUE; + } +} diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Plugin/Devel/Dumper/NotAvailableTestDumper.php b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Plugin/Devel/Dumper/NotAvailableTestDumper.php new file mode 100644 index 000000000..2b0d886ee --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/src/Plugin/Devel/Dumper/NotAvailableTestDumper.php @@ -0,0 +1,41 @@ +' . $input . ''; + echo $input; + } + + /** + * {@inheritdoc} + */ + public function export($input, $name = NULL) { + $input = '
' . $input . '
'; + return $this->setSafeMarkup($input); + } + + /** + * {@inheritdoc} + */ + public static function checkRequirements() { + return FALSE; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.info.yml b/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.info.yml new file mode 100644 index 000000000..9011e5668 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.info.yml @@ -0,0 +1,16 @@ +name: 'Devel entity test module' +type: module +description: 'Provides entity types for Devel tests.' +package: Testing +# version: VERSION +# core: 8.x +dependencies: + - field + - text + - entity_test + +# Information added by Drupal.org packaging script on 2016-09-03 +version: '8.x-1.0-alpha1+23-dev' +core: '8.x' +project: 'devel' +datestamp: 1472897643 diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.links.task.yml b/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.links.task.yml new file mode 100644 index 000000000..052baa9c8 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.links.task.yml @@ -0,0 +1,2 @@ +devel_entity_test.local_tasks: + deriver: 'Drupal\devel_entity_test\Plugin\Derivative\DevelEntityTestLocalTasks' diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.module b/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.module new file mode 100644 index 000000000..4be887b29 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.module @@ -0,0 +1,29 @@ +getDefinitions(); + foreach ($entity_info as $entity_type => $info) { + if ($entity_info[$entity_type]->getProvider() == 'devel_entity_test_canonical' && !isset($view_modes[$entity_type])) { + $view_modes[$entity_type] = array( + 'full' => array( + 'label' => t('Full object'), + 'status' => TRUE, + 'cache' => TRUE, + ), + 'teaser' => array( + 'label' => t('Teaser'), + 'status' => TRUE, + 'cache' => TRUE, + ), + ); + } + } +} diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/src/Entity/DevelEntityTestCanonical.php b/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/src/Entity/DevelEntityTestCanonical.php new file mode 100644 index 000000000..e7ed04f3f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/src/Entity/DevelEntityTestCanonical.php @@ -0,0 +1,44 @@ +derivatives = array(); + + $this->derivatives['devel_entity_test_canonical.canonical'] = array(); + $this->derivatives['devel_entity_test_canonical.canonical']['base_route'] = "entity.devel_entity_test_canonical.canonical"; + $this->derivatives['devel_entity_test_canonical.canonical']['route_name'] = "entity.devel_entity_test_canonical.canonical"; + $this->derivatives['devel_entity_test_canonical.canonical']['title'] = 'View'; + + $this->derivatives['devel_entity_test_edit.edit'] = array(); + $this->derivatives['devel_entity_test_edit.edit']['base_route'] = "entity.devel_entity_test_edit.edit_form"; + $this->derivatives['devel_entity_test_edit.edit']['route_name'] = "entity.devel_entity_test_edit.edit_form"; + $this->derivatives['devel_entity_test_edit.edit']['title'] = 'Edit'; + + return parent::getDerivativeDefinitions($base_plugin_definition); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.info.yml b/sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.info.yml new file mode 100644 index 000000000..a348c5ef8 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.info.yml @@ -0,0 +1,12 @@ +name: 'Devel test module' +type: module +description: 'Support module for Devel testing.' +package: Testing +# version: VERSION +# core: 8.x + +# Information added by Drupal.org packaging script on 2016-09-03 +version: '8.x-1.0-alpha1+23-dev' +core: '8.x' +project: 'devel' +datestamp: 1472897643 diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.routing.yml b/sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.routing.yml new file mode 100644 index 000000000..c16a04348 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.routing.yml @@ -0,0 +1,7 @@ +devel.simple_page: + path: '/devel/simple-page' + defaults: + _controller: '\Drupal\devel_test\Controller\DevelTestController::simplePage' + _title: 'Simple Page' + requirements: + _permission: 'access devel information' diff --git a/sites/all/modules/contrib/dev/devel/tests/modules/devel_test/src/Controller/DevelTestController.php b/sites/all/modules/contrib/dev/devel/tests/modules/devel_test/src/Controller/DevelTestController.php new file mode 100644 index 000000000..fa2c77edf --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/modules/devel_test/src/Controller/DevelTestController.php @@ -0,0 +1,24 @@ + $this->t('Simple page'), + ]; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelDumperTestTrait.php b/sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelDumperTestTrait.php new file mode 100644 index 000000000..ff80cf179 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelDumperTestTrait.php @@ -0,0 +1,127 @@ +getDumperExportDump($data, $name); + $this->assertEqual(rtrim($dump), $output, $message); + } + + /** + * Asserts that a haystack contains the dump export output. + * + * Use \Drupal\devel\DevelDumperManager::export(). + * + * @param $haystack + * The string that contains the dump output to test. + * @param $data + * The variable to dump. + * @param null $name + * (optional) The label to output before variable, defaults to NULL. + * @param string $message + * (optional) A message to display with the assertion. + */ + public function assertContainsDumpExport($haystack, $data, $name = NULL, $message = '') { + $output = $this->getDumperExportDump($data, $name); + $this->assertContains($output, (string) $haystack, $message); + } + + /** + * Asserts that the string passed in input is equals to the string + * representation of a variable obtained dumping the data. + * + * Use \Drupal\devel\DevelDumperManager::dump(). + * + * @param $dump + * The string that contains the dump output to test. + * @param $data + * The variable to dump. + * @param null $name + * (optional) The label to output before variable, defaults to NULL. + * @param string $message + * (optional) A message to display with the assertion. + */ + public function assertDumpEquals($dump, $data, $name = NULL, $message = '') { + $output = $this->getDumperDump($data, $name); + $this->assertEqual(rtrim($dump), $output, $message); + } + + /** + * Asserts that a haystack contains the dump output. + * + * Use \Drupal\devel\DevelDumperManager::dump(). + * + * @param $haystack + * The string that contains the dump output to test. + * @param $data + * The variable to dump. + * @param null $name + * (optional) The label to output before variable, defaults to NULL. + * @param string $message + * (optional) A message to display with the assertion. + */ + public function assertContainsDump($haystack, $data, $name = NULL, $message = '') { + $output = $this->getDumperDump($data, $name); + $this->assertContains($output, (string) $haystack, $message); + } + + /** + * Returns a string representation of a variable. + * + * @param mixed $input + * The variable to dump. + * @param string $name + * (optional) The label to output before variable, defaults to NULL. + * + * @return string + * String representation of a variable. + * + * @see \Drupal\devel\DevelDumperManager::export() + */ + private function getDumperExportDump($input, $name = NULL) { + $output = \Drupal::service('devel.dumper')->export($input, $name); + return rtrim($output); + } + + /** + * Returns a string representation of a variable. + * + * @param mixed $input + * The variable to dump. + * @param string $name + * (optional) The label to output before variable, defaults to NULL. + * + * @return string + * String representation of a variable. + * + * @see \Drupal\devel\DevelDumperManager::dump() + */ + private function getDumperDump($input, $name = NULL) { + ob_start(); + \Drupal::service('devel.dumper')->dump($input, $name); + $output = ob_get_contents(); + ob_end_clean(); + return rtrim($output); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelEnforcedDependenciesTest.php b/sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelEnforcedDependenciesTest.php new file mode 100644 index 000000000..b67965b25 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelEnforcedDependenciesTest.php @@ -0,0 +1,80 @@ +installEntitySchema('user'); + $this->installConfig('devel'); + // For uninstall to work. + $this->installSchema('user', ['users_data']); + } + + /** + * Tests devel menu enforced dependencies. + */ + public function testMenuEnforcedDependencies() { + /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */ + $config_manager = $this->container->get('config.manager'); + + // Ensure that the Devel menu has explicit enforced dependencies on devel + // module. + $menu = Menu::load('devel'); + $this->assertEquals(['enforced' => ['module' => ['devel']]], $menu->get('dependencies')); + + // Creates an instance of devel menu block so you can test if enforced + // dependencies work properly with it. + $block_id = strtolower($this->randomMachineName(8)); + + $block = Block::create([ + 'plugin' => 'system_menu_block:devel', + 'region' => 'sidebar_first', + 'id' => $block_id, + 'theme' => $this->config('system.theme')->get('default'), + 'label' => $this->randomMachineName(8), + 'visibility' => array(), + 'weight' => 0, + ]); + $block->save(); + + // Ensure that the menu and block instance depend on devel module. + $dependents = $config_manager->findConfigEntityDependents('module', ['devel']); + $this->assertTrue(isset($dependents['system.menu.devel'])); + $this->assertTrue(isset($dependents['block.block.' . $block_id])); + + $this->container->get('module_installer')->uninstall(['devel']); + + // Ensure that the menu and block instance are deleted when the dependency + // is uninstalled. + $this->assertNull(Menu::load('devel')); + $this->assertNull(Block::load($block_id)); + + // Ensure that no config entities depend on devel once uninstalled. + $dependents = $config_manager->findConfigEntityDependents('module', ['devel']); + $this->assertFalse(isset($dependents['system.menu.devel'])); + $this->assertFalse(isset($dependents['block.block.' . $block_id])); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelTwigExtensionTest.php b/sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelTwigExtensionTest.php new file mode 100644 index 000000000..3d22d13c1 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/tests/src/Kernel/DevelTwigExtensionTest.php @@ -0,0 +1,203 @@ +installEntitySchema('user'); + $this->installSchema('system', 'sequences'); + + $devel_role = Role::create([ + 'id' => 'admin', + 'permissions' => ['access devel information'], + ]); + $devel_role->save(); + + $this->develUser = User::create([ + 'name' => $this->randomMachineName(), + 'roles' => [$devel_role->id()], + ]); + $this->develUser->save(); + } + + /** + * {@inheritdoc} + */ + public function register(ContainerBuilder $container) { + parent::register($container); + + $parameters = $container->getParameter('twig.config'); + $parameters['debug'] = TRUE; + $container->setParameter('twig.config', $parameters); + } + + /** + * Tests that Twig extension loads appropriately. + */ + public function testTwigExtensionLoaded() { + $twig_service = \Drupal::service('twig'); + $extension = $twig_service->getExtension('devel_debug'); + $this->assertEquals(get_class($extension), Debug::class, 'Debug Extension loaded successfully.'); + } + + /** + * Tests that the Twig dump functions are registered properly. + */ + public function testDumpFunctionsRegistered() { + /** @var \Twig_SimpleFunction[] $functions */ + $functions = \Drupal::service('twig')->getFunctions(); + + $dump_functions = ['devel_dump', 'kpr']; + $message_functions = ['devel_message', 'dpm', 'dsm']; + $registered_functions = $dump_functions + $message_functions; + + foreach ($registered_functions as $name) { + $function = $functions[$name]; + $this->assertTrue($function instanceof \Twig_SimpleFunction); + $this->assertEquals($function->getName(), $name); + $this->assertTrue($function->needsContext()); + $this->assertTrue($function->needsEnvironment()); + $this->assertTrue($function->isVariadic()); + + is_callable($function->getCallable(), TRUE, $callable); + if (in_array($name, $dump_functions)) { + $this->assertEquals($callable, 'Drupal\devel\Twig\Extension\Debug::dump'); + } + else { + $this->assertEquals($callable, 'Drupal\devel\Twig\Extension\Debug::message'); + } + } + } + + /** + * Tests that the Twig function for XDebug integration is registered properly. + */ + public function testXDebugIntegrationFunctionsRegistered() { + /** @var \Twig_SimpleFunction $function */ + $function = \Drupal::service('twig')->getFunction('devel_breakpoint'); + $this->assertTrue($function instanceof \Twig_SimpleFunction); + $this->assertEquals($function->getName(), 'devel_breakpoint'); + $this->assertTrue($function->needsContext()); + $this->assertTrue($function->needsEnvironment()); + $this->assertTrue($function->isVariadic()); + is_callable($function->getCallable(), TRUE, $callable); + $this->assertEquals($callable, 'Drupal\devel\Twig\Extension\Debug::breakpoint'); + } + + /** + * Tests that the Twig extension's dump functions produce the expected output. + */ + public function testDumpFunctions() { + $template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_dump() }}'; + $expected_template_output = 'test-with-context context! first value second value'; + + $context = [ + 'twig_string' => 'context!', + 'twig_array' => [ + 'first' => 'first value', + 'second' => 'second value', + ], + 'twig_object' => new \stdClass(), + ]; + + /** @var \Drupal\Core\Template\TwigEnvironment $environment */ + $environment = \Drupal::service('twig'); + + // Ensures that the twig extension does nothing if the current + // user has not the adequate permission. + $this->assertTrue($environment->isDebug()); + $this->assertEquals($environment->renderInline($template, $context), $expected_template_output); + + \Drupal::currentUser()->setAccount($this->develUser); + + // Ensures that if no argument is passed to the function the twig context is + // dumped. + $output = (string) $environment->renderInline($template, $context); + $this->assertContains($expected_template_output, $output); + $this->assertContainsDump($output, $context, 'Twig context'); + + // Ensures that if an argument is passed to the function it is dumped. + $template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_dump(twig_array) }}'; + $output = (string) $environment->renderInline($template, $context); + $this->assertContains($expected_template_output, $output); + $this->assertContainsDump($output, $context['twig_array']); + + // Ensures that if more than one argument is passed the function works + // properly and every argument is dumped separately. + $template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_dump(twig_string, twig_array.first, twig_array, twig_object) }}'; + $output = (string) $environment->renderInline($template, $context); + $this->assertContains($expected_template_output, $output); + $this->assertContainsDump($output, $context['twig_string']); + $this->assertContainsDump($output, $context['twig_array']['first']); + $this->assertContainsDump($output, $context['twig_array']); + $this->assertContainsDump($output, $context['twig_object']); + + // Clear messages. + drupal_get_messages(); + + $retrieve_message = function ($messages, $index) { + return isset($messages['status'][$index]) ? (string) $messages['status'][$index] : NULL; + }; + + // Ensures that if no argument is passed to the function the twig context is + // dumped. + $template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_message() }}'; + $output = (string) $environment->renderInline($template, $context); + $this->assertContains($expected_template_output, $output); + $messages = drupal_get_messages(); + $this->assertDumpExportEquals($retrieve_message($messages, 0), $context, 'Twig context'); + + // Ensures that if an argument is passed to the function it is dumped. + $template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_message(twig_array) }}'; + $output = (string) $environment->renderInline($template, $context); + $this->assertContains($expected_template_output, $output); + $messages = drupal_get_messages(); + $this->assertDumpExportEquals($retrieve_message($messages, 0), $context['twig_array']); + + // Ensures that if more than one argument is passed to the function works + // properly and every argument is dumped separately. + $template = 'test-with-context {{ twig_string }} {{ twig_array.first }} {{ twig_array.second }}{{ devel_message(twig_string, twig_array.first, twig_array, twig_object) }}'; + $output = (string) $environment->renderInline($template, $context); + $this->assertContains($expected_template_output, $output); + $messages = drupal_get_messages(); + $this->assertDumpExportEquals($retrieve_message($messages, 0), $context['twig_string']); + $this->assertDumpExportEquals($retrieve_message($messages, 1), $context['twig_array']['first']); + $this->assertDumpExportEquals($retrieve_message($messages, 2), $context['twig_array']); + $this->assertDumpExportEquals($retrieve_message($messages, 3), $context['twig_object']); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/README.md b/sites/all/modules/contrib/dev/devel/webprofiler/README.md new file mode 100644 index 000000000..3b269efca --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/README.md @@ -0,0 +1,28 @@ +!! README.md is a work in progress !! + +#Dependencies: + +- d3.js: Webprofiler module requires D3 library 3.x (not 4.x) to render data. + Download https://github.com/mbostock/d3 into /libraries/d3/d3.min.js + +- highlight.js: Webprofiler module requires highlight library to syntax highlight collected queries. + Download http://highlightjs.org into /libraries/highlight + +#IDE link: + +Every class name discovered while profiling (controller class, event class) are linked to an url for directly open in +an IDE, you can configure the url of those link based on the IDE you are using: + +- Sublime text (2 and 3): see https://github.com/dhoulb/subl for Mac OS X +- Textmate: should be supported by default, use txmt://open?url=file://@file&line=@line as link +- PhpStorm 8+: use phpstorm://open?file=@file&line=@line as link + +#Timeline: + +Now it is possible to also collect the time needed to instantiate every single service used in a request, to make it +work you need to add these two lines to settings.php (or, event better, to settings.local.php): + +``` +$class_loader->addPsr4('Drupal\\webprofiler\\', [ __DIR__ . '/../../modules/contrib/devel/webprofiler/src']); +$settings['container_base_class'] = '\Drupal\webprofiler\DependencyInjection\TraceableContainer'; +``` diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/composer.json b/sites/all/modules/contrib/dev/devel/webprofiler/composer.json new file mode 100644 index 000000000..25cffbfee --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/composer.json @@ -0,0 +1,18 @@ +{ + "name": "drupal/webprofiler", + "description": "Drupal Web Profiler.", + "type": "drupal-module", + "license": "GPL-2.0+", + "require": { + "oomphinc/composer-installers-extender" : "v1.1.*", + "mbostock/d3": "v3.5.*", + "components/highlightjs": "v9.5.*", + "symfony/stopwatch": "2.4.*" + }, + "extra": { + "installer-types": ["library", "component"], + "installer-paths": { + "libraries/{$name}": ["mbostock/d3", "components/highlightjs"] + } + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/config/install/webprofiler.config.yml b/sites/all/modules/contrib/dev/devel/webprofiler/config/install/webprofiler.config.yml new file mode 100644 index 000000000..5e8127433 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/config/install/webprofiler.config.yml @@ -0,0 +1,28 @@ +purge_on_cache_clear: true +storage: profiler.database_storage +exclude: "/contextual/*\r\n/toolbar/*\r\n/edit/*\r\n*.js\r\n*.css" +ide_link: "txmt://open?url=file://@file&line=@line" +ide_link_remote: "" +ide_link_local: "" +active_toolbar_items: + devel: devel + assets: assets + blocks: blocks + cache: cache + database: database + drupal_extension: drupal_extension + forms: forms + performance_timing: performance_timing + php_config: php_config + request: request + time: time + user: user + views: views + config: '0' + events: '0' + http: '0' + routing: '0' + services: '0' + state: '0' +query_sort: source +query_highlight: 5 diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/config/schema/webprofiler.schema.yml b/sites/all/modules/contrib/dev/devel/webprofiler/config/schema/webprofiler.schema.yml new file mode 100644 index 000000000..1adc2f166 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/config/schema/webprofiler.schema.yml @@ -0,0 +1,35 @@ +# Schema for the configuration files of the Webprofiler module. +webprofiler.config: + type: config_object + label: 'Webprofiler configuration' + mapping: + purge_on_cache_clear: + type: boolean + label: 'Purge profiles on cache clear' + storage: + type: string + label: 'Storage implementation' + exclude: + type: string + label: 'Paths to exclude' + active_toolbar_items: + type: sequence + label: 'Active toolbar items' + sequence: + - type: string + label: 'Toolbar item' + ide_link: + type: string + label: 'IDE link' + ide_link_remote: + type: string + label: 'IDE link remote path' + ide_link_local: + type: string + label: 'IDE link local path' + query_sort: + type: string + label: 'Sort query log' + query_highlight: + type: integer + label: 'Slow query highlighting' diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/config/translations/console.en.yml b/sites/all/modules/contrib/dev/devel/webprofiler/config/translations/console.en.yml new file mode 100644 index 000000000..2f4bfa942 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/config/translations/console.en.yml @@ -0,0 +1,54 @@ +commands: + webprofiler: + export: + description: Exports Webprofiler profile/s to file. + arguments: + id: Profile id. + options: + directory: Destination directory to store exported file/s. + messages: + success: Succesfully exported to %s + exported_count: Exported %s profiles + error_writing: Error writing file %s + error_no_profile: No profile with id %s + progress: + exporting: Exporting profiles... + archive: Create archive... + delete_tmp: Delete temp files... + done: Done. + list: + description: Lists Webprofiler profiles. + options: + ip: Filter by IP. + url: Filter by URL. + method: Filter by HTTP method. + limit: Limit printed profiles. + rows: + time: D, m/d/Y - H:i:s + header: + token: Token + ip: IP + method: Method + url: URL + time: Time + benchmark: + description: Benchmark an url. + arguments: + url: Url to benchmark. + options: + runs: Number of runs. + file: Save results as file. + cache-rebuild: Rebuild cache before start benchmark. + messages: + not_git: Not in a git repository. + error_login: Impossibile to login in the user. + progress: + cache_rebuild: Rebuilding cache... + login: Login user... + get: Http request... + compute_avg: Compute average... + compute_median: Compute median... + compute_95percentile: Compute 95 percentile... + git_hash: Compute GIT hash... + yaml: Generate output... + done: Done. diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/config/translations/console.es.yml b/sites/all/modules/contrib/dev/devel/webprofiler/config/translations/console.es.yml new file mode 100644 index 000000000..71f66efc0 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/config/translations/console.es.yml @@ -0,0 +1,54 @@ +commands: + webprofiler: + export: + description: Exportar Webprofiler perfil(es) a un archivo. + arguments: + id: Profile id. + options: + directory: Directorio de destiono para almacenar archivo(s) exportados. + messages: + success: Satisfactoriamente exportado en %s + exported_count: Exportados %s perfiles + error_writing: Error escribiendo el archivo %s + error_no_profile: No fue encontrado un perfil con id %s + progress: + exporting: Exportando perfiles... + archive: Creando archivos... + delete_tmp: Borrando archivos temporales... + done: Terminado. + list: + description: Listar perfiles Webprofiler. + options: + ip: Filtrar por IP. + url: Filtrar por URL. + method: Filtrar por método HTTP. + limit: Limitar perfiles a mostrar. + rows: + time: D, d/m/Y - H:i:s + header: + token: Token + ip: IP + method: Método + url: URL + time: Fecha + benchmark: + description: Benchmark una URL. + arguments: + url: Url para efectuar un benchmark. + options: + runs: Número de ejecuciones. + file: Guardar los resultados en un archivo. + cache-rebuild: Reconstruir cache antes de iniciar el benchmark. + messages: + not_git: No en un repositorio git. + error_login: Error ingresando al sistema. + progress: + cache_rebuild: Reconstruyendo cache... + login: Ingresando al sistema... + get: Solicitud Http... + compute_avg: Calculando promedio... + compute_median: Calculando media... + compute_95percentile: Calculando percentile 95... + git_hash: Calculando GIT hash... + yaml: Generando salida... + done: Terminado. diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/css/app/dashboard.css b/sites/all/modules/contrib/dev/devel/webprofiler/css/app/dashboard.css new file mode 100644 index 000000000..dfa517a38 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/css/app/dashboard.css @@ -0,0 +1,582 @@ +/* base themes */ +#webrofiler { + overflow: hidden; + font-size: 1em; + line-height: 1.2; + color: rgba(0, 0, 0, 0.7); + background-color: #ffffff; } + #webrofiler * { + box-sizing: border-box; } + +#webprofiler { + /* + + overview example + +
    +
  • + + + + + + + */ + /* + + general element template + + will be implemented in the next dashboard relase + + this HTML will contain the preview of any single card present inside the dashboard + + + +
    +

    + + +
    +
    text +
    text + + */ } + #webprofiler .tabs:after, #webprofiler .tabs__tabs:after, #webprofiler .tabs__panels:after, #webprofiler .tabs__panel:after, #webprofiler .resume:after, #webprofiler .collectors:after, #webprofiler .panel:after, #webprofiler .panel__toolbar:after, #webprofiler .panel__container:after, #webprofiler .panel__expand-header:after, #webprofiler .panel__expand-content:after, #webprofiler .app__bar:after, #webprofiler .app__container:after, #webprofiler .modal__bar:after, #webprofiler .modal__content:after { + content: ' '; + display: block; + clear: both; } + #webprofiler .panel__container, #webprofiler .ui--red, #webprofiler .ui--pink, #webprofiler .ui--purple, #webprofiler .ui--deepPurple, #webprofiler .ui--indigo, #webprofiler .ui--blue, #webprofiler .ui--lightBlue, #webprofiler .ui--cyan, #webprofiler .ui--teal, #webprofiler .ui--green, #webprofiler .ui--lightGreen, #webprofiler .ui--lime, #webprofiler .ui--yellow, #webprofiler .ui--amber, #webprofiler .ui--orange, #webprofiler .ui--deepOrange, #webprofiler .ui--brown, #webprofiler .ui--gray, #webprofiler .ui--blueGray, #webprofiler .modal__container { + box-shadow: 0 2px 4px -2px #000000; + margin-bottom: 40px; + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; + overflow: hidden; } + #webprofiler .ui--red .app__title, #webprofiler .ui--pink .app__title, #webprofiler .ui--purple .app__title, #webprofiler .ui--deepPurple .app__title, #webprofiler .ui--blue .app__title, #webprofiler .ui--lightBlue .app__title, #webprofiler .ui--cyan .app__title, #webprofiler .ui--green .app__title, #webprofiler .ui--lightGreen .app__title, #webprofiler .ui--lime .app__title, #webprofiler .ui--yellow .app__title, #webprofiler .ui--amber .app__title, #webprofiler .ui--orange .app__title, #webprofiler .ui--deepOrange .app__title, #webprofiler .ui--brown .app__title { + color: white; } + #webprofiler .ui--indigo .app__title, #webprofiler .ui--teal .app__title, #webprofiler .ui--gray .app__title, #webprofiler .ui--blueGray .app__title, #webprofiler .modal__title { + color: rgba(0, 0, 0, 0.87); } + #webprofiler .ui--red .app__main-data, #webprofiler .ui--pink .app__main-data, #webprofiler .ui--purple .app__main-data, #webprofiler .ui--deepPurple .app__main-data, #webprofiler .ui--indigo .app__main-data, #webprofiler .ui--blue .app__main-data, #webprofiler .ui--lightBlue .app__main-data, #webprofiler .ui--cyan .app__main-data, #webprofiler .ui--teal .app__main-data, #webprofiler .ui--green .app__main-data, #webprofiler .ui--lightGreen .app__main-data, #webprofiler .ui--lime .app__main-data, #webprofiler .ui--yellow .app__main-data, #webprofiler .ui--amber .app__main-data, #webprofiler .ui--orange .app__main-data, #webprofiler .ui--deepOrange .app__main-data, #webprofiler .ui--brown .app__main-data, #webprofiler .ui--gray .app__main-data, #webprofiler .ui--blueGray .app__main-data, #webprofiler .modal__content { + color: rgba(0, 0, 0, 0.7); } + #webprofiler .ui--red .app__secondary-data, #webprofiler .ui--pink .app__secondary-data, #webprofiler .ui--purple .app__secondary-data, #webprofiler .ui--deepPurple .app__secondary-data, #webprofiler .ui--indigo .app__secondary-data, #webprofiler .ui--blue .app__secondary-data, #webprofiler .ui--lightBlue .app__secondary-data, #webprofiler .ui--cyan .app__secondary-data, #webprofiler .ui--teal .app__secondary-data, #webprofiler .ui--green .app__secondary-data, #webprofiler .ui--lightGreen .app__secondary-data, #webprofiler .ui--lime .app__secondary-data, #webprofiler .ui--yellow .app__secondary-data, #webprofiler .ui--amber .app__secondary-data, #webprofiler .ui--orange .app__secondary-data, #webprofiler .ui--deepOrange .app__secondary-data, #webprofiler .ui--brown .app__secondary-data, #webprofiler .ui--gray .app__secondary-data, #webprofiler .ui--blueGray .app__secondary-data { + color: rgba(0, 0, 0, 0.3); } + #webprofiler a { + color: #2979ff; } + #webprofiler a:hover { + color: #2962ff; + text-decoration: none; } + #webprofiler .list--unstyled { + margin: 0; + list-style-type: none; + padding: 0; } + #webprofiler .list--unstyled > li { + display: block; + padding: 0 8px; } + #webprofiler .list--inline { + margin: 0; + list-style-type: none; + padding: 0; } + #webprofiler .list--inline > li { + display: inline-block; + padding: 8px; } + #webprofiler .list--level-0 li { + padding: 0 0 0 0; } + #webprofiler .list--level-1 li { + padding: 0 0 0 8px; } + #webprofiler .list--level-3 li { + padding: 0 0 0 16px; } + #webprofiler pre { + padding: 16px; } + #webprofiler pre, #webprofiler code { + white-space: normal; + word-break: break-all; + word-break: break-word; + font-size: 14px; + background-color: #f5f5f5; } + #webprofiler .list-item--bold { + font-weight: bold; + } + #webprofiler code.code--json { + white-space: pre-wrap; } + #webprofiler thead { + white-space: nowrap; } + #webprofiler thead th { + background-color: transparent; } + #webprofiler th { + font-size: 12px; + color: rgba(0, 0, 0, 0.54); + border: none; } + #webprofiler td { + word-break: break-all; + font-size: 13px; + color: rgba(0, 0, 0, 0.87); } + #webprofiler tr { + border-bottom: 1px solid rgba(0, 0, 0, 0.18); } + #webprofiler tr:hover { + background-color: #eeeeee; } + #webprofiler td, #webprofiler th { + padding: 16px 24px; + min-height: 46px; + text-transform: none; + vertical-align: top; } + #webprofiler .table--compact { + table-layout: fixed; } + #webprofiler .table--compact td, #webprofiler .table--compact th { + word-spacing: 99em; } + #webprofiler .table--fixed { + table-layout: fixed; } + #webprofiler .table--duo th:first-child, + #webprofiler .table--duo td:first-child { + width: 35%; } + #webprofiler .table--trio th:first-child, + #webprofiler .table--trio td:first-child { + width: 80%; } + #webprofiler .table--trio th:nth-child(2), + #webprofiler .table--trio td:nth-child(2), + #webprofiler .table--trio th:nth-child(3), + #webprofiler .table--trio td:nth-child(3) { + width: 10%; } + #webprofiler [class*="button"] { + display: block; + color: #2979ff; + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; + border: none; + text-transform: uppercase; + font-size: 16px; + text-align: center; + padding: 16px; + margin: 8px; + cursor: pointer; + min-width: 42px; } + #webprofiler .button--flat { + background-color: transparent; } + #webprofiler .button--flat:hover { + background-color: #eeeeee; } + #webprofiler .button--flat:active, #webprofiler .button--flat:focus { + background-color: #bdbdbd; } + #webprofiler .tabs { + position: relative; + margin: -16px 0 16px 0; } + #webprofiler .tabs a { + display: inline; } + #webprofiler .tabs__radio { + display: none; } + #webprofiler .tabs__tabs { + margin: 0 -16px 16px -16px; + text-transform: uppercase; + height: 48px; + background-color: #f5f5f5; } + #webprofiler .tabs__tabs > li { + padding: 0; } + #webprofiler .tabs__label { + border-bottom: 5px solid transparent; + text-align: center; + line-height: 48px; + min-width: 160px; + display: inline-block; + margin: -5px 0 0 0; } + #webprofiler .tabs__panel { + display: none; + width: 100%; } + #webprofiler .tabs__radio:checked:nth-child(1) ~ .tabs__panels .tabs__panel:nth-child(1) { + display: block; } + #webprofiler .tabs__radio:checked:nth-child(2) ~ .tabs__panels .tabs__panel:nth-child(2) { + display: block; } + #webprofiler .tabs__radio:checked:nth-child(3) ~ .tabs__panels .tabs__panel:nth-child(3) { + display: block; } + #webprofiler .tabs__radio:checked:nth-child(1) ~ .tabs__tabs li:nth-child(1) .tabs__label { + border-bottom-color: #2962ff; } + #webprofiler .tabs__radio:checked:nth-child(2) ~ .tabs__tabs li:nth-child(2) .tabs__label { + border-bottom-color: #2962ff; } + #webprofiler .tabs__radio:checked:nth-child(3) ~ .tabs__tabs li:nth-child(3) .tabs__label { + border-bottom-color: #2962ff; } + #webprofiler .overview { + display: table-cell; + width: 20%; + vertical-align: top; + font-size: 1.2em; + line-height: 2em; + background: #ffffff; } + #webprofiler .overview__list { + padding: 0; + margin: -1px -1px 0 0; + list-style-type: none; + border-top: 1px solid rgba(0, 0, 0, 0.18); } + #webprofiler .overview__link { + position: relative; + display: block; + padding: 0 0 0 72px; + border-right: 5px solid transparent; } + #webprofiler .overview__link:after { + content: ''; + display: block; + position: absolute; + bottom: 0; + right: -5px; + left: 72px; + border-bottom: 1px solid rgba(0, 0, 0, 0.18); } + #webprofiler .overview__icon { + position: absolute; + top: 16px; + left: 16px; } + #webprofiler .overview__title { + display: block; + font-size: 16px; + line-height: 22px; + padding-top: 16px; + padding-bottom: 16px; + padding-right: 11px; } + #webprofiler .overview__subtitle { + display: block; + font-size: 14px; + line-height: 20px; + padding-bottom: 16px; + padding-right: 11px; + margin-top: -16px; } + #webprofiler .is--selected .overview__link { + color: #000000; + background-color: #f5f5f5; + border-right-color: #2962ff; } + #webprofiler .overview__link:hover { + color: #000000; + background-color: #82b1ff; } + #webprofiler .resume { + position: relative; + background-color: #e0e0e0; + border-bottom: 1px solid rgba(0, 0, 0, 0.18); + border-left: 1px solid rgba(0, 0, 0, 0.18); + padding: 16px 16px 0 16px; + margin-left: 20%; } + #webprofiler .resume__title { + display: block; + font-size: 24px; + line-height: 32px; } + #webprofiler .resume__subtitle, + #webprofiler .resume__time { + display: block; + font-size: 16px; + line-height: 24px; } + #webprofiler .resume__time { + margin-bottom: 52px; } + #webprofiler .resume__button { + position: absolute; + bottom: 0; + right: 16px; } + #webprofiler .collectors { + display: table; + width: 100%; } + #webprofiler .details { + display: table-cell; + overflow: hidden; + padding: 16px; + border-left: 1px solid rgba(0, 0, 0, 0.18); } + #webprofiler .details__title { + border-bottom: 1px solid #ccc; + padding-bottom: 5px; } + #webprofiler .details svg { + max-width: 100%; } + #webprofiler [class*='panel__filter--'] { + display: inline-block; + padding: 32px 16px 0 16px; + position: relative; } + #webprofiler .panel { + margin: 0 0 20px; + padding: 0; + background: #fafafa; + border: none; } + #webprofiler .panel select, + #webprofiler .panel input[type="text"] { + min-width: 165px; + font-size: 16px; + background-color: #fafafa; + border-color: transparent; + border-width: 0; + border-bottom: 2px solid #ffffff; + padding: 8px 0; + margin-bottom: 8px; } + #webprofiler .panel select:focus, + #webprofiler .panel input[type="text"]:focus { + outline: none; + border-bottom-color: #2979ff; } + #webprofiler .panel select:focus + label, + #webprofiler .panel input[type="text"]:focus + label { + color: #2979ff; } + #webprofiler .panel select { + min-width: 165px; + padding-right: 24px; + border-radius: 0; + background: url(../../images/caret-down.svg) no-repeat 99% 63%; + -moz-appearance: none; } + #webprofiler .panel select:hover { + box-shadow: none; } + #webprofiler .panel__title { + font-size: 24px; + line-height: 1; + background-color: #f5f5f5; + text-transform: none; + padding: 16px; + margin: -16px -16px 16px -16px; } + #webprofiler .panel__toolbar { + background-color: #fafafa; + margin: -16px -16px 16px -16px; } + #webprofiler .panel__filter-action { + display: inline-block; } + #webprofiler .panel__filter-label { + display: block; + position: absolute; + top: 0; + margin-top: 16px; } + #webprofiler .panel__container { + background-color: #ffffff; + position: relative; + padding: 16px; } + #webprofiler .panel__expand-content { + display: none; } + #webprofiler .is--open .panel__expand-content { + display: block; } + #webprofiler .is--hightlighted { + border-right: 10px solid #aa00ff; + padding-right: 6px; } + #webprofiler .is--hidden { + display: none; } + #webprofiler .app { + overflow: hidden; + position: relative; + display: block; + cursor: pointer; } + #webprofiler .app__bar { + padding: 16px; } + #webprofiler .app__title { + margin: 8px 32px 0 0; + font-weight: normal; } + #webprofiler .app__icon { + float: right; + height: 32px; + width: 32px; } + #webprofiler .app__container { + padding: 16px; } + #webprofiler .app__main-data { + font-size: 24px; + padding-bottom: 16px; } + #webprofiler .app__secondary-data { + font-size: 14px; + padding-bottom: 16px; + margin-top: -8px; } + #webprofiler .ui--red .app__bar { + background-color: #ff1744; } + #webprofiler .ui--red .app__container { + background-color: #ff8a80; } + #webprofiler .ui--pink .app__bar { + background-color: #f50057; } + #webprofiler .ui--pink .app__container { + background-color: #ff80ab; } + #webprofiler .ui--purple .app__bar { + background-color: #d500f9; } + #webprofiler .ui--purple .app__container { + background-color: #ea80fc; } + #webprofiler .ui--deepPurple .app__bar { + background-color: #651fff; } + #webprofiler .ui--deepPurple .app__container { + background-color: #b388ff; } + #webprofiler .ui--indigo .app__bar { + background-color: #3d5afe; } + #webprofiler .ui--indigo .app__container { + background-color: #8c9eff; } + #webprofiler .ui--blue .app__bar { + background-color: #2979ff; } + #webprofiler .ui--blue .app__container { + background-color: #82b1ff; } + #webprofiler .ui--lightBlue .app__bar { + background-color: #00b0ff; } + #webprofiler .ui--lightBlue .app__container { + background-color: #80d8ff; } + #webprofiler .ui--cyan .app__bar { + background-color: #00e5ff; } + #webprofiler .ui--cyan .app__container { + background-color: #84ffff; } + #webprofiler .ui--teal .app__bar { + background-color: #ade9b6; } + #webprofiler .ui--teal .app__container { + background-color: #a7ffeb; } + #webprofiler .ui--green .app__bar { + background-color: #00e676; } + #webprofiler .ui--green .app__container { + background-color: #b9f6ca; } + #webprofiler .ui--lightGreen .app__bar { + background-color: #76ff03; } + #webprofiler .ui--lightGreen .app__container { + background-color: #ccff90; } + #webprofiler .ui--lime .app__bar { + background-color: #c6ff00; } + #webprofiler .ui--lime .app__container { + background-color: #f4ff81; } + #webprofiler .ui--yellow .app__bar { + background-color: #ffea00; } + #webprofiler .ui--yellow .app__container { + background-color: #ffff8d; } + #webprofiler .ui--amber .app__bar { + background-color: #ffc400; } + #webprofiler .ui--amber .app__container { + background-color: #ffe57f; } + #webprofiler .ui--orange .app__bar { + background-color: #ff9100; } + #webprofiler .ui--orange .app__container { + background-color: #ffd180; } + #webprofiler .ui--deepOrange .app__bar { + background-color: #ff3d00; } + #webprofiler .ui--deepOrange .app__container { + background-color: #ff9e80; } + #webprofiler .ui--brown .app__bar { + background-color: #8d6e63; } + #webprofiler .ui--brown .app__container { + background-color: #d7ccc8; } + #webprofiler .ui--gray .app__bar { + background-color: #bdbdbd; } + #webprofiler .ui--gray .app__container { + background-color: #f5f5f5; } + #webprofiler .ui--blueGray .app__bar { + background-color: #bdbdbd; } + #webprofiler .ui--blueGray .app__container { + background-color: #f5f5f5; } + #webprofiler .loader--fixed { + position: fixed; + right: 0; + top: 50%; + width: 80%; + height: 100px; + zoom: 1.7; + text-align: center; } + #webprofiler .loader__circle { + animation: rotate 2s linear infinite; + height: 100px; + position: relative; + width: 100px; } + #webprofiler .loader__path { + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite; + stroke-linecap: round; } +@keyframes rotate { + 100% { + transform: rotate(360deg); } } +@keyframes dash { + 0% { + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; } + 50% { + stroke-dasharray: 89, 200; + stroke-dashoffset: -35; } + 100% { + stroke-dasharray: 89, 200; + stroke-dashoffset: -124; } } +@keyframes color { + 100%, 0% { + stroke: #ff1744; } + 40% { + stroke: #2979ff; } + 66% { + stroke: #00e676; } + 80%, 90% { + stroke: #ffea00; } } + #webprofiler .loader--linear { + height: 3px; + background-color: #ffea00; + position: absolute; + top: 0; + left: 0; + right: 0; } + #webprofiler .loader__bar { + content: ""; + display: inline; + position: absolute; + width: 0; + height: 100%; + left: 50%; + text-align: center; } + #webprofiler .loader__bar:nth-child(1) { + background-color: #ff1744; + animation: loading 3s linear infinite; } + #webprofiler .loader__bar:nth-child(2) { + background-color: #2979ff; + animation: loading 3s linear 1s infinite; } + #webprofiler .loader__bar:nth-child(3) { + background-color: #00e676; + animation: loading 3s linear 2s infinite; } +@keyframes loading { + from { + left: 50%; + width: 0; + z-index: 100; } + 33.3333% { + left: 0; + width: 100%; + z-index: 10; } + to { + left: 0; + width: 100%; } } + #webprofiler .modal { + background-color: rgba(0, 0, 0, 0.87); + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; } + #webprofiler .modal__container { + border-top-right-radius: 2px; + border-top-left-radius: 2px; + box-shadow: 0 2px 4px -2px #000000; + background-color: #ffffff; + position: relative; + width: 33%; + min-width: 350px; + margin: 0 auto; + margin-top: 15%; } + #webprofiler .modal__bar { + padding: 16px; } + #webprofiler .modal__content { + padding: 16px; + font-size: 14px; } + #webprofiler .modal__title { + margin: 8px 32px 0 0; + font-weight: normal; + font-size: 24px; } + #webprofiler .modal__main-data { + font-size: 24px; + padding-bottom: 16px; } + #webprofiler .modal textarea { + white-space: normal; + word-break: break-all; + word-break: break-word; + font-size: 14px; + background-color: #f5f5f5; + max-width: 100%; + height: 320px; + width: 100%; + padding: 16px; + box-sizing: border-box; + border: none; } + #webprofiler .t--heading, #webprofiler .tabs__radio:checked:nth-child(1) ~ .tabs__tabs li:nth-child(1) .tabs__label, #webprofiler .tabs__radio:checked:nth-child(2) ~ .tabs__tabs li:nth-child(2) .tabs__label, #webprofiler .tabs__radio:checked:nth-child(3) ~ .tabs__tabs li:nth-child(3) .tabs__label, #webprofiler .overview__title, #webprofiler .resume__title, #webprofiler .panel select, + #webprofiler .panel input[type="text"], #webprofiler .panel__title { + color: black; } + #webprofiler .t--notice, #webprofiler .tabs__label, #webprofiler .overview__subtitle, #webprofiler .resume__subtitle, + #webprofiler .resume__time, #webprofiler .panel__filter-label { + color: rgba(0, 0, 0, 0.3); } + #webprofiler .l-card { + width: 32%; + margin-left: 1%; + margin-right: 1%; + float: left; } + #webprofiler .l-card:nth-child(3n + 1) { + margin-left: 0; } + #webprofiler .l-card:nth-child(3n) { + margin-right: 0; } + #webprofiler .l-left { + float: left; } + #webprofiler .l-right { + float: right; } + #webprofiler .h--word-broken { + word-break: break-all; } + #webprofiler .h--word-intact { + word-break: normal; } + +/*# sourceMappingURL=dashboard.css.map */ diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/css/timeline.css b/sites/all/modules/contrib/dev/devel/webprofiler/css/timeline.css new file mode 100644 index 000000000..f7623106f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/css/timeline.css @@ -0,0 +1,106 @@ +/** + * Timeline + */ + +.timeline__legends { + font-size: 12px; + line-height: 1.5em; +} + +.timeline__legends span { + border-left-width: 10px; + border-left-style: solid; + padding: 0 10px 0 5px; +} + +.timeline__legends--default { + border-left-color: #E91E63; +} + +.timeline__legends--section { + border-left-color: #3F51B5; +} + +.timeline__legends--event_listener { + border-left-color: #00BCD4; +} + +.timeline__legends--event_listener_loading { + border-left-color: #8BC34A; +} + +.timeline__legends--template { + border-left-color: #FFC107; +} + +.timeline__legends--service { + border-left-color: #795548; +} + +text { + font-size: 12px; + line-height: 20px; + height: 22px; + fill: rgba(0, 0, 0, 0.87); +} +#timeline { + background: white; + margin: 10px 0; + width: 100%; + position: relative; + padding: 0 0 40px 0; +} +.timeline__row, +.timeline__scale--x, +.timeline__label rect { + stroke: rgba(0, 0, 0, 0.18); +} +.timeline__row{ + fill: transparent; +} +.timeline__label rect { + fill: white; +} +.timeline__period--default { + fill: #E91E63; +} +.timeline__period--service { + fill: #795548; +} +.timeline__period--section { + fill: #3F51B5; +} +.timeline__period--event_listener { + fill: #00BCD4; +} +.timeline__period--event_listener_loading { + fill: #8BC34A; +} +.timeline__period--template { + fill: #FFC107; +} +.timeline__period-trigger{ + fill: transparent; +} +.tooltip{ + position: absolute; + padding: 8px; + background: rgb(0,0,0); + background: rgba(0, 0, 0, 0.87); + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + width: 175px; + text-align: center; + display: none; + color: white; +} +.tooltip__title{ + display: block; +} +.tooltip__content{ + display: block; +} +.axis{ + stroke-width: 2px; + fill: none; +} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/images/caret-down.svg b/sites/all/modules/contrib/dev/devel/webprofiler/images/caret-down.svg new file mode 100644 index 000000000..4e48c8c98 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/images/caret-down.svg @@ -0,0 +1 @@ + diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/app/collections/collectors.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/collections/collectors.js new file mode 100644 index 000000000..ed26836f3 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/collections/collectors.js @@ -0,0 +1,35 @@ +(function ($, Drupal, drupalSettings, Backbone) { + + "use strict"; + + Drupal.webprofiler.collectors.Collectors = Backbone.Collection.extend({ + model: Drupal.webprofiler.models.Collector, + + url: Drupal.url('admin/reports/profiler/view/' + drupalSettings.webprofiler.token + '/collectors'), + + /** + * Unselect all models. + */ + resetSelected: function () { + this.each(function (model) { + model.set({"selected": false}); + }); + }, + + /** + * Select a specific model from the collection. + * + * @param id + * @returns {*} + */ + selectByID: function (id) { + this.resetSelected(); + var collector = this.get(id); + collector.set({"selected": true}); + + return collector.id; + } + }); + +}(jQuery, Drupal, drupalSettings, Backbone)); + diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/app/helpers.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/helpers.js new file mode 100644 index 000000000..651deb863 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/helpers.js @@ -0,0 +1,104 @@ +(function (drupalSettings) { + + Drupal.webprofiler.helpers = (function () { + + "use strict"; + + var escapeRx = function escapeRegExp(string) { + return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); + }, + + repl = function replaceAll(string, find, replace) { + return string.replace(new RegExp(escapeRx(find), 'g'), replace); + }, + + shortLink = function (clazz) { + if (!clazz) { + return null; + } + clazz = repl(clazz, '/', '\\'); + var parts = clazz.split("\\"), result = [], size = (parts.length - 1); + + _.each(parts, function (item, key) { + if (key < size) { + result.push(item.substring(0, 1)); + } else { + result.push(item); + } + }); + return result.join("\\"); + }, + + abbr = function (clazz) { + if (!clazz) { + return null; + } + + return '' + shortLink(clazz) + ''; + }, + + ideLink = function (file, line) { + if (!file) { + return null; + } + + line = line || 0; + + file = file.replace(drupalSettings.webprofiler.ide_link_remote, drupalSettings.webprofiler.ide_link_local); + + return drupalSettings.webprofiler.ide_link.replace("@file", file).replace("@line", line); + }, + + classLink = function (data) { + var link = ideLink(data['file'], data['line']), clazz = abbr(data['class']), method = data['method'], output = ''; + + output = clazz; + if (method) { + output += '::' + method; + } + + if (link) { + output = '' + output + ''; + } + + return output; + }, + + printTime = function (data, unit) { + unit = unit || 'ms'; + data = Math.round((data + 0.00001) * 100) / 100; + return data + ' ' + unit; + }, + + frm = function (obj, level) { + level = level || 0; + var str = '
      ', prop; + if (typeof obj != 'object') { + return obj; + } + for (prop in obj) { + if (isInt(prop)) { + str += '
    • ' + frm(obj[prop], level + 1) + '
    • '; + } else { + str += '
    • ' + prop + ': ' + frm(obj[prop], level + 1) + '
    • '; + } + } + return str + '
    '; + }, + + isInt = function (value) { + var x; + return isNaN(value) ? !1 : (x = parseFloat(value), (0 | x) === x); + }; + + return { + frm: frm, + ideLink: ideLink, + shortLink: shortLink, + classLink: classLink, + printTime: printTime + } + + })(); + +}(drupalSettings)); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/app/main.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/main.js new file mode 100644 index 000000000..d5d71cd42 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/main.js @@ -0,0 +1,105 @@ +(function ($, Drupal, Backbone) { + + "use strict"; + + /** + * Define namespaces. + */ + Drupal.webprofiler = { + views: {}, + models: {}, + collectors: {}, + routers: {} + }; + + Drupal.behaviors.webprofiler = { + attach: function (context) { + var el, + elz, + key, + sel, + value, + select, + selector, + unselected, + filter = [], + + livefilter = function (e) { + el = $(e).attr('id').replace('edit-', ''); + value = $(e).val(); + filter[el] = value.replace('/', '\/'); + selector = []; + unselected = []; + + for (key in filter) { + if (filter[key].length > 0 && filter[key] != ' ') { + select = filter[key].split(' ').filter(Boolean); + for (sel in select) { + selector.push('[data-wp-' + key + ' *= ' + select[sel].toLowerCase() + ']'); + unselected.push('[data-wp-' + key + ']:not([data-wp-' + key + ' *= ' + select[sel].toLowerCase() + '])'); + } + } + else { + selector.push('[data-wp-' + key + ']'); + } + } + for (elz in unselected) { + $(unselected[elz]).addClass('is--hidden'); + } + $(selector.join('')).removeClass('is--hidden'); + }, + + modalFill = function(t,c){ + $('.modal__title').html(t); + $('.modal__main-data').html(c); + }, + + clipboard = function (e, t) { + var clip = e.parent().find(t).get(0), + title = 'Original Code', + content = ''; + + modalFill(title,content); + $('.modal').show(); + }; + + $(context).find('#collectors').once('webprofiler').each(function () { + new Drupal.webprofiler.routers.CollectorsRouter({el: $('#collectors')}); + Backbone.history.start({ + pushState: false + }); + }); + + $(context).find('.js--modal-close').each(function () { + $(this).on('click', function () { + $('.js--modal').hide(); + }); + }); + + $(context).find('.js--live-filter').each(function () { + $(this).on('keyup', function () { + livefilter($(this)); + }); + $(this).on('change', function () { + livefilter($(this)); + }); + }); + + $(context).find('.js--panel-toggle').once('js--panel-toggle').each(function () { + $(this).on('click', function () { + $(this).parent().parent().toggleClass('is--open'); + }); + }); + + $(context).find('.js--clipboard-trigger').once('js--clipboard-trigger').each(function () { + $(this).on('click', function () { + clipboard($(this), '.js--clipboard-target') + } + ); + }); + } + }; + +}(jQuery, Drupal, Backbone)); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/app/models/collector.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/models/collector.js new file mode 100644 index 000000000..483033c47 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/models/collector.js @@ -0,0 +1,15 @@ +(function ($, Drupal, drupalSettings, Backbone) { + + "use strict"; + + Drupal.webprofiler.models.Collector = Backbone.Model.extend({ + idAttribute: 'name', + urlRoot: Drupal.url('admin/reports/profiler/view/' + drupalSettings.webprofiler.token + '/collectors'), + defaults: { + name: "default", + data: [], + selected: false + } + }); + +}(jQuery, Drupal, drupalSettings, Backbone)); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/app/routers/collectors.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/routers/collectors.js new file mode 100644 index 000000000..876f2387e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/routers/collectors.js @@ -0,0 +1,48 @@ +(function ($, Drupal, drupalSettings, Backbone) { + + "use strict"; + + var collectors = new Drupal.webprofiler.collectors.Collectors(drupalSettings.webprofiler.collectors); + + Drupal.webprofiler.routers.CollectorsRouter = Backbone.Router.extend({ + routes: { + ':id': 'selectCollector' + }, + + /** + * + * @param id + */ + selectCollector: function (id) { + var collectors = this.collectors, layout = this.layout; + + //collectors.resetSelected(); + collectors.selectByID(id); + + var collector = collectors.get(id); + + if (collector.get('data').length != 0) { + layout.setDetails(collector); + } else { + var deferred = collectors.get(id).fetch(); + deferred.done(function () { + layout.setDetails(collector); + }); + } + }, + + /** + * + * @param options + */ + initialize: function (options) { + this.collectors = collectors; + this.layout = Drupal.webprofiler.views.Layout.getInstance({ + el: options.el, + router: this + }); + this.layout.render(); + } + }); + +}(jQuery, Drupal, drupalSettings, Backbone)); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/collector.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/collector.js new file mode 100644 index 000000000..955a36e1b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/collector.js @@ -0,0 +1,29 @@ +(function ($, Drupal, Backbone) { + + "use strict"; + + Drupal.webprofiler.views.CollectorView = Backbone.View.extend({ + tagName: 'li', + template: _.template($("script#collector").html()), + + /** + * + */ + initialize: function () { + _.bindAll(this, "render"); + this.listenTo(this.model, 'change:selected', this.render); + }, + + /** + * + * @returns {Drupal.webprofiler.views.CollectorView} + */ + render: function () { + this.$el.html(this.template(this.model.toJSON())); + this.$el.toggleClass('is--selected', this.model.get('selected')); + return this; + } + + }); + +}(jQuery, Drupal, Backbone)); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/collectorsList.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/collectorsList.js new file mode 100644 index 000000000..8628371cb --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/collectorsList.js @@ -0,0 +1,21 @@ +(function ($, Drupal, Backbone) { + + "use strict"; + + Drupal.webprofiler.views.CollectorsList = Backbone.View.extend({ + tagName: 'section', + + /** + * + * @returns {Drupal.webprofiler.views.CollectorsList} + */ + render: function () { + var collectorsView = this.collection.map(function (collector) { + return (new Drupal.webprofiler.views.CollectorView({model: collector})).render().el; + }); + this.$el.html(collectorsView); + return this; + } + }); + +}(jQuery, Drupal, Backbone)); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/details.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/details.js new file mode 100644 index 000000000..709f17ea7 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/details.js @@ -0,0 +1,20 @@ +(function ($, Drupal, Backbone) { + + "use strict"; + + Drupal.webprofiler.views.DetailsView = Backbone.View.extend({ + el: '#details', + + /** + * + * @returns {Drupal.webprofiler.views.DetailsView} + */ + render: function () { + var template = _.template($("script#" + this.model.get('name')).html()); + + this.$el.html(template(this.model.toJSON())); + return this; + } + }); + +}(jQuery, Drupal, Backbone)); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/layout.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/layout.js new file mode 100644 index 000000000..6168b3430 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/app/views/layout.js @@ -0,0 +1,83 @@ +(function ($, Drupal, drupalSettings, Backbone) { + + "use strict"; + + Drupal.webprofiler.views.Layout = Backbone.View.extend({ + template: _.template( + '
    ' + + '
      ' + + '
      ' + + '
      ' + + 'Choose a collector.' + + '
      ' + ), + + /** + * + * @returns {Drupal.webprofiler.views.Layout} + */ + render: function () { + this.$el.html(this.template()); + + if(this.currentDetails) { + this.currentDetails.setElement(this.$('#details')).render(); + } + + this.overview.setElement(this.$('#overview ul')).render(); + return this; + }, + + /** + * + * @param options + */ + initialize: function (options) { + options.router.collectors.on('request', this.beginSync); + options.router.collectors.on('sync', this.finishSync); + + this.overview = new Drupal.webprofiler.views.CollectorsList({ + collection: options.router.collectors, + router: options.router + }); + }, + + /** + * + * @param collector + */ + setDetails: function (collector) { + if (this.currentDetails) this.currentDetails.remove(); + this.currentDetails = new Drupal.webprofiler.views.DetailsView({model: collector}); + this.render(); + Drupal.attachBehaviors(/*this.$el, drupalSettings*/); + }, + + /** + * + */ + beginSync: function () { + $('.loader--fixed').fadeIn({duration: 100}); + }, + + /** + * + */ + finishSync: function () { + $('.loader--fixed').fadeOut({duration: 100}); + } + }); + + var instance; + Drupal.webprofiler.views.Layout.getInstance = function (options) { + if (!instance) { + instance = new Drupal.webprofiler.views.Layout({ + el: options.el, + router: options.router, + collection: options.router.collectors + }); + } + + return instance; + } + +}(jQuery, Drupal, drupalSettings, Backbone)); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/database.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/database.js new file mode 100644 index 000000000..5f5043079 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/database.js @@ -0,0 +1,68 @@ +/** + * @file + * Database panel app. + */ +(function ($, Drupal, drupalSettings) { + + "use strict"; + + Drupal.behaviors.webprofiler_database = { + attach: function (context) { + $(context).find('.js--explain-trigger').once('js--explain-trigger').each(function () { + + $(this).on('click', function () { + var position = $(this).attr('data-wp-queryPosition'), + wrapper = $(this).parent().parent().find('.js--explain-target'), + loader = $(this).parent().parent().find('.js--loader'); + + if (wrapper.html().length === 0) { + + var url = Drupal.url('admin/reports/profiler/database_explain/' + drupalSettings.webprofiler.token + '/' + position); + + loader.show(); + + $.getJSON(url, function (data) { + _.templateSettings.variable = 'rc'; + var template = _.template( + $("#wp-query-explain-template").html() + ); + wrapper.html(template(data)); + loader.hide(); + delete _.templateSettings.variable; + }); + } + wrapper.toggle(); + + }); + }); + + $(context).find('.js--code-toggle').once('js--code-toggle').each(function () { + $(this).on('click', function () { + $(this).parent().find('.js--code-target').find('code').toggleClass('is--hidden'); + }); + }); + + $(context).find('.js--code-toggle--global').once('js--code-toggle--global').each(function () { + $(this).on('click', function () { + + if($(this).hasClass('js--placeholder-visible')){ + $('.js--placeholder-query').addClass('is--hidden'); + $('.js--original-query').removeClass('is--hidden'); + + }else{ + $('.js--placeholder-query').removeClass('is--hidden'); + $('.js--original-query').addClass('is--hidden'); + } + $(this).toggleClass('js--placeholder-visible'); + }); + }); + + if (typeof hljs != "undefined") { + $('code.sql').each(function (i, block) { + hljs.highlightBlock(block); + }); + } + } + } +}) +(jQuery, Drupal, drupalSettings); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/js/timeline.js b/sites/all/modules/contrib/dev/devel/webprofiler/js/timeline.js new file mode 100644 index 000000000..f550e080e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/js/timeline.js @@ -0,0 +1,223 @@ +/** + * @file + * Timeline panel app. + */ +(function ($, Drupal, drupalSettings, d3) { + + 'use strict'; + + Drupal.behaviors.webprofiler_timeline = { + attach: function (context) { + if (typeof d3 != 'undefined') { + + // data + var data = drupalSettings.webprofiler.time.events; + var parts = []; + var dataL = data.length; + var perL; + var labelW = []; + var rowW; + var scalePadding; + var endTime = parseInt(data[(dataL - 1)].endtime); + var roundTime = Math.ceil(endTime / 1000) * 1000; + var endScale; + + for (var j = 0; j < dataL; j++) { + perL = data[j].periods.length; + for (var k = 0; k < perL; k++) { + parts.push({ + lane: j, + category: data[j].category, + memory: data[j].memory, + name: data[j].name, + start: data[j].periods[k].start, + end: data[j].periods[k].end + }); + } + } + + var tooltipCtrl = function (d, i) { + tooltip.html('memory usage: ' + d.memory + '' + + '' + parseInt(d.start) + 'ms ~ ' + parseInt(d.end) + 'ms'); + tooltip + .style('display', 'block') + .style('left', (d3.event.layerX - 87) + 'px') + .style('top', ((d.lane + 1) * 22) + 'px') + .style('opacity', .9); + }; + + var xscale = d3.scale.linear().domain([0, roundTime]).range([0, 1000]); + d3.select('#timeline').append('svg').attr('height', (dataL + 1) * 22 + 'px').attr('width', '100%').attr('class', 'timeline__canvas'); + + // tooltips + var tooltip = d3.select('#timeline') + .append('div') + .attr('class', 'tooltip'); + + + // Add a rectangle for every data element. + d3.select('.timeline__canvas') + .append('g') + .attr('class', 'timeline__rows') + .attr('x', 0) + .attr('y', 0) + .selectAll('g') + .data(data) + .enter() + .append('rect') + .attr('class', 'timeline__row') + .attr('x', 0) + .attr('y', function (d, i) { + return (i * 22); + }) + .attr('height', 22) + .attr('width', '100%') + .each(function () { + rowW = this.getBoundingClientRect().width; + }); + + // scale + var scale = d3.select('.timeline__canvas') + .append('g') + .attr('class', 'timeline__scale') + .attr('id', 'timeline__scale') + .attr('x', 0) + .attr('y', 0) + .selectAll('g') + .data(data) + .enter() + .append('a') + .attr('xlink:href', function (d) { + return Drupal.webprofiler.helpers.ideLink(d.link); + }) + .attr('class', function (d) { + return 'timeline__label ' + d.category; + }) + .attr('x', xscale(5)) + .attr('y', function (d, i) { + return (((i + 1) * 22) - 5); + }); + + scale.append('title') + .text(function (d) { + return d.name; + }); + + scale.append('text') + .attr('x', xscale(5)) + .attr('y', function (d, i) { + return (((i + 1) * 22) - 5); + }) + .text(function (d) { + return Drupal.webprofiler.helpers.shortLink(d.name); + }) + .each(function (d) { + labelW.push(this.getBoundingClientRect().width); + }); + + scalePadding = Math.max.apply(null, labelW) + 10; + + scale.insert('rect', 'title') + .attr('x', 0) + .attr('y', function (d, i) { + return (i * 22); + }) + .attr('height', 22) + .attr('stroke', 'transparent') + .attr('strokw-width', 1) + .attr('width', scalePadding); + + // times + var events = d3.select('.timeline__canvas') + .insert('g', '.timeline__scale') + .attr('class', 'timeline__parts') + .attr('x', 0) + .attr('y', 0) + .selectAll('g') + .data(parts) + .enter(); + + events.append('rect').attr('class', function (d) { + return 'timeline__period--' + d.category; + }) + .attr('x', function (d) { + return xscale(parseInt(d.start)) + scalePadding; + }) + .attr('y', function (d) { + return d.lane * 22; + }) + .attr('height', 22) + .attr('width', function (d) { + return xscale(Math.max(parseInt(d.end - d.start), 1)); + }); + + events.append('rect') + .attr('class', function (d) { + return 'timeline__period-trigger'; + }) + .attr('x', function (d) { + return xscale(parseInt(d.start)) + scalePadding - 5; + }) + .attr('y', function (d) { + return d.lane * 22; + }) + .attr('height', 22) + .attr('width', function (d) { + return xscale(Math.max(parseInt(d.end - d.start), 1)) + 11; + }) + .on('mouseover', function (d, i) { + tooltipCtrl(d, i); + }) + .on('mouseout', function (d) { + tooltip + .style('display', 'none'); + }); + + // Draw X-axis grid lines + d3.select('.timeline__parts').insert('g', '.timeline__parts') + .selectAll('line') + .data(xscale.ticks(10)) + .enter() + .append('line') + .attr('class', 'timeline__scale--x') + .attr('x1', xscale) + .attr('x2', xscale) + .attr('y1', 0) + .attr('y2', data.length * 22) + .attr('transform', 'translate( ' + scalePadding + ' , 0)'); + + var xAxis = d3.svg.axis().scale(xscale).ticks(10).orient('bottom').tickFormat(function (d) { + return d + ' ms'; + }); + + d3.select('.timeline__parts').insert('g', '.timeline__parts') + .attr('class', 'axis') + .attr('transform', 'translate(' + scalePadding + ', ' + dataL * 22 + ')') + .call(xAxis); + + endScale = xscale(endTime) - rowW - parseInt(scalePadding); + + if (parseInt(xscale(endTime)) > (rowW - parseInt(scalePadding))) { + d3.select('.timeline__canvas') + .call( + d3.behavior.zoom() + .scaleExtent([1, 1]) + .x(xscale) + .on('zoom', function () { + + var t = d3.event.translate; + var tx = t[0]; + + tx = tx > 0 ? 0 : tx; + + tx = tx < endScale ? endScale : tx; + + d3.select('.timeline__parts').attr('transform', 'translate( ' + tx + ' , 0)'); + })); + } + + } + } + }; + +})(jQuery, Drupal, drupalSettings, d3); diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Access/AccessManagerWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Access/AccessManagerWrapper.php new file mode 100644 index 000000000..5de834f7a --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Access/AccessManagerWrapper.php @@ -0,0 +1,83 @@ +currentUser; + } + $route = $route_match->getRouteObject(); + $checks = $route->getOption('_access_checks') ?: array(); + + // Filter out checks which require the incoming request. + if (!isset($request)) { + $checks = array_diff($checks, $this->checkProvider->getChecksNeedRequest()); + } + + $result = AccessResult::neutral(); + if (!empty($checks)) { + $arguments_resolver = $this->argumentsResolverFactory->getArgumentsResolver($route_match, $account, $request); + + if (!$checks) { + return AccessResult::neutral(); + } + $result = AccessResult::allowed(); + foreach ($checks as $service_id) { + $result = $result->andIf($this->performCheck($service_id, $arguments_resolver, $request)); + } + } + return $return_as_object ? $result : $result->isAllowed(); + } + + /** + * {@inheritdoc} + */ + protected function performCheck($service_id, ArgumentsResolverInterface $arguments_resolver, Request $request = NULL) { + $callable = $this->checkProvider->loadCheck($service_id); + $arguments = $arguments_resolver->getArguments($callable); + /** @var \Drupal\Core\Access\AccessResultInterface $service_access **/ + $service_access = call_user_func_array($callable, $arguments); + + if (!$service_access instanceof AccessResultInterface) { + throw new AccessException("Access error in $service_id. Access services must return an object that implements AccessResultInterface."); + } + + if($request) { + $this->dataCollector->addAccessCheck($service_id, $callable, $request); + } + + return $service_access; + } + + /** + * @param \Drupal\webprofiler\DataCollector\RequestDataCollector $dataCollector + */ + public function setDataCollector(RequestDataCollector $dataCollector) { + $this->dataCollector = $dataCollector; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Asset/CssCollectionRendererWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Asset/CssCollectionRendererWrapper.php new file mode 100644 index 000000000..cf13ac688 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Asset/CssCollectionRendererWrapper.php @@ -0,0 +1,40 @@ +assetCollectionRenderer = $assetCollectionRenderer; + $this->dataCollector = $dataCollector; + } + + /** + * {@inheritdoc} + */ + public function render(array $css_assets) { + $this->dataCollector->addCssAsset($css_assets); + + return $this->assetCollectionRenderer->render($css_assets); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Asset/JsCollectionRendererWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Asset/JsCollectionRendererWrapper.php new file mode 100644 index 000000000..dd7596a67 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Asset/JsCollectionRendererWrapper.php @@ -0,0 +1,40 @@ +assetCollectionRenderer = $assetCollectionRenderer; + $this->dataCollector = $dataCollector; + } + + /** + * {@inheritdoc} + */ + public function render(array $js_assets) { + $this->dataCollector->addJsAsset($js_assets); + + return $this->assetCollectionRenderer->render($js_assets); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Cache/CacheBackendWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Cache/CacheBackendWrapper.php new file mode 100644 index 000000000..5b88ef43d --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Cache/CacheBackendWrapper.php @@ -0,0 +1,179 @@ +cacheDataCollector = $cacheDataCollector; + $this->cacheBackend = $cacheBackend; + $this->bin = $bin; + } + + /** + * {@inheritdoc} + */ + public function get($cid, $allow_invalid = FALSE) { + $cache = $this->cacheBackend->get($cid, $allow_invalid); + + if ($cache) { + $cacheCopy = new \StdClass(); + $cacheCopy->cid = $cache->cid; + $cacheCopy->expire = $cache->expire; + $cacheCopy->tags = $cache->tags; + + $this->cacheDataCollector->registerCacheHit($this->bin, $cacheCopy); + } + else { + $this->cacheDataCollector->registerCacheMiss($this->bin, $cid); + } + + return $cache; + } + + /** + * {@inheritdoc} + */ + public function getMultiple(&$cids, $allow_invalid = FALSE) { + $cidsCopy = $cids; + $cache = $this->cacheBackend->getMultiple($cids, $allow_invalid); + + foreach ($cidsCopy as $cid) { + if (in_array($cid, $cids)) { + $this->cacheDataCollector->registerCacheMiss($this->bin, $cid); + } + else { + $cacheCopy = new \StdClass(); + $cacheCopy->cid = $cache[$cid]->cid; + $cacheCopy->expire = $cache[$cid]->expire; + $cacheCopy->tags = $cache[$cid]->tags; + + $this->cacheDataCollector->registerCacheHit($this->bin, $cacheCopy); + } + } + + return $cache; + } + + /** + * {@inheritdoc} + */ + public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = []) { + return $this->cacheBackend->set($cid, $data, $expire, $tags); + } + + /** + * {@inheritdoc} + */ + public function setMultiple(array $items) { + return $this->cacheBackend->setMultiple($items); + } + + /** + * {@inheritdoc} + */ + public function delete($cid) { + return $this->cacheBackend->delete($cid); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple(array $cids) { + return $this->cacheBackend->deleteMultiple($cids); + } + + /** + * {@inheritdoc} + */ + public function deleteTags(array $tags) { + return $this->cacheBackend->deleteTags($tags); + } + + /** + * {@inheritdoc} + */ + public function deleteAll() { + return $this->cacheBackend->deleteAll(); + } + + /** + * {@inheritdoc} + */ + public function invalidate($cid) { + return $this->cacheBackend->invalidate($cid); + } + + /** + * {@inheritdoc} + */ + public function invalidateMultiple(array $cids) { + return $this->cacheBackend->invalidateMultiple($cids); + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) { + return $this->cacheBackend->invalidateTags($tags); + } + + /** + * {@inheritdoc} + */ + public function invalidateAll() { + return $this->cacheBackend->invalidateAll(); + } + + /** + * {@inheritdoc} + */ + public function garbageCollection() { + return $this->cacheBackend->garbageCollection(); + } + + /** + * {@inheritdoc} + */ + public function removeBin() { + return $this->cacheBackend->removeBin(); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Cache/CacheFactoryWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Cache/CacheFactoryWrapper.php new file mode 100644 index 000000000..d37e8941b --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Cache/CacheFactoryWrapper.php @@ -0,0 +1,62 @@ +cacheFactory = $cache_factory; + $this->cacheDataCollector = $cacheDataCollector; + } + + /** + * {@inheritdoc} + */ + public function get($bin) { + if (!isset($this->cacheBackends[$bin])) { + $cache_backend = $this->cacheFactory->get($bin); + $this->cacheBackends[$bin] = new CacheBackendWrapper($this->cacheDataCollector, $cache_backend, $bin); + } + return $this->cacheBackends[$bin]; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/BenchmarkCommand.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/BenchmarkCommand.php new file mode 100644 index 000000000..b04995782 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/BenchmarkCommand.php @@ -0,0 +1,302 @@ +setName('webprofiler:benchmark') + ->setDescription($this->trans('commands.webprofiler.benchmark.description')) + ->addArgument('url', InputArgument::REQUIRED, $this->trans('commands.webprofiler.benchmark.arguments.url')) + ->addOption('runs', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.benchmark.options.runs'), 100) + ->addOption('file', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.benchmark.options.file')) + ->addOption('cache-rebuild', 'cr', InputOption::VALUE_NONE, $this->trans('commands.webprofiler.benchmark.options.cache_rebuild')); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $runs = $input->getOption('runs'); + $file = $input->getOption('file'); + $cache_rebuild = $input->getOption('cache-rebuild'); + + // http://username:password@hostname/ + $url = $input->getArgument('url'); + $url_components = parse_url($url); + $login = isset($url_components['user']) && isset($url_components['pass']); + + $steps = 3; + + if ($cache_rebuild) { + $steps++; + } + + if ($login) { + $steps++; + } + + /** @var \Drupal\Core\Http\Client $client */ + $client = $this->getContainer()->get('http_client'); + + $progress = new ProgressBar($output, $runs + $steps); + $progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%'); + + if ($cache_rebuild) { + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.cache_rebuild')); + $this->RebuildCache(); + $progress->advance(); + } + + if ($login) { + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.login')); + $login_url = "{$url_components['scheme']}://{$url_components['host']}/user/login"; + + // Enable cookies storage. + $cookieJar = new CookieJar(); + $client->setDefaultOption('cookies', $cookieJar); + + // Retrieve a form_build_id using the DomCrawler component. + $response = $client->get($login_url)->getBody()->getContents(); + $crawler = new Crawler($response); + $form_build_id = $crawler->filter('#user-login-form input[name=form_build_id]') + ->attr('value'); + $op = $crawler->filter('#user-login-form input[name=op]')->attr('value'); + + // Login a user. + $response = $client->post($login_url, [ + 'body' => [ + 'name' => $url_components['user'], + 'pass' => $url_components['pass'], + 'form_build_id' => $form_build_id, + 'form_id' => 'user_login_form', + 'op' => $op, + ] + ]); + $progress->advance(); + + if ($response->getStatusCode() != 200) { + throw new \Exception($this->trans('commands.webprofiler.benchmark.messages.error_login')); + } + } + + $datas = []; + for ($i = 0; $i < $runs; $i++) { + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.get')); + $datas[] = $this->getData($client, $url); + $progress->advance(); + } + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.compute_avg')); + $avg = $this->computeAvg($datas); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.compute_median')); + $median = $this->computePercentile($datas, 50); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.compute_95percentile')); + $percentile95 = $this->computePercentile($datas, 95); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.git_hash')); + $gitHash = $this->getGitHash(); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.yaml')); + $yaml = $this->generateYaml($gitHash, $runs, $url, $avg, $median, $percentile95); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.done')); + $progress->finish(); + $output->writeln(''); + + if ($file) { + file_put_contents($file, $yaml); + } + else { + $output->writeln($yaml); + } + + } + + /** + * @param \GuzzleHttp\ClientInterface $client + * @param $url + * + * @return array + */ + private function getData(ClientInterface $client, $url) { + /** @var \GuzzleHttp\Message\ResponseInterface $response */ + $response = $client->get($url); + + $token = $response->getHeader('X-Debug-Token'); + + /** @var \Drupal\webprofiler\Profiler\Profiler $profiler */ + $profiler = $this->getContainer()->get('profiler'); + + /** @var \Symfony\Component\HttpKernel\Profiler\Profile $profile */ + $profile = $profiler->loadProfile($token); + + /** @var \Drupal\webprofiler\DataCollector\TimeDataCollector $timeDataCollector */ + $timeDataCollector = $profile->getCollector('time'); + + return new BenchmarkData( + $token, + $timeDataCollector->getMemory(), + $timeDataCollector->getDuration()); + } + + /** + * @param \Drupal\webprofiler\Command\BenchmarkData[] $datas + * + * @return \Drupal\webprofiler\Command\BenchmarkData + */ + private function computeAvg($datas) { + $profiles = count($datas); + $totalTime = 0; + $totalMemory = 0; + + foreach ($datas as $data) { + $totalTime += $data->getTime(); + $totalMemory += $data->getMemory(); + } + + return new BenchmarkData(NULL, $totalMemory / $profiles, $totalTime / $profiles); + } + + /** + * Computes percentile using The Nearest Rank method. + * + * @param \Drupal\webprofiler\Command\BenchmarkData[] $datas + * @param $percentile + * + * @return \Drupal\webprofiler\Command\BenchmarkData + * + * @throws \Exception + */ + private function computePercentile($datas, $percentile) { + if ($percentile < 0 || $percentile > 100) { + throw new \Exception('Percentile has to be between 0 and 100'); + } + + $profiles = count($datas); + + $n = ceil((($percentile / 100) * $profiles)); + $index = (int) $n - 1; + + $orderedTime = $datas; + $this->getOrderedDatas($orderedTime, 'Time'); + + $orderedMemory = $datas; + $this->getOrderedDatas($orderedMemory, 'Memory'); + + return new BenchmarkData(NULL, $orderedMemory[$index]->getMemory(), $orderedTime[$index]->getTime()); + } + + /** + * @return string + */ + private function getGitHash() { + try { + $process = new Process('git rev-parse HEAD'); + $process->setTimeout(3600); + $process->run(); + $git_hash = $process->getOutput(); + } catch (\Exception $e) { + $git_hash = $this->trans('commands.webprofiler.benchmark.messages.not_git'); + } + + return $git_hash; + } + + /** + * @param \Drupal\webprofiler\Command\BenchmarkData[] $datas + * @param $string + * + * @return array + */ + private function getOrderedDatas(&$datas, $string) { + usort($datas, function ($a, $b) use ($string) { + $method = 'get' . $string; + if ($a->{$method} > $b->{$method}) { + return 1; + } + if ($a->{$method} < $b->{$method}) { + return -1; + } + return 0; + }); + } + + /** + * Rebuilds Drupal cache. + */ + protected function RebuildCache() { + require_once DRUPAL_ROOT . '/core/includes/utility.inc'; + $kernelHelper = $this->getHelper('kernel'); + $classLoader = $kernelHelper->getClassLoader(); + $request = $kernelHelper->getRequest(); + drupal_rebuild($classLoader, $request); + } + + /** + * @param $gitHash + * @param $runs + * @param $url + * @param \Drupal\webprofiler\Command\BenchmarkData $avg + * @param \Drupal\webprofiler\Command\BenchmarkData $median + * @param \Drupal\webprofiler\Command\BenchmarkData $percentile95 + * + * @return string + */ + private function generateYaml($gitHash, $runs, $url, BenchmarkData $avg, BenchmarkData $median, BenchmarkData $percentile95) { + $yaml = Yaml::dump([ + 'date' => date($this->trans('commands.webprofiler.list.rows.time'), time()), + 'git_commit' => $gitHash, + 'number_of_runs' => $runs, + 'url' => $url, + 'results' => [ + 'average' => [ + 'time' => sprintf('%.0f ms', $avg->getTime()), + 'memory' => sprintf('%.1f MB', $avg->getMemory() / 1024 / 1024), + ], + 'median' => [ + 'time' => sprintf('%.0f ms', $median->getTime()), + 'memory' => sprintf('%.1f MB', $median->getMemory() / 1024 / 1024), + ], + '95_percentile' => [ + 'time' => sprintf('%.0f ms', $percentile95->getTime()), + 'memory' => sprintf('%.1f MB', $percentile95->getMemory() / 1024 / 1024), + ], + ], + ]); + return $yaml; + } + + /** + * {@inheritdoc} + */ + public function showMessage($output, $message, $type = 'info') { + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/BenchmarkData.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/BenchmarkData.php new file mode 100644 index 000000000..e587c90ed --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/BenchmarkData.php @@ -0,0 +1,57 @@ +token = $token; + $this->memory = $memory; + $this->time = $time; + } + + /** + * @return mixed + */ + public function getToken() { + return $this->token; + } + + /** + * @return mixed + */ + public function getMemory() { + return $this->memory; + } + + /** + * @return mixed + */ + public function getTime() { + return $this->time; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/ExportCommand.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/ExportCommand.php new file mode 100644 index 000000000..0322febd7 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/ExportCommand.php @@ -0,0 +1,151 @@ +setName('webprofiler:export') + ->setDescription($this->trans('commands.webprofiler.export.description')) + ->addArgument('id', InputArgument::OPTIONAL, $this->trans('commands.webprofiler.export.arguments.id')) + ->addOption('directory', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.export.options.directory'), '/tmp'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $id = $input->getArgument('id'); + $directory = $input->getOption('directory'); + + /** @var \Drupal\webprofiler\Profiler\Profiler $profiler */ + $profiler = $this->getContainer()->get('profiler'); + + try { + if ($id) { + $this->filename = $this->exportSingle($profiler, $id, $directory); + } + else { + $this->filename = $this->exportAll($profiler, $directory, $output); + } + + } catch (\Exception $e) { + $output->writeln('' . $e->getMessage() . ''); + } + } + + /** + * Exports a single profile. + * + * @param \Drupal\webprofiler\Profiler\Profiler $profiler + * @param int $id + * @param string $directory + * + * @return string + * + * @throws \Exception + */ + private function exportSingle(Profiler $profiler, $id, $directory) { + $profile = $profiler->loadProfile($id); + if ($profile) { + $data = $profiler->export($profile); + + $filename = $directory . DIRECTORY_SEPARATOR . $id . '.txt'; + if (file_put_contents($filename, $data) === FALSE) { + throw new \Exception(sprintf( + $this->trans('commands.webprofiler.export.messages.error_writing'), + $filename)); + } + } + else { + throw new \Exception(sprintf( + $this->trans('commands.webprofiler.export.messages.error_no_profile'), + $id)); + } + + return $filename; + } + + /** + * Exports all stored profiles (cap limit at 1000 items). + * + * @param \Drupal\webprofiler\Profiler\Profiler $profiler + * @param string $directory + * @param \Symfony\Component\Console\Output\OutputInterface $output + * + * @return string + */ + private function exportAll(Profiler $profiler, $directory, $output) { + $filename = $directory . DIRECTORY_SEPARATOR . 'profiles_' . time() . '.tar.gz'; + $archiver = new ArchiveTar($filename, 'gz'); + $profiles = $profiler->find(NULL, NULL, 1000, NULL, '', ''); + $progress = new ProgressBar($output, count($profiles) + 2); + $progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%'); + + $files = []; + $progress->start(); + $progress->setMessage($this->trans('commands.webprofiler.export.progress.exporting')); + foreach ($profiles as $profile) { + $data = $profiler->export($profiler->loadProfile($profile['token'])); + $profileFilename = $directory . "/{$profile['token']}.txt"; + file_put_contents($profileFilename, $data); + $files[] = $profileFilename; + $progress->advance(); + } + + $progress->setMessage($this->trans('commands.webprofiler.export.progress.archive')); + $archiver->createModify($files, '', $directory); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.export.progress.delete_tmp')); + foreach ($files as $file) { + unlink($file); + } + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.export.progress.done')); + $progress->finish(); + $output->writeln(''); + + $output->writeln(sprintf( + $this->trans('commands.webprofiler.export.messages.exported_count'), + count($profiles))); + + return $filename; + } + + /** + * {@inheritdoc} + */ + public function showMessage($output, $message, $type = 'info') { + if (!$this->filename) { + return; + } + + $completeMessageKey = 'commands.webprofiler.export.messages.success'; + $completeMessage = sprintf($this->trans($completeMessageKey), $this->filename); + + if ($completeMessage != $completeMessageKey) { + parent::showMessage($output, $completeMessage); + } + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/ListCommand.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/ListCommand.php new file mode 100644 index 000000000..33a1347f2 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Command/ListCommand.php @@ -0,0 +1,73 @@ +setName('webprofiler:list') + ->setDescription($this->trans('commands.webprofiler.list.description')) + ->addOption('ip', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.list.options.ip'), NULL) + ->addOption('url', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.list.options.url'), NULL) + ->addOption('method', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.list.options.method'), NULL) + ->addOption('limit', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.list.options.limit'), 10); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $ip = $input->getOption('ip'); + $url = $input->getOption('url'); + $method = $input->getOption('method'); + $limit = $input->getOption('limit'); + + /** @var \Drupal\webprofiler\Profiler\Profiler $profiler */ + $profiler = $this->getContainer()->get('profiler'); + $profiles = $profiler->find($ip, $url, $limit, $method, '', ''); + + $rows = []; + foreach ($profiles as $profile) { + $row = []; + + $row[] = $profile['token']; + $row[] = $profile['ip']; + $row[] = $profile['method']; + $row[] = $profile['url']; + $row[] = date($this->trans('commands.webprofiler.list.rows.time'), $profile['time']); + + $rows[] = $row; + } + + $table = new Table($output); + $table + ->setHeaders([ + $this->trans('commands.webprofiler.list.header.token'), + $this->trans('commands.webprofiler.list.header.ip'), + $this->trans('commands.webprofiler.list.header.method'), + $this->trans('commands.webprofiler.list.header.url'), + $this->trans('commands.webprofiler.list.header.time'), + ]) + ->setRows($rows); + $table->render(); + } + + /** + * {@inheritdoc} + */ + public function showMessage($output, $message, $type = 'info') { + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/DecoratorPass.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/DecoratorPass.php new file mode 100644 index 000000000..e460b2200 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/DecoratorPass.php @@ -0,0 +1,33 @@ +findDefinition('plugin.manager.mail'); + $definition->setPublic(FALSE); + $container->setDefinition('webprofiler.debug.plugin.manager.mail.default', $definition); + $container->register('plugin.manager.mail', 'Drupal\webprofiler\Mail\MailManagerWrapper') + ->addArgument(new Reference('container.namespaces')) + ->addArgument(new Reference('cache.discovery')) + ->addArgument(new Reference('module_handler')) + ->addArgument(new Reference('config.factory')) + ->addArgument(new Reference('logger.factory')) + ->addArgument(new Reference('string_translation')) + ->addArgument(new Reference('webprofiler.debug.plugin.manager.mail.default')) + ->addArgument(new Reference('webprofiler.mail')) + ->setProperty('_serviceId', 'plugin.manager.mail'); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/EventPass.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/EventPass.php new file mode 100644 index 000000000..5f009afdb --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/EventPass.php @@ -0,0 +1,30 @@ +findDefinition('http_kernel.basic'); + $definition->replaceArgument(1, new Reference('webprofiler.debug.controller_resolver')); + + // Replace the regular event_dispatcher service with the debug one. + $definition = $container->findDefinition('event_dispatcher'); + $definition->setPublic(FALSE); + $container->setDefinition('webprofiler.debug.event_dispatcher.default', $definition); + $container->register('event_dispatcher', 'Drupal\webprofiler\TraceableEventDispatcher') + ->addArgument(new Reference('webprofiler.debug.event_dispatcher.default')) + ->addArgument(new Reference('stopwatch')) + ->setProperty('_serviceId', 'event_dispatcher'); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/ProfilerPass.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/ProfilerPass.php new file mode 100644 index 000000000..10b8d24e7 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/ProfilerPass.php @@ -0,0 +1,64 @@ +hasDefinition('profiler')) { + return; + } + + $definition = $container->getDefinition('profiler'); + + $collectors = new \SplPriorityQueue(); + $order = PHP_INT_MAX; + foreach ($container->findTaggedServiceIds('data_collector') as $id => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $template = NULL; + + if (isset($attributes[0]['template'])) { + if (!isset($attributes[0]['id'])) { + throw new \InvalidArgumentException(sprintf('Data collector service "%s" must have an id attribute in order to specify a template', $id)); + } + if (!isset($attributes[0]['title'])) { + throw new \InvalidArgumentException(sprintf('Data collector service "%s" must have a title attribute', $id)); + } + + $template = [ + $attributes[0]['id'], + $attributes[0]['template'], + $attributes[0]['title'] + ]; + } + + $collectors->insert([$id, $template], [-$priority, --$order]); + } + + $templates = []; + foreach ($collectors as $collector) { + $definition->addMethodCall('add', [new Reference($collector[0])]); + $templates[$collector[0]] = $collector[1]; + } + + $container->setParameter('data_collector.templates', $templates); + + // set parameter to store the public folder path + $path = 'file:' . DRUPAL_ROOT . '/' . PublicStream::basePath() . '/profiler'; + $container->setParameter('data_collector.storage', $path); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/ServicePass.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/ServicePass.php new file mode 100644 index 000000000..df6b87907 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/ServicePass.php @@ -0,0 +1,110 @@ +hasDefinition('webprofiler.services')) { + return; + } + + $definition = $container->getDefinition('webprofiler.services'); + $graph = $container->getCompiler()->getServiceReferenceGraph(); + + $definition->addMethodCall('setServices', [$this->extractData($container, $graph)]); + } + + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraph $graph + * + * @return array + */ + private function extractData(ContainerBuilder $container, ServiceReferenceGraph $graph) { + $data = []; + + foreach ($container->getDefinitions() as $id => $definition) { + $inEdges = []; + $outEdges = []; + + if ($graph->hasNode($id)) { + $node = $graph->getNode($id); + + /** @var \Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphEdge $edge */ + foreach ($node->getInEdges() as $edge) { + /** @var \Symfony\Component\DependencyInjection\Reference $edgeValue */ + $edgeValue = $edge->getValue(); + + $inEdges[] = [ + 'id' => $edge->getSourceNode()->getId(), + 'invalidBehavior' => $edgeValue ? $edgeValue->getInvalidBehavior() : NULL, + 'strict' => $edgeValue ? $edgeValue->isStrict() : NULL, + ]; + } + + + /** @var \Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphEdge $edge */ + foreach ($node->getOutEdges() as $edge) { + /** @var \Symfony\Component\DependencyInjection\Reference $edgeValue */ + $edgeValue = $edge->getValue(); + + $outEdges[] = [ + 'id' => $edge->getDestNode()->getId(), + 'invalidBehavior' => $edgeValue ? $edgeValue->getInvalidBehavior() : NULL, + 'strict' => $edgeValue ? $edgeValue->isStrict() : NULL, + ]; + } + } + + if ($definition instanceof Definition) { + $class = $definition->getClass(); + + try { + $reflectedClass = new \ReflectionClass($class); + $file = $reflectedClass->getFileName(); + } catch (\ReflectionException $e) { + $file = NULL; + } + + $tags = $definition->getTags(); + $public = $definition->isPublic(); + $synthetic = $definition->isSynthetic(); + } + else { + $id = $definition->__toString(); + $class = NULL; + $file = NULL; + $tags = []; + $public = NULL; + $synthetic = NULL; + } + + $data[$id] = [ + 'inEdges' => $inEdges, + 'outEdges' => $outEdges, + 'value' => [ + 'class' => $class, + 'file' => $file, + 'id' => $id, + 'tags' => $tags, + 'public' => $public, + 'synthetic' => $synthetic, + ], + ]; + } + + return $data; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/StoragePass.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/StoragePass.php new file mode 100644 index 000000000..8b26e63f4 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Compiler/StoragePass.php @@ -0,0 +1,32 @@ +hasDefinition('profiler.storage_manager')) { + return; + } + + $definition = $container->getDefinition('profiler.storage_manager'); + + foreach ($container->findTaggedServiceIds('webprofiler_storage') as $id => $attributes) { + $definition->addMethodCall('addStorage', [ + $id, + $attributes[0]['title'], + new Reference($id) + ]); + } + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Config/ConfigFactoryWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Config/ConfigFactoryWrapper.php new file mode 100644 index 000000000..1f4b75eb6 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Config/ConfigFactoryWrapper.php @@ -0,0 +1,44 @@ +dataCollector->addConfigName($name); + return $result; + } + + /** + * {@inheritdoc} + */ + public function loadMultiple(array $names) { + $result = parent::loadMultiple($names); + foreach (array_keys($result) as $name) { + $this->dataCollector->addConfigName($name); + } + return $result; + } + + /** + * @param \Drupal\webprofiler\DataCollector\ConfigDataCollector $dataCollector + */ + public function setDataCollector(ConfigDataCollector $dataCollector) { + $this->dataCollector = $dataCollector; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/DashboardController.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/DashboardController.php new file mode 100644 index 000000000..2b1d93ef1 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/DashboardController.php @@ -0,0 +1,243 @@ +get('profiler'), + $container->get('router'), + $container->get('template_manager'), + $container->get('date.formatter'), + $container->get('profiler.storage_manager') + ); + } + + /** + * Constructs a new WebprofilerController. + * + * @param \Drupal\webprofiler\Profiler\Profiler $profiler + * @param \Symfony\Component\Routing\RouterInterface $router + * @param \Drupal\webprofiler\Profiler\TemplateManager $templateManager + * @param \Drupal\Core\Datetime\DateFormatter $date + * @param \Drupal\webprofiler\Profiler\ProfilerStorageManager $storageManager + */ + public function __construct(Profiler $profiler, RouterInterface $router, TemplateManager $templateManager, DateFormatter $date, ProfilerStorageManager $storageManager) { + $this->profiler = $profiler; + $this->router = $router; + $this->templateManager = $templateManager; + $this->date = $date; + $this->storageManager = $storageManager; + } + + /** + * Generates the dashboard page. + * + * @param Profile $profile + * + * @return array + */ + public function dashboardAction(Profile $profile) { + $this->profiler->disable(); + + $templateManager = $this->templateManager; + $templates = $templateManager->getTemplates($profile); + + $panels = []; + $libraries = ['webprofiler/dashboard']; + $drupalSettings = [ + 'webprofiler' => [ + 'token' => $profile->getToken(), + 'ide_link' => $this->config('webprofiler.config')->get('ide_link'), + 'ide_link_remote' => $this->config('webprofiler.config')->get('ide_link_remote'), + 'ide_link_local' => $this->config('webprofiler.config')->get('ide_link_local'), + 'collectors' => [], + ], + ]; + + foreach ($templates as $name => $template) { + /** @var DrupalDataCollectorInterface $collector */ + $collector = $profile->getCollector($name); + + if ($collector->hasPanel()) { + $rendered = $template->renderBlock('panel', [ + 'token' => $profile->getToken(), + 'name' => $name, + ]); + + $panels[] = [ + '#theme' => 'webprofiler_panel', + '#panel' => $rendered, + ]; + + $drupalSettings['webprofiler']['collectors'][] = [ + 'id' => $name, + 'name' => $name, + 'label' => $collector->getTitle(), + 'summary' => $collector->getPanelSummary(), + 'icon' => $collector->getIcon(), + ]; + + $libraries = array_merge($libraries, $collector->getLibraries()); + $drupalSettings['webprofiler'] += $collector->getDrupalSettings(); + } + } + + $build = []; + $build['panels'] = [ + '#theme' => 'webprofiler_dashboard', + '#profile' => $profile, + '#panels' => $panels, + '#spinner_path' => '/' . $this->moduleHandler() + ->getModule('webprofiler') + ->getPath() . '/images/searching.gif', + '#attached' => [ + 'drupalSettings' => $drupalSettings, + 'library' => $libraries, + ], + ]; + + return $build; + } + + /** + * Generates the list page. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * + * @return array + */ + public function listAction(Request $request) { + $limit = $request->get('limit', 10); + $this->profiler->disable(); + + $ip = $request->query->get('ip'); + $method = $request->query->get('method'); + $url = $request->query->get('url'); + + $profiles = $this->profiler->find($ip, $url, $limit, $method, '', ''); + + $rows = []; + if (count($profiles)) { + foreach ($profiles as $profile) { + $row = []; + $row[] = $this->l($profile['token'], new Url('webprofiler.dashboard', ['profile' => $profile['token']])); + $row[] = $profile['ip']; + $row[] = $profile['method']; + $row[] = $profile['url']; + $row[] = $this->date->format($profile['time']); + + $rows[] = $row; + } + } + else { + $rows[] = [ + [ + 'data' => $this->t('No profiles found'), + 'colspan' => 6, + ], + ]; + } + + $build = []; + + $storage_id = $this->config('webprofiler.config')->get('storage'); + $storage = $this->storageManager->getStorage($storage_id); + + $build['resume'] = [ + '#type' => 'inline_template', + '#template' => '

      {{ message }}

      ', + '#context' => [ + 'message' => $this->t('Profiles stored with %storage service.', ['%storage' => $storage['title']]), + ], + ]; + + $build['filters'] = $this->formBuilder() + ->getForm('Drupal\\webprofiler\\Form\\ProfilesFilterForm'); + + $build['table'] = [ + '#type' => 'table', + '#rows' => $rows, + '#header' => [ + $this->t('Token'), + [ + 'data' => $this->t('Ip'), + 'class' => [RESPONSIVE_PRIORITY_LOW], + ], + [ + 'data' => $this->t('Method'), + 'class' => [RESPONSIVE_PRIORITY_LOW], + ], + $this->t('Url'), + [ + 'data' => $this->t('Time'), + 'class' => [RESPONSIVE_PRIORITY_MEDIUM], + ], + ], + '#sticky' => TRUE, + ]; + + return $build; + } + + /** + * Exposes collector's data as JSON. + * + * @param \Symfony\Component\HttpKernel\Profiler\Profile $profile + * @param $collector + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + */ + public function restCollectorAction(Profile $profile, $collector) { + $this->profiler->disable(); + + $data = $profile->getCollector($collector)->getData(); + + return new JsonResponse(['data' => $data]); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/DatabaseController.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/DatabaseController.php new file mode 100644 index 000000000..f6a5b95e3 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/DatabaseController.php @@ -0,0 +1,95 @@ +get('profiler'), + $container->get('database') + ); + } + + /** + * Constructs a new WebprofilerController. + * + * @param \Symfony\Component\HttpKernel\Profiler\Profiler $profiler + * @param \Drupal\Core\Database\Connection $database + */ + public function __construct(Profiler $profiler, Connection $database) { + $this->profiler = $profiler; + $this->database = $database; + } + + /** + * @param Profile $profile + * @param int $qid + * + * @return JsonResponse + */ + public function explainAction(Profile $profile, $qid) { + $query = $this->getQuery($profile, $qid); + + $data = []; + $result = $this->database->query('EXPLAIN ' . $query['query'], (array) $query['args']) + ->fetchAllAssoc('table'); + $i = 1; + foreach ($result as $row) { + foreach ($row as $key => $value) { + $data[$i][$key] = $value; + } + $i++; + } + + return new JsonResponse(['data' => $data]); + } + + /** + * @param $profile ->getToken() + * @param int $qid + * + * @return array + */ + private function getQuery(Profile $profile, $qid) { + $this->profiler->disable(); + $token = $profile->getToken(); + + if (!$profile = $this->profiler->loadProfile($token)) { + throw new NotFoundHttpException($this->t('Token @token does not exist.', ['@token' => $token])); + } + + /** @var DatabaseDataCollector $databaseCollector */ + $databaseCollector = $profile->getCollector('database'); + + $queries = $databaseCollector->getQueries(); + $query = $queries[$qid]; + + return $query; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/ToolbarController.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/ToolbarController.php new file mode 100644 index 000000000..de0e240e5 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Controller/ToolbarController.php @@ -0,0 +1,108 @@ +get('profiler'), + $container->get('template_manager'), + $container->get('renderer') + ); + } + + /** + * Constructs a new WebprofilerController. + * + * @param \Drupal\webprofiler\Profiler\Profiler $profiler + * @param \Drupal\webprofiler\Profiler\TemplateManager $templateManager + * @param \Drupal\Core\Render\RendererInterface $renderer + */ + public function __construct(Profiler $profiler, TemplateManager $templateManager, RendererInterface $renderer) { + $this->profiler = $profiler; + $this->templateManager = $templateManager; + $this->renderer = $renderer; + } + + /** + * Generates the toolbar. + * + * @param Profile $profile + * + * @return array + */ + public function toolbarAction(Profile $profile) { + $this->profiler->disable(); + + $templates = $this->templateManager->getTemplates($profile); + + $rendered = ''; + foreach ($templates as $name => $template) { + $rendered .= $template->renderBlock('toolbar', [ + 'collector' => $profile->getcollector($name), + 'token' => $profile->getToken(), + 'name' => $name + ]); + } + + $toolbar = [ + '#theme' => 'webprofiler_toolbar', + '#toolbar' => $rendered, + '#token' => $profile->getToken(), + ]; + + return new Response($this->renderer->render($toolbar)); + } + + /** + * @param \Symfony\Component\HttpKernel\Profiler\Profile $profile + * @param \Symfony\Component\HttpFoundation\Request $request + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + */ + public function savePerformanceTimingAction(Profile $profile, Request $request) { + $this->profiler->disable(); + + $data = Json::decode($request->getContent()); + + /** @var $collector */ + $collector = $profile->getCollector('performance_timing'); + $collector->setData($data); + $this->profiler->updateProfile($profile); + + return new JsonResponse(['success' => TRUE]); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/AssetsDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/AssetsDataCollector.php new file mode 100644 index 000000000..7eabac8ff --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/AssetsDataCollector.php @@ -0,0 +1,107 @@ +root = $root; + + $this->data['js'] = []; + $this->data['css'] = []; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $this->data['assets']['installation_path'] = $this->root . '/'; + } + + /** + * @param $jsAsset + */ + public function addJsAsset($jsAsset) { + $this->data['js'] = NestedArray::mergeDeepArray([ + $jsAsset, + $this->data['js'] + ]); + } + + /** + * @param $cssAsset + */ + public function addCssAsset($cssAsset) { + $this->data['css'] = NestedArray::mergeDeepArray([ + $cssAsset, + $this->data['css'] + ]); + } + + /** + * Twig callback to return the amount of CSS files. + */ + public function getCssCount() { + return count($this->data['css']); + } + + /** + * Twig callback to return the amount of JS files. + */ + public function getJsCount() { + return count($this->data['js']) - 1; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'assets'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Assets'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Total: @count', ['@count' => ($this->getCssCount() + $this->getJsCount())]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxQUE0NEI2NTlCQTkxMUUzQkFDRjg2NUVCQ0NFNTcwQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxQUE0NEI2NjlCQTkxMUUzQkFDRjg2NUVCQ0NFNTcwQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjFBQTQ0QjYzOUJBOTExRTNCQUNGODY1RUJDQ0U1NzBCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjFBQTQ0QjY0OUJBOTExRTNCQUNGODY1RUJDQ0U1NzBCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+C1mVdgAAAktJREFUeNrUlk+I6VEUx48/4SVKpkgiCywkK8ksWVhYSFm9hYXFqxfbt2KnLJS9hbKQhZXtFMVmUrKYkZmslPxLSvnvYZx37+2NTDPP03ss5tTpcu/xued77rm/Hw4iwqWNC1cwzv8CPlJ6lUypfSf+k256Ad8R/0HlL4l/uWCSL5zfO/xTLTkczrvx9aDwU7QUX61Ww/Pz88WAdrv9Oplyzz0UPp8PIpEIzrnWJ6EUJBAI2DgYDOD+/h7EYjFwuadz4X80SUFCoRB6vR6Mx2Mwm82QTqfh4eEBNBoN3NzcgFQqhdVqBbvd7s+ZUlkUJpFIoN1uQyQSAYvFAqlUiq2Xy2VwuVwQCoXAZrNBMpmE6XTK4nk83lsqOX0ki0h2xUajgYFAAJVKJRoMBoxGo9hqtXA4HCKNq1ar+Pj4iMFgEFUqFer1egyHw9jv93GxWDCOyWTCA5RaLBZjd9jhcGCz2cRXKxQKqNVqcTQaHeZKpRLqdDoWf3d3h6QMB+hB/nK5BL/fD4lEAsiPmUSfzwf1eh1qtRoYjUaQy+WQz+eBbAoej4fVN5PJgNVqZfV9J586lUBtMplgLpdDEoyktuj1ejEej7M1p9OJbrcbi8UirtdrNjebzQ6MN/KPfT6f436/Z3XudDp4e3uLlUqFQej37XbL5B7DjqEfthQBAgGzfpTJZJDNZpn0zWbDRgI/eQlOdjGFU1coFIfHGu3Lv90qbrfbXRFpJ4POAVGjh/r09PRCP38l3r3Q62RA/NtV3qb8T/Nn4irQXwIMANMNuV/Q8qbhAAAAAElFTkSuQmCC'; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/BlocksDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/BlocksDataCollector.php new file mode 100644 index 000000000..71443a987 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/BlocksDataCollector.php @@ -0,0 +1,149 @@ +entityManager = $entityManager; + + $this->data['blocks']['loaded'] = []; + $this->data['blocks']['rendered'] = []; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $storage = $this->entityManager->getStorage('block'); + + $loaded = $this->entityManager->getLoaded('config', 'block'); + $rendered = $this->entityManager->getRendered('block'); + + if ($loaded) { + $this->data['blocks']['loaded'] = $this->getBlocksData($loaded, $storage); + + } + + if ($rendered) { + $this->data['blocks']['rendered'] = $this->getBlocksData($rendered, $storage); + } + } + + /** + * @return array + */ + public function getRenderedBlocks() { + return $this->data['blocks']['rendered']; + } + + /** + * @return int + */ + public function getRenderedBlocksCount() { + return count($this->getRenderedBlocks()); + } + + /** + * @return array + */ + public function getLoadedBlocks() { + return $this->data['blocks']['loaded']; + } + + /** + * @return int + */ + public function getLoadedBlocksCount() { + return count($this->getLoadedBlocks()); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'blocks'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Blocks'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Loaded: @loaded, rendered: @rendered', [ + '@loaded' => $this->getLoadedBlocksCount(), + '@rendered' => $this->getRenderedBlocksCount() + ]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNDgwMTE3NDA3MjA2ODExOEY2MkNCNjI0NDY3NzkwRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQjg5OTA4OEYwQTgxMUUzQkJDRThFQjA5Q0E1REFCRCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQjg5OTA4N0YwQTgxMUUzQkJDRThFQjA5Q0E1REFCRCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDM4MDExNzQwNzIwNjgxMTg3MUZDQ0I0RjY1RTlEM0IiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDQ4MDExNzQwNzIwNjgxMThGNjJDQjYyNDQ2Nzc5MEQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz68h+kGAAAAWElEQVR42mL8//8/A7UBEwMNwAg3lAVEODg4UCW2Dhw4wDgapgyMtEj8eCMKFPCkyI1GFCJMiUnQVDUUX0SNllK4wxRf+JAdUeQUfaMRRd+ib4SHKUCAAQAMcyf8vLAstgAAAABJRU5ErkJggg=='; + } + + /** + * @param $decorator + * @param $storage + * + * @return array + */ + private function getBlocksData(EntityDecorator $decorator, EntityStorageInterface $storage) { + $blocks = []; + + /** @var \Drupal\block\BlockInterface $block */ + foreach ($decorator->getEntities() as $block) { + /** @var Block $entity */ + if ($entity = $storage->load($block->get('id'))) { + + $route = ''; + if ($entity->hasLinkTemplate('edit-form')) { + $route = $entity->toUrl('edit-form')->toString(); + } + + $id = $block->get('id'); + $blocks[$id] = [ + 'id' => $id, + 'region' => $block->getRegion(), + 'status' => $block->get('status'), + 'theme' => $block->getTheme(), + 'plugin' => $block->get('plugin'), + 'settings' => $block->get('settings'), + 'route' => $route, + ]; + } + } + + return $blocks; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/CacheDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/CacheDataCollector.php new file mode 100644 index 000000000..57371e879 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/CacheDataCollector.php @@ -0,0 +1,172 @@ +data['total'][CacheDataCollector::WEBPROFILER_CACHE_HIT] = 0; + $this->data['total'][CacheDataCollector::WEBPROFILER_CACHE_MISS] = 0; + $this->data['cache'] = []; + } + + /** + * Registers a cache get on a specific cache bin. + * + * @param $cache + */ + public function registerCacheHit($bin, $cache) { + $current = isset($this->data['cache'][$bin][$cache->cid]) ? $this->data['cache'][$bin][$cache->cid] : NULL; + + if (!$current) { + $current = $cache; + $current->{CacheDataCollector::WEBPROFILER_CACHE_HIT} = 0; + $current->{CacheDataCollector::WEBPROFILER_CACHE_MISS} = 0; + $this->data['cache'][$bin][$cache->cid] = $current; + } + + $current->{CacheDataCollector::WEBPROFILER_CACHE_HIT}++; + $this->data['total'][CacheDataCollector::WEBPROFILER_CACHE_HIT]++; + } + + /** + * Registers a cache get on a specific cache bin. + * + * @param $bin + * @param $cid + */ + public function registerCacheMiss($bin, $cid) { + $current = isset($this->data['cache'][$bin][$cid]) ? + $this->data['cache'][$bin][$cid] : NULL; + + if (!$current) { + $current = new \StdClass(); + $current->{CacheDataCollector::WEBPROFILER_CACHE_HIT} = 0; + $current->{CacheDataCollector::WEBPROFILER_CACHE_MISS} = 0; + $this->data['cache'][$bin][$cid] = $current; + } + + $current->{CacheDataCollector::WEBPROFILER_CACHE_MISS}++; + $this->data['total'][CacheDataCollector::WEBPROFILER_CACHE_MISS]++; + } + + /** + * Callback to return the total amount of requested cache CIDS. + * + * @param string $type + * + * @return int + */ + public function getCacheCidsCount($type) { + return $this->data['total'][$type]; + } + + /** + * Callback to return the total amount of hit cache CIDS. + * + * @return int + */ + public function getCacheHitsCount() { + return $this->getCacheCidsCount(CacheDataCollector::WEBPROFILER_CACHE_HIT); + } + + /** + * Callback to return the total amount of miss cache CIDS. + * + * @return int + */ + public function getCacheMissesCount() { + return $this->getCacheCidsCount(CacheDataCollector::WEBPROFILER_CACHE_MISS); + } + + /** + * Callback to return the total amount of hit cache CIDs keyed by bin. + * + * @param $type + * + * @return array + */ + public function cacheCids($type) { + $hits = []; + foreach ($this->data['cache'] as $bin => $caches) { + $hits[$bin] = 0; + foreach ($caches as $cid => $cache) { + $hits[$bin] += $cache->{$type}; + } + } + + return $hits; + } + + /** + * Callback to return hit cache CIDs keyed by bin. + * + * @return array + */ + public function getCacheHits() { + return $this->cacheCids(CacheDataCollector::WEBPROFILER_CACHE_HIT); + } + + /** + * Callback to return miss cache CIDs keyed by bin. + * + * @return array + */ + public function getCacheMisses() { + return $this->cacheCids(CacheDataCollector::WEBPROFILER_CACHE_MISS); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'cache'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Cache'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Hit: @cache_hit, miss: @cache_miss', [ + '@cache_hit' => $this->getCacheCidsCount(CacheDataCollector::WEBPROFILER_CACHE_HIT), + '@cache_miss' => $this->getCacheCidsCount(CacheDataCollector::WEBPROFILER_CACHE_MISS), + ]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2Njc3QTVEQTkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2Njc3QTVEQjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjRGQTVBQzYxOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjRGQTVBQzYyOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+BsBwAAAAAJtJREFUeNpi/P//PwM1ARMDlcEIMdDBweEZjM0IihSgwEx8Gg4cOJCOrhGHOimqe5kF2QVYvDITl0vQvQwTo4oLkS1gQrPpPwiTEBkY6pnwKJ5JyOskJRvkcMUVxjgjhRhDsUUGSQZu3rwZb1j6+voyjhYOI9VAFmKTBTC3oMsTbyAx+RndAqxejo2NJdmL6HoYR6vRwWcgQIABAOn0PsqqgQzcAAAAAElFTkSuQmCC'; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ConfigDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ConfigDataCollector.php new file mode 100644 index 000000000..21f11075d --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ConfigDataCollector.php @@ -0,0 +1,68 @@ +data['config_names'][$name] = isset($this->data['config_names'][$name]) ? $this->data['config_names'][$name] + 1 : 1; + } + + /** + * Callback to display the config names. + */ + public function getConfigNames() { + return $this->data['config_names']; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'config'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Config'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Total: @count', ['@count' => count($this->getConfigNames())]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAcCAYAAACQ0cTtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2Njc3QTVFMjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2Njc3QTVFMzkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY2NzdBNUUwOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY2NzdBNUUxOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+f0Re8gAAA3RJREFUeNrsVk9ME1kY/82fTqfQf5ROW2gpuKCioHbdEDeBxITdxE08eOHkiQOr8cZhNx7k4MGDmpAQxTtHPBgPXjCbeLDE/XtglSUGGyhxgBYLdNpSOu3MPN+MwUSQP1HUZONv8st7897M9833fb837zGEEHwusPiM+P86AxjmEqVKSfaBGuWvlHgf6cUUb9+65WhsbPzo706n0/j5wgWddgOUK1vSKEnSvjgyEQwGQW1xtBv+4jXjc7kcBgcH981gJpPZ3tmnjoa7NgOSXwYK2U+bRu76y73XLBaLIRQKvXe8paVlZ0c35L2ncWho6K3B/v5+JBIJOJ3Ot+PmfV9fnzWvcyJWpeNQ/O0oH7oILhK7Q4cnKR9RjlEWrCVtF0Xy/alTW5z19PRgbGwMo6OjlmHToekolUpheHgYhYL1Pso2F+a+OQd/cwTftdlRH9QhujPGconJx5/VyRMvpD+IWrxCa5bmeJ6/GolEtjibmppCuVyGz+dDd3e3ZTwajWJgYMAatyJiBcyGf0J7pwcdzQEcbktBnW8Fr3NMXcOSeLRpKSCw2cD0tNgEQ3+0q0DGx8ettre3FyMjI+/MrYhReKMcgpII1jePdZVBWQXcBxJYfOFGzsjjYGsyErYvdkLJnN1V+hMTE1Zrpm+jv4FVxoemhjISyWqsKi68JDWwcSUsPhWQSQBSdA4Lz+vh92j18rRyZs/rbLMjE0WaTSJlsab+h1LqNES7BqLpYB2vIPwYx/zjNqwlDsBR8VaRwlzzrs42FGlGtgWaBiVZjzLngVChNYQBGATas5OoKCvQSB6k4x64uXYGTwyW322dmao01WgqczNEujMRRYSjqhUOhwAbz4Klu4nB8yhyEtZyPEqh36BquSLYzpltBdLV1WWJwmzN6EzZb4aPUZCfVRHyeiA53Qi6XKhzuxGurUFD8Qz8Xga1BQ+y8SNLEKoe8jupcEOJ26HWSGN5VYYiJ3Ei9i14GpaNezOn8gIcjISn/3Sks8n2PyFqDzhd1y/XeL02URQ/4ExhwFOSMbtux0qxgoDHBb+7GiLPG9l8ce2vyYI8nQjHMXn/Fzy+uWzu1Ofpeze32/D29scVgLpjwMEfSrSVUe1PoUK/Yib+N/69+zuy8gJ9apGxzgcfCkLMmjspq3b4qZsH03XKPPP1kLofeC3AAMaxXzYck+ZeAAAAAElFTkSuQmCC'; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DatabaseDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DatabaseDataCollector.php new file mode 100644 index 000000000..bdcf49ceb --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DatabaseDataCollector.php @@ -0,0 +1,253 @@ +database = $database; + $this->configFactory = $configFactory; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $connections = []; + foreach (Database::getAllConnectionInfo() as $key => $info) { + $database = Database::getConnection('default', $key); + $connections[$key] = $database->getLogger()->get('webprofiler'); + } + + $this->data['connections'] = array_keys($connections); + + $data = []; + foreach ($connections as $key => $queries) { + foreach ($queries as $query) { + // Remove caller args. + unset($query['caller']['args']); + + // Remove query args element if empty. + if (empty($query['args'])) { + unset($query['args']); + } + + // Save time in milliseconds. + $query['time'] = $query['time'] * 1000; + $query['database'] = $key; + $data[] = $query; + } + } + + $querySort = $this->configFactory->get('webprofiler.config') + ->get('query_sort'); + if ('duration' === $querySort) { + usort( + $data, [ + "Drupal\\webprofiler\\DataCollector\\DatabaseDataCollector", + "orderQueryByTime", + ] + ); + } + + $this->data['queries'] = $data; + + $options = $this->database->getConnectionOptions(); + + // Remove password for security. + unset($options['password']); + + $this->data['database'] = $options; + } + + /** + * @return array + */ + public function getDatabase() { + return $this->data['database']; + } + + /** + * @return int + */ + public function getQueryCount() { + return count($this->data['queries']); + } + + /** + * @return array + */ + public function getQueries() { + return $this->data['queries']; + } + + /** + * Returns the total execution time. + * + * @return float + */ + public function getTime() { + $time = 0; + + foreach ($this->data['queries'] as $query) { + $time += $query['time']; + } + + return $time; + } + + /** + * Returns a color based on the number of executed queries. + * + * @return string + */ + public function getColorCode() { + if ($this->getQueryCount() < 100) { + return 'green'; + } + if ($this->getQueryCount() < 200) { + return 'yellow'; + } + + return 'red'; + } + + /** + * Returns the configured query highlight threshold. + * + * @return int + */ + public function getQueryHighlightThreshold() { + // When a profile is loaded from storage this object is deserialized and + // no constructor is called so we cannot use dependency injection. + return \Drupal::config('webprofiler.config')->get('query_highlight'); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'database'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Database'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Executed queries: @count', ['@count' => $this->getQueryCount()]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQRJREFUeNpi/P//PwM1ARMDlcGogZQDlpMnT7pxc3NbA9nhQKxOpL5rQLwJiPeBsI6Ozl+YBOOOHTv+AOllQNwtLS39F2owKYZ/gRq8G4i3ggxEToggWzvc3d2Pk+1lNL4fFAs6ODi8JzdS7mMRVyDVoAMHDsANdAPiOCC+jCQvQKqBQB/BDbwBxK5AHA3E/kB8nKJkA8TMQBwLxaBIKQbi70AvTADSBiSadwFXpCikpKQU8PDwkGTaly9fHFigkaKIJid4584dkiMFFI6jkTJII0WVmpHCAixZQEXWYhDeuXMnyLsVlEQKI45qFBQZ8eRECi4DBaAlDqle/8A48ip6gAADANdQY88Uc0oGAAAAAElFTkSuQmCC'; + } + + /** + * {@inheritdoc} + */ + public function getLibraries() { + return [ + 'webprofiler/database', + ]; + } + + /** + * {@inheritdoc} + */ + public function getData() { + $data = $this->data; + + $conn = Database::getConnection(); + foreach ($data['queries'] as &$query) { + $explain = TRUE; + $type = 'select'; + + if (strpos($query['query'], 'INSERT') !== FALSE) { + $explain = FALSE; + $type = 'insert'; + } + + if (strpos($query['query'], 'UPDATE') !== FALSE) { + $explain = FALSE; + $type = 'update'; + } + + if (strpos($query['query'], 'CREATE') !== FALSE) { + $explain = FALSE; + $type = 'create'; + } + + if (strpos($query['query'], 'DELETE') !== FALSE) { + $explain = FALSE; + $type = 'delete'; + } + + $query['explain'] = $explain; + $query['type'] = $type; + + $quoted = []; + foreach ((array) $query['args'] as $key => $val) { + $quoted[$key] = is_null($val) ? 'NULL' : $conn->quote($val); + } + + $query['query_args'] = strtr($query['query'], $quoted); + } + + $data['query_highlight_threshold'] = $this->getQueryHighlightThreshold(); + + return $data; + } + + /** + * @param $a + * @param $b + * + * @return int + */ + private function orderQueryByTime($a, $b) { + $at = $a['time']; + $bt = $b['time']; + + if ($at == $bt) { + return 0; + } + return ($at < $bt) ? 1 : -1; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DevelDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DevelDataCollector.php new file mode 100644 index 000000000..ac150c9b3 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DevelDataCollector.php @@ -0,0 +1,110 @@ +menuLinkTree = $menuLinkTree; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $this->data['links'] = $this->develMenuLinks(); + } + + /** + * @return string + */ + public function getLinks() { + return $this->data['links']; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'devel'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Development'); + } + + /** + * {@inheritdoc} + */ + public function hasPanel() { + return FALSE; + } + + /** + * @return array + * Array containing Devel Menu links + */ + protected function develMenuLinks() { + $parameters = new MenuTreeParameters(); + $parameters->setMaxDepth(1)->onlyEnabledLinks(); + $tree = $this->menuLinkTree->load('devel', $parameters); + + $manipulators = array( + array('callable' => 'menu.default_tree_manipulators:checkAccess'), + array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'), + ); + $tree = $this->menuLinkTree->transform($tree, $manipulators); + + $links = array(); + + foreach ($tree as $item) { + if ($item->access->isAllowed()) { + $links[] = array( + 'title' => $item->link->getTitle(), + 'description' => $item->link->getDescription(), + 'url' => $item->link->getUrlObject()->toString(), + ); + } + } + + return $links; + } + + /** + * Returns the collector icon in base64 format. + * + * @return string + * The collector icon. + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAcCAYAAABlL09dAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2RTdCREU2NUVFQUUxMUU1QTc4Q0Q0OEU5RUY0N0YwMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2RTdCREU2NkVFQUUxMUU1QTc4Q0Q0OEU5RUY0N0YwMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjZFN0JERTYzRUVBRTExRTVBNzhDRDQ4RTlFRjQ3RjAzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjZFN0JERTY0RUVBRTExRTVBNzhDRDQ4RTlFRjQ3RjAzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+6sNOKAAAAPVJREFUeNpi/P//PwMtABMDjcCowbQ3mNHe3h6XnBUQTwJiTRzy14E4+8CBAyexSbLgsZQbiI2BOBCH/HqoGgZSDYaBDXjk/uMzuBaLzeVI7FV4DN7n4OAAojvRxL+CwvgnkMFG5bj7BUoVv4lUfBWIrxGp9jcoKM4DsQABhZ+A2BPqs61AzEVA/XuQwT1AzElA4Q8g/gJN951EBN13kMFLgJiHgMJvQCwNxOxAvJwIg7+wEBnGv4D4M1TtLyIMBocxMQUyKExnAzEzEHMQof4/C5GxDHJh4qAp3VhpYC4rKCieAbEQlQ1+yzhamY4aTD+DAQIMAFv+MFaJEyYhAAAAAElFTkSuQmCC'; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DrupalDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DrupalDataCollector.php new file mode 100644 index 000000000..63ec647fa --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DrupalDataCollector.php @@ -0,0 +1,134 @@ +redirectDestination = $redirectDestination; + $this->urlGenerator = $urlGenerator; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $this->data['version'] = Drupal::VERSION; + $this->data['profile'] = drupal_get_profile(); + $this->data['config_url'] = (new Drupal\Core\Url('webprofiler.settings', [], ['query' => $this->redirectDestination->getAsArray()]))->toString(); + + try { + $process = new Process("git log -1 --pretty=format:'%H - %s (%ci)' --abbrev-commit"); + $process->setTimeout(3600); + $process->mustRun(); + $this->data['git_commit'] = $process->getOutput(); + + $process = new Process("git log -1 --pretty=format:'%h' --abbrev-commit"); + $process->setTimeout(3600); + $process->mustRun(); + $this->data['abbr_git_commit'] = $process->getOutput(); + } catch (ProcessFailedException $e) { + $this->data['git_commit'] = $this->data['git_commit_abbr'] = NULL; + } catch (RuntimeException $e) { + $this->data['git_commit'] = $this->data['git_commit_abbr'] = NULL; + } + } + + /** + * @return string + */ + public function getVersion() { + return $this->data['version']; + } + + /** + * @return string + */ + public function getProfile() { + return $this->data['profile']; + } + + /** + * @return string + */ + public function getConfigUrl() { + return $this->data['config_url']; + } + + /** + * @return string + */ + public function getGitCommit() { + return $this->data['git_commit']; + } + + /** + * @return string + */ + public function getAbbrGitCommit() { + return $this->data['abbr_git_commit']; + } + + /** + * Returns the name of the collector. + * + * @return string The collector name + * + * @api + */ + public function getName() { + return 'drupal'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Drupal'); + } + + /** + * {@inheritdoc} + */ + public function hasPanel() { + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCRDU5MUVDNzVGRTkxMUUzODdBMEJDOEZFRjA2QUY5MiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCRDU5MUVDODVGRTkxMUUzODdBMEJDOEZFRjA2QUY5MiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkJENTkxRUM1NUZFOTExRTM4N0EwQkM4RkVGMDZBRjkyIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkJENTkxRUM2NUZFOTExRTM4N0EwQkM4RkVGMDZBRjkyIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+iYsuIgAAAiNJREFUeNq8lj3KwkAQhhOxUPAnoIgWQiysbFLYG2/gEVJaegQ9QfQECh4g3kA9QbxBLMQ2KaxE2G/ffG5Yl/ytiguDJBn32Z15d2dUQojy0wGgDHQwGFjj8Vh7hxFyZIHNZtOt1+uuDPRtYK/Xs4rFIpyJDFQaiIm73a7NYMyq1arf7/dnWWApIN3VHBPzINHwHX5J4FxAKg6j0Wh4aSDREGb8TxqIXJVKJSIDY1Yul30RmgqE87swHkrDq2cCkYNareZ9AmPWarX2mUAk/hswZrgkUoGyIsm7y1ggDachnrNPrVKpEKSJBxZYnC+Xi/l4PL56T99uN/y8KDYCUpj2i2JRzONkWZYyGo0UXdeV4/GobDYb5Xw+J/obxv+mTqdT8rWTpND1ek3ihm3bRNO0F188O44T+UwmE+TQjBXNcDiciLD5fE7Shuu6xDTN0JdGgfi+//J9v98nA6EmqIoHYsJPxuFwIOKxiERDVxNQ4I4PNw1Ram6DIFAWiwUWG5uv+/2+E98V+IdOp7OiZzF6pitMhC2XyxDEfOKg1+t1ldnTtNtthxcB8sAGcgQRUbWGufM8LxQPfJBv3n+73Tq5qgVyibqWdYsASOUfLYyJJ679yKyHadDZbBYqkn8HMHaO3ydMl674rI8RayOACKUo/+l0SuAf12ZI9TRYLSZCJYk76GgbnyA9TxOlMpiqqrnuQhxk7igd8raJbKi/bvX/BBgANANSieJk+XMAAAAASUVORK5CYII='; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DrupalDataCollectorTrait.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DrupalDataCollectorTrait.php new file mode 100644 index 000000000..98a3faf46 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DrupalDataCollectorTrait.php @@ -0,0 +1,115 @@ +data; + } + + /** + * @param $class + * @param $method + * + * @return array + * + * @throws \ReflectionException + */ + public function getMethodData($class, $method) { + $class = is_object($class) ? get_class($class) : $class; + $data = []; + + try { + $reflectedMethod = new \ReflectionMethod($class, $method); + + $data = [ + 'class' => $class, + 'method' => $method, + 'file' => $reflectedMethod->getFilename(), + 'line' => $reflectedMethod->getStartLine(), + ]; + } catch (\ReflectionException $re) { + // TODO: handle the exception. + } finally { + return $data; + } + } + + /** + * @param $value + * + * @return int|string + */ + private function convertToBytes($value) { + if ('-1' === $value) { + return -1; + } + + $value = strtolower($value); + $max = strtolower(ltrim($value, '+')); + if (0 === strpos($max, '0x')) { + $max = intval($max, 16); + } + elseif (0 === strpos($max, '0')) { + $max = intval($max, 8); + } + else { + $max = intval($max); + } + + switch (substr($value, -1)) { + case 't': + $max *= 1024; + break; + + case 'g': + $max *= 1024; + break; + + case 'm': + $max *= 1024; + break; + + case 'k': + $max *= 1024; + break; + } + + return $max; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/EventsDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/EventsDataCollector.php new file mode 100644 index 000000000..bb7772f81 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/EventsDataCollector.php @@ -0,0 +1,90 @@ +getCalledListeners()); + } + + /** + * @return int + */ + public function getNotCalledListenersCount() { + return count($this->getNotCalledListeners()); + } + + /** + * {@inheritdoc} + */ + public function setCalledListeners(array $listeners) { + $listeners = $this->computePriority($listeners); + $this->data['called_listeners'] = $listeners; + } + + /** + * Adds the priority value to the $listeners array. + * + * @param array $listeners + * @return array + */ + private function computePriority(array $listeners) { + foreach ($listeners as &$listener) { + foreach ($listener['class']::getSubscribedEvents() as $event => $methods) { + + if (is_string($methods)) { + $methods = [[$methods], 0]; + } + else { + if (is_string($methods[0])) { + $methods = [$methods]; + } + } + + foreach ($methods as $method) { + if ($listener['event'] === $event) { + if ($listener['method'] === $method[0]) { + $listener['priority'] = isset($method[1]) ? $method[1] : 0; + } + } + } + } + } + + return $listeners; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Events'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Called listeners: @listeners', ['@listeners' => count($this->getCalledListeners())]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABFJJREFUeNrkVVlIY2cY/RMTE81NMkkajUs1OBqkiVsjjAtStGrtSGyFjOjAQNVCKRb66ot9KrjgQx+FUgTBKkURbIfighWl4r6h44pajcZEo3ESTeKS9PzB2AyNZaD1qRcOem+S83/f+c53Lsvj8ZD/+mKTB7gehJTj+2d9fZ1MTk6S0NBQSW9vb97e3t7jmpqaXzIzM185HA7vd4KDg8nGxoaysbGxVCwWm/V6/aDL5TKlpKSQpKSkv5NyuVxyc3Mj7e7u/jw2NjYxJyfnMDIykmGz2UQgEBAWi0XcbjeRSqWhZWVl4v39fXVXV5cqNzf3exxmCNj+9fU1MzQ09JVWq32sUqmMu7u7QhwiDwoKIoeHh2R7e5twOByCwcrQhUShUJjz8vJkw8PDX5+fn8sDkvb3938YHR39rlAoNBoMBgGqtWxubnJRKbu9vZ20trZSQoJnvKioKMvZ2Rn/6urKmpqayvT19ekCks7NzaUnJyeboK0kPj7+cGZmJprH4zGnp6duEBFUTg4ODqjmIfPz87GQxoRnori4ODOKUPuTsnw+RRvPGIYJMZvNDNplYmJiLvPz839oamoSj4yMfAJNuRqN5mV9ff0fOPDF1NSUAt85lclkDkjnys7O/vGOlZLeQgjIgUggnmqHqmMqKip+z8jI8MAFnpKSkpXZ2dn38JkIUAFRQNjt/R2Xv09twBFwAGwClunp6efLy8tZdFgUW1tbiaOjo1/is9fUhcA+YL69fzvzSyQSEQZHfBJBT4J2Bf9qo9Rq9bxcLndeXl4STJrA8B4Mc/atN4pesAk5OTkh1PB0exYXF/kWi4UTFhZG+Hw+wZQJ5BDR7fEPIroYASu9uLggJpOJYO2I0+kkqI47Njb2MdzAKS4uXisvL5/FurIGBgaeYoDS1dVVsrKyQpaWlghsF7hS2IJERER4T4U/qckT4ccP6BYplco+rOcxqn0fZFqj0fgkLS3tV18m0EICktJV9F101xcWFj5Cu+HQ1YGNoeSXWGErpv8IwVOSlZXVh7xw0zy4V1MY3/uXWgetMzB8EZUHw7lKSEjgQ0MONLei2kcTExN5R0dHMehshw7x3umLRKI7YDhaDOSJ18hstq2qquobLMG30DKYkuzs7KggTa5Pf4p/rJReSCud1WplEBYuSMGrra39FG1ywsPDgwsLC+0YFoMAKi0qKupA5c57K0V1XjsdHx+/g6mXUksVFBS8wmF23FeMj48/w7PXiLsxePcG65qPDNCsra15XRCQFNP1AgRPMaA4aOvp6OjQ2O12cVtb20vE389YAHFLS0sO2vbYbLYQHKRHShEEy5ul+kIAe02Q5vy6urouTNyDV8VNT0/PBGzzxW1wRIHsM7T+W3V1tROvEE9lZeUCKlVgSfyD6S9SGsKdnZ1pOp3OkJ6efj04OPgTnmsAlv8PACXa/Q4L4UByuZqbm/UNDQ1vkLL+3+/9ByH9U4ABADscgvUMKuLiAAAAAElFTkSuQmCC'; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ExtensionDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ExtensionDataCollector.php new file mode 100644 index 000000000..bdf08f4f5 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ExtensionDataCollector.php @@ -0,0 +1,156 @@ +moduleHandler = $module_handler; + $this->themeHandler = $theme_handler; + $this->root = $root; + + $this->data['drupal_extension']['modules'] = []; + $this->data['drupal_extension']['themes'] = []; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $modules = $this->moduleHandler->getModuleList(); + $themes = $this->themeHandler->listInfo(); + + $this->data['drupal_extension']['count'] = count($modules) + count($themes); + $this->data['drupal_extension']['modules'] = $modules; + $this->data['drupal_extension']['themes'] = $themes; + $this->data['drupal_extension']['installation_path'] = $this->root . '/'; + } + + /** + * Returns the total number of active extensions. + * + * @return int + */ + public function getExtensionsCount() { + return isset($this->data['drupal_extension']['count']) ? $this->data['drupal_extension']['count'] : 0; + } + + /** + * Returns the total number of active modules. + * + * @return int + */ + public function getModulesCount() { + return count($this->data['drupal_extension']['modules']); + } + + /** + * Returns the total number of active themes. + * + * @return int + */ + public function getThemesCount() { + return count($this->data['drupal_extension']['themes']); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'drupal_extension'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Extensions'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Total: @extensions', ['@extensions' => $this->getExtensionsCount()]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0RkE1QUM1RjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0RkE1QUM2MDkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjRGQTVBQzVEOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjRGQTVBQzVFOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+7LqYtwAAAUBJREFUeNpi/P//PwM1ARMDlcGogYPQQBYQ4eDgQIza/SClQNwIxA3okgcOHCDZhRVAfA6I64F4EjW8fBLqwn1AnAvE8aQaqAc1QAZJ7DMQRwPxayDuAGIBYgwUAeL1QHwWGm6XgLgUSf4FEPcAsQQQ+xBjICh8AmARBgSCQNwFxEFIatYCMagQcEHXzGhvb09s6bAdiL2gbHYg/oEm/x8Y00ykRAofEvsvzlgGmsoIpIWBWBaIlYD4Bg61B5DY/ED8HRrjIH2yINchh+E7IH4CxPeBuAAam8jgBBD3IfEtgZgTiPdC9T1BySloYCcQ20DTmTg0tuegWVICjZSNWLMeFgBKKsU45EDi9kA8H4ivEmsgNsAKxEXQBH0TiMtwFg5EgvnQXHIFmibfUGrgeaghTdBIxAoYR2u9EWAgQIABAKKeRzEX0gXIAAAAAElFTkSuQmCC'; + } + + /** + * {@inheritdoc} + */ + public function getData() { + $data = $this->data; + + // Copy protected properties over public ones to + // let json_encode to find them. + $this->copyToPublic($data['drupal_extension']['modules']); + $this->copyToPublic($data['drupal_extension']['themes']); + + return $data; + } + + /** + * Copies protected properties to public ones. + * + * @param \Drupal\Core\Extension\Extension[] $extensions + */ + private function copyToPublic($extensions) { + foreach ($extensions as &$extension) { + $extension->public_type = $extension->getType(); + $extension->public_name = $extension->getName(); + $extension->public_path = $extension->getPath(); + $extension->public_pathname = $extension->getPathname(); + $extension->public_filename = $extension->getFilename(); + $extension->public_extension_pathname = $extension->getExtensionPathname(); + $extension->public_extension_filename = $extension->getExtensionFilename(); + } + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/FormsDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/FormsDataCollector.php new file mode 100644 index 000000000..79f633253 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/FormsDataCollector.php @@ -0,0 +1,81 @@ +formBuilder = $formBuilder; + + $this->data['forms'] = []; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $this->data['forms'] = $this->formBuilder->getBuildForm(); + } + + /** + * @return array + */ + public function getForms() { + return $this->data['forms']; + } + + /** + * @return array + */ + public function getFormsCount() { + return count($this->getForms()); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'forms'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Forms'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Rendered: @forms', ['@forms' => $this->getFormsCount()]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0RkE1QUM1QjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0RkE1QUM1QzkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjRGQTVBQzU5OTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjRGQTVBQzVBOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+I7130QAAAKRJREFUeNrsVVsKxCAMzIgH8897eDHxGP55LX+0RmjpYxHdyrLsNl9iknEyExA5Z5oZgibH9wPKVlJrnVNKRwZCkPceQ4AhhOpUjJFaeaXUBRjssrW2FvDZGINS2GV9AQb3rpvCvZWhc24rKpdDmp17P2MKjzIFEAD16vdDi73Xbx2/pelZItnz6oiusneB32b4is0Iw8eUfzBlOiCef/l2LAIMAGACWQCJ5bXFAAAAAElFTkSuQmCC'; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/HttpDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/HttpDataCollector.php new file mode 100644 index 000000000..78ce382f9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/HttpDataCollector.php @@ -0,0 +1,168 @@ +middleware = $middleware; + + $this->data['completed'] = []; + $this->data['failed'] = []; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $completed = $this->middleware->getCompletedRequests(); + $failed = $this->middleware->getFailedRequests(); + + foreach ($completed as $data) { + /** @var \GuzzleHttp\Psr7\Request $request */ + $request = $data['request']; + /** @var \GuzzleHttp\Psr7\Response $response */ + $response = $data['response']; + /** @var \GuzzleHttp\TransferStats $stats */ + $stats = $request->stats; + + $uri = $request->getUri(); + $this->data['completed'][] = [ + 'request' => [ + 'method' => $request->getMethod(), + 'uri' => [ + 'schema' => $uri->getScheme(), + 'host' => $uri->getHost(), + 'port' => $uri->getPort(), + 'path' => $uri->getPath(), + 'query' => $uri->getQuery(), + 'fragment' => $uri->getFragment(), + ], + 'headers' => $request->getHeaders(), + 'protocol' => $request->getProtocolVersion(), + 'request_target' => $request->getRequestTarget(), + 'stats' => [ + 'transferTime' => $stats->getTransferTime(), + 'handlerStats' => $stats->getHandlerStats() + ], + ], + 'response' => [ + 'phrase' => $response->getReasonPhrase(), + 'status' => $response->getStatusCode(), + 'headers' => $response->getHeaders(), + 'protocol' => $response->getProtocolVersion(), + ] + ]; + } + + foreach ($failed as $data) { + /** @var \GuzzleHttp\Psr7\Request $request */ + $request = $data['request']; + /** @var \GuzzleHttp\Psr7\Response $response */ + $response = $data['response']; + + $uri = $request->getUri(); + $this->data['failed'][] = [ + 'request' => [ + 'method' => $request->getMethod(), + 'uri' => [ + 'schema' => $uri->getScheme(), + 'host' => $uri->getHost(), + 'port' => $uri->getPort(), + 'path' => $uri->getPath(), + 'query' => $uri->getQuery(), + 'fragment' => $uri->getFragment(), + ], + 'headers' => $request->getHeaders(), + 'protocol' => $request->getProtocolVersion(), + 'request_target' => $request->getRequestTarget(), + ], + 'response' => [ + 'phrase' => $response->getReasonPhrase(), + 'status' => $response->getStatusCode(), + 'headers' => $response->getHeaders(), + 'protocol' => $response->getProtocolVersion(), + ] + ]; + } + } + + /** + * @return int + */ + public function getCompletedRequestsCount() { + return count($this->getCompletedRequests()); + } + + /** + * @return array + */ + public function getCompletedRequests() { + return $this->data['completed']; + } + + /** + * @return int + */ + public function getFailedRequestsCount() { + return count($this->getFailedRequests()); + } + + /** + * @return array + */ + public function getFailedRequests() { + return $this->data['failed']; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'http'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Http'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Completed @completed, error @error', [ + '@completed' => $this->getCompletedRequestsCount(), + '@error' => $this->getFailedRequestsCount() + ]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAATlJREFUeNrsleERgjAMha0TdISOwAZ2BEZghI7ABo7AOQFuwAjoBLgBblBbfb0LudByp/4jdw+PNnxtkqYq7/3h13Y8/MF26B9spfo6yAX1QSa6EY2Y0xLrzROgddAMOcgLmuFbhDqyG00W8Rm1JWgEWCG0oQBuJKjGigNUs/xWUJdJheZQJzhZFGkgKWkw1mM8bmTCvOPQcSVXrTA+Ydc0kujXJGg6p5VwrG5BJzb2CLriN9kT74afUylPloQ+kdDPELXpg1qGvwbtURwjFGkkC8RIZw6d2QeO7MII81wRbDm0Z068Zf0G1bxQl8z1UH1zoQwk9NRZdkPoHt+KbZoqa+HYZS4T3iimdEvRDrIF4KIRclBNjk+uSB2/eBJUvR9KSek26BZHOit2zl3oqkV91P6//3N7CTAAIIc/qj2gy4gAAAAASUVORK5CYII='; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/MailDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/MailDataCollector.php new file mode 100644 index 000000000..79551502d --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/MailDataCollector.php @@ -0,0 +1,99 @@ +messages = []; + } + + /** + * Collects data for the given Request and Response. + * + * @param Request $request A Request instance + * @param Response $response A Response instance + * @param \Exception $exception An Exception instance + * + * @api + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $this->data['mail'] = $this->messages; + } + + /** + * @param $message + * @param \Drupal\Core\Mail\MailInterface $mail + */ + public function addMessage($message, MailInterface $mail) { + $class = get_class($mail); + $method = $this->getMethodData($class, 'mail'); + + $this->messages[] = [ + 'message' => $message, + 'method' => $method, + ]; + } + + /** + * @return int + */ + public function getMailSent() { + return count($this->data['mail']); + } + + /** + * Returns the name of the collector. + * + * @return string The collector name + * + * @api + */ + public function getName() { + return 'mail'; + } + + /** + * Returns the datacollector title. + * + * @return string + * The datacollector title. + */ + public function getTitle() { + return $this->t('Mail'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Total: @count', ['@count' => $this->getMailSent()]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABcAAAAcCAYAAACK7SRjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6N0NEOTU1MjM0OThFMTFFMDg3NzJBNTE2ODgwQzMxMzQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6N0NEOTU1MjQ0OThFMTFFMDg3NzJBNTE2ODgwQzMxMzQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMEQ5OTQ5QzQ5OEMxMUUwODc3MkE1MTY4ODBDMzEzNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3Q0Q5NTUyMjQ5OEUxMUUwODc3MkE1MTY4ODBDMzEzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpkRnSAAAAJ0SURBVHjaYvz//z8DrQATAw3BqOFYAaO9vT1FBhw4cGCAXA5MipxBQUHT3r17l0AVAxkZ/wkLC89as2ZNIcjlYkALXKnlWqBZTH/+/PEDmQsynLW/v3+NoaHhN2oYDjJn8uTJK4BMNpDhPwsLCwOKiop2+fn5vafEYC8vrw8gc/Lz8wOB3B8gw/nev38vn5WV5eTg4LA/Ly/vESsrK2npmYmJITU19SnQ8L0gc4DxpwgyF2S4EEjB58+f+crLy31YWFjOt7S0XBYUFPxHjMEcHBz/6+rqboqJiZ0qKSnxBpkDlRICGc4MU/j792+2CRMm+L18+fLSxIkTDykoKPzBZ7CoqOi/np6eE8rKylvb29v9fvz4wYEkzYKRzjk5OX/LyMjcnDRpEkjjdisrK6wRraOj8wvokAMLFy788ejRoxcaGhrPCWai4ODgB8DUE3/mzBknYMToASNoMzAfvEVW4+Tk9LmhoWFbTU2NwunTpx2BjiiMjo6+hm4WCzJHUlLyz+vXrxkfP36sDOI/ffpUPikpibe7u3sLsJjQW7VqlSrQxe+Avjmanp7u9PbtWzGQOmCCkARmmu/m5uYfT548yY/V5UpKSl+2b9+uiiz26dMnIWBSDTp27NgdYGrYCIzwE7m5uR4wg2Hg/PnzSsDI/QlKOcjZ3wGUBLm5uf+DwLdv38gub4AG/xcSEvr35s0bZmCB5sgCE/z69SsjyDJKMtG/f/8YQQYD8wkoGf8H51AbG5sH1CpbQBnQ09PzBiiHgoIFFHlBQGwLxLxUMP8dqJgH4k3gIhfIkAKVYkDMTmmhCHIxEL8A4peMo02L4WU4QIABANxZAoDIQDv3AAAAAElFTkSuQmCC'; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/PerformanceTimingDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/PerformanceTimingDataCollector.php new file mode 100644 index 000000000..6a5e699f7 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/PerformanceTimingDataCollector.php @@ -0,0 +1,83 @@ +data['performance'] = $data; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'performance_timing'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Performance Timing'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + if (isset($this->data['performance'])) { + $performanceData = new PerformanceTimingData($this->data['performance']); + return $this->t('TTFB: @ttfb', ['@ttfb' => sprintf('%.0f ms', $performanceData->getTtfbTiming())]); + } + + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpi/P//PwO1ARMDDcCooUPEUEYi1AgAcQIQ+0P5H4B4IxAvwKaYmCSqAMT7gbgBajhMrB8qLkCsoQFQQ0CuOw/EBjgsLIAajmEouvdBhukD8UQgdkASwwXuA7EhNEhwuvQ8iXHSj2Q53FBY7BtADVxIoqEfoQYnYJPEF3bEROZ6WDDBvO+ALcCxJCsBAmpA4SuA7P2PBDQUEOGTDTA1TNCYs6dCRgIlxQswQ0GMB0A8nwgv4gqa+VCXgpMWC1QiEerF9WgaDmJJp/OhkUNIHUHQgJ4ecQHkiMKXXALQIowqpdR8pJi/AA0qvC4lFsyHYqK8zzhaRQ8NQwECDABNaU12xhTp2QAAAABJRU5ErkJggg=='; + } + + /** + * {@inheritdoc} + */ + public function getData() { + $data = $this->data; + + if (isset($this->data['performance'])) { + $performanceData = new PerformanceTimingData($this->data['performance']); + $data['performance']['computed']['DNS lookup time'] = $performanceData->getDNSTiming(); + $data['performance']['computed']['TCP handshake time'] = $performanceData->getTCPTiming(); + $data['performance']['computed']['Time to first byte'] = $performanceData->getTtfbTiming(); + $data['performance']['computed']['Data download time'] = $performanceData->getDataTiming(); + $data['performance']['computed']['DOM building time'] = $performanceData->getDomTiming(); + } + + return $data; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/PhpConfigDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/PhpConfigDataCollector.php new file mode 100644 index 000000000..d959a4818 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/PhpConfigDataCollector.php @@ -0,0 +1,163 @@ +data = [ + 'token' => $response->headers->get('X-Debug-Token'), + 'php_version' => PHP_VERSION, + 'xdebug_enabled' => extension_loaded('xdebug'), + 'xhprof_enabled' => extension_loaded('xhprof'), + 'eaccel_enabled' => extension_loaded('eaccelerator') && ini_get('eaccelerator.enable'), + 'apc_enabled' => extension_loaded('apc') && ini_get('apc.enabled'), + 'xcache_enabled' => extension_loaded('xcache') && ini_get('xcache.cacher'), + 'wincache_enabled' => extension_loaded('wincache') && ini_get('wincache.ocenabled'), + 'zend_opcache_enabled' => extension_loaded('Zend OPcache') && ini_get('opcache.enable'), + 'sapi_name' => php_sapi_name() + ]; + } + + /** + * Gets the token. + * + * @return string The token + */ + public function getToken() { + return $this->data['token']; + } + + /** + * Gets the PHP version. + * + * @return string The PHP version + */ + public function getPhpVersion() { + return $this->data['php_version']; + } + + /** + * Returns true if the XDebug is enabled. + * + * @return Boolean true if XDebug is enabled, false otherwise + */ + public function hasXDebug() { + return $this->data['xdebug_enabled']; + } + + /** + * Returns true if the XHProf is enabled. + * + * @return Boolean true if XHProf is enabled, false otherwise + */ + public function hasXHProf() { + return $this->data['xhprof_enabled']; + } + + /** + * Returns true if EAccelerator is enabled. + * + * @return Boolean true if EAccelerator is enabled, false otherwise + */ + public function hasEAccelerator() { + return $this->data['eaccel_enabled']; + } + + /** + * Returns true if APC is enabled. + * + * @return Boolean true if APC is enabled, false otherwise + */ + public function hasApc() { + return $this->data['apc_enabled']; + } + + /** + * Returns true if Zend OPcache is enabled + * + * @return Boolean true if Zend OPcache is enabled, false otherwise + */ + public function hasZendOpcache() { + return $this->data['zend_opcache_enabled']; + } + + /** + * Returns true if XCache is enabled. + * + * @return Boolean true if XCache is enabled, false otherwise + */ + public function hasXCache() { + return $this->data['xcache_enabled']; + } + + /** + * Returns true if WinCache is enabled. + * + * @return Boolean true if WinCache is enabled, false otherwise + */ + public function hasWinCache() { + return $this->data['wincache_enabled']; + } + + /** + * Returns true if any accelerator is enabled. + * + * @return Boolean true if any accelerator is enabled, false otherwise + */ + public function hasAccelerator() { + return $this->hasApc() || $this->hasZendOpcache() || $this->hasEAccelerator() || $this->hasXCache() || $this->hasWinCache(); + } + + /** + * Gets the PHP SAPI name. + * + * @return string The environment + */ + public function getSapiName() { + return $this->data['sapi_name']; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('PHP Config'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('PHP: @version', ['@version' => $this->getPhpVersion()]); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'php_config'; + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAMAAAC5xgRsAAAAZlBMVEX///////////////////////////////////////////////////////////////////////////////////////////+ZmZmZmZlISEhJSUmdnZ1HR0fR0dFZWVlpaWlfX18/Pz+puygPAAAAIXRSTlMACwwlJygpLzIzNjs8QEtMUmd6e32AucDBw8fIydTm6u5l8MjvAAAAo0lEQVR42r2P2Q6CMBBFL6XsZRGRfZv//0nbDBNEE19MnJeTc5ILKf58ahiUwzy/AJpIWwREwQnEXRdbGCLjrO+djWRvVMiJcigxB7viGogxDdJpSmHEmCVPS7YczJvgUu+CS30IvtbNYZMvsGVo2mVpG/kbm4auiCpdcC3YPCAhSpAdUzaAn6qPKZtUT6ZSzb4bi2hdo9MQ1nX4ASjfV+/4/Z40pyCHrNTbIgAAAABJRU5ErkJggg=='; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/RequestDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/RequestDataCollector.php new file mode 100644 index 000000000..9de8f4942 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/RequestDataCollector.php @@ -0,0 +1,83 @@ +controllerResolver = $controllerResolver; + $this->accessCheck = []; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + parent::collect($request, $response, $exception); + + $controller = $this->controllerResolver->getController($request); + + $this->data['controller'] = $this->getMethodData($controller[0], $controller[1]); + $this->data['access_check'] = $this->accessCheck; + } + + /** + * @param $service_id + * @param $callable + * @param $request + */ + public function addAccessCheck($service_id, $callable, Request $request) { + $this->accessCheck[$request->getPathInfo()][] = [ + 'service_id' => $service_id, + 'callable' => $this->getMethodData($callable[0], $callable[1]), + ]; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Request'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->data['status_code'].' '.$this->data['status_text']; + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAQAAADYBBcfAAACvElEQVR42tVTbUhTYRTerDCnKVoUUr/KCZmypA9Koet0bXNLJ5XazDJ/WFaCUY0pExRZXxYiJgsxWWjkaL+yK+po1gjyR2QfmqWxtBmaBtqWGnabT++c11Fu4l/P4VzOPc95zoHznsNZodIbLDdRcKnc1Bu8DAK45ZsOnykQNMopsNooLxCknb0cDq5vml9FtHiIgpBR0R6iihYyFMTDt2Lg56ObPkI6TMGXSof1EV67IqCwisJSWliFAG/E0CfFIiebdNypcxi/1zgyFiIiZ3sJQr0RQx5frLa6k7SOKRo3oMFNR5t62h2rttKXEOKFqDCxtXNmmBokO2KKTlp3IdWuT2dYRNGKwEXEBCcL172G5FG0aIxC0kR9PBTVH1kkwQn+IqJnCE33EalVzT9GJQS1tAdD3CKicJYFrxqx7W2ejCEdZy1FiC5tZxHhLJKOZaRdQJAyV/YAvDliySALHxmxR4Hqe2iwvaOR/CEuZYJFSgYhVbZRkA8KGdEktrqnqra90NndCdkt77fjIHIhexOrfO6O3bbbOj/rqu5IptgyR3sU93QbOYhquZK4MCDp0Ina/PLsu5JvbCTRaapUdUmIV/RzoMdsk/0hWRNdAvKOmvqlN0drsJbJf1P4YsQ5lGrJeuosiOUgbOC8cto3LfOXTdVd7BqZsQKbse+0jUL6WPcesqs4MNSUTQAxGjwFiC8m3yzmqwHJBWYKBJ9WNqW/dHkpU/osch1Yj5RJfXPfSEe/2UPsN490NPfZG5CKyJmcV5ayHyzy7BMqsXfuHhGK/cjAIeSpR92gehR55D8TcQhDEKJwytBJ4fr4NULvrEM8NszfJPyxDoHYAQ1oPCWmIX4gifmDS/DV2DKeb25FHWr76yEG7/9L4YFPeiQQ4/8LkgJ8Et+NncTCsYqzXAEXa7CWdPZzGWdlyV+vST0JanfPvwAAAABJRU5ErkJggg=='; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/RoutingDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/RoutingDataCollector.php new file mode 100644 index 000000000..e892b9a34 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/RoutingDataCollector.php @@ -0,0 +1,91 @@ +routeProvider = $routeProvider; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $this->data['routing'] = []; + foreach ($this->routeProvider->getAllRoutes() as $route_name => $route) { + // @TODO Find a better visual representation. + $this->data['routing'][] = [ + 'name' => $route_name, + 'path' => $route->getPath(), + ]; + } + } + + /** + * @return int + */ + public function getRoutesCount() { + return count($this->routing()); + } + + /** + * Twig callback for displaying the routes. + */ + public function routing() { + return $this->data['routing']; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Routing'); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'routing'; + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Defined routes: @route', ['@route' => $this->getRoutesCount()]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPxJREFUeNrkVsERREAQ7D0koXwlIAISkIGXvwAkoJSfHITg5SMUVR4eMuCxZ1xxV+5uuTL30lV8bPX09PSuFVJKcOOGP+DipLrrusoFdV1Lz/NgmiaKosC0XrAopYT0fc/f/jAMa41Pj23bkrpi9bRpGmRZ9lRKFSzLkl9UHMI4jijLcuaaSZMkQdd1fNMn5r0EHIFhGEjTlDenYRjCcZyHUnqRwXEcz77sYepM+Z1yTOEXZEFVVYcUba2iItsNcbr9IAiw5HMd1CJZtU1VpG3bIooi3gPF933keY43pb9gb1Bskdrap58luMjvRNO04wcK18RfIa59mbgLMAASuWsKAyoEhgAAAABJRU5ErkJggg=='; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ServicesDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ServicesDataCollector.php new file mode 100644 index 000000000..b7ecb1095 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ServicesDataCollector.php @@ -0,0 +1,162 @@ +container = $container; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + if ($this->getServicesCount()) { + + $tracedData = []; + if ($this->container instanceof TraceableContainer) { + $tracedData = $this->container->getTracedData(); + } + + foreach (array_keys($this->getServices()) as $id) { + $this->data['services'][$id]['initialized'] = ($this->container->initialized($id)) ? TRUE : FALSE; + $this->data['services'][$id]['time'] = isset($tracedData[$id]) ? $tracedData[$id] : NULL; + } + } + } + + /** + * @param $services + */ + public function setServices($services) { + $this->data['services'] = $services; + } + + /** + * @return array + */ + public function getServices() { + return $this->data['services']; + } + + /** + * @return int + */ + public function getServicesCount() { + return count($this->getServices()); + } + + /** + * @return array + */ + public function getInitializedServices() { + return array_filter($this->getServices(), function($item) { + return $item['initialized']; + }); + } + + /** + * @return int + */ + public function getInitializedServicesCount() { + return count($this->getInitializedServices()); + } + + /** + * @return array + */ + public function getInitializedServicesWithoutWebprofiler() { + return array_filter($this->getInitializedServices(), function($item) { + return strpos($item['value']['id'], 'webprofiler') !== 0; + }); + } + + /** + * @return int + */ + public function getInitializedServicesWithoutWebprofilerCount() { + return count($this->getInitializedServicesWithoutWebprofiler()); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'services'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Services'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Initialized: @count', [ + '@count' => $this->getInitializedServicesCount(), + ]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQVJREFUeNrkVe0NgjAQBeMAdYO6AWxQNtANGEFHcALZANyADegGsIFsIBvgu6Q/LtWmxdTEjyYvd6Hw8t5de6TzPCex1yp5w/pz0rVrQymVIXSAACqt9TGG0p0hpHWIZb9lebWENOXn1FgWbL8GJHACNHs+ohyjlxSEZPEcKGYC6SbEvljgUHzEOR3IXiiB6YOTlLqdo1Y54tZHDLIauCHtETtn962P6EUVqhhi0gelIJEEk1MjMg9Py9xol/0SuBqFva/DULY3ZSqQF767v8TyZKv83tFXWVaEufsUG+DCr2nwQLGOlGQNizZPy3fMU16K5uV5+qQEpFTC+hCN9Pd/0XcBBgBxwVqjDkAznAAAAABJRU5ErkJggg=='; + } + + /** + * @return array + */ + public function getData() { + $data = $this->data; + + $http_middleware = array_filter($data['services'], function($service) { + return isset($service['value']['tags']['http_middleware']); + }); + + foreach ($http_middleware as &$service) { + $service['value']['handle_method'] = $this->getMethodData($service['value']['class'], 'handle'); + } + + uasort($http_middleware, function ($a, $b) { + $va = $a['value']['tags']['http_middleware'][0]['priority']; + $vb = $b['value']['tags']['http_middleware'][0]['priority']; + + if ($va == $vb) { + return 0; + } + return ($va > $vb) ? -1 : 1; + }); + + $data['http_middleware'] = $http_middleware; + + return $data; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/StateDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/StateDataCollector.php new file mode 100644 index 000000000..20bab443e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/StateDataCollector.php @@ -0,0 +1,65 @@ +data['state_get'][$key] = isset($this->data['state_get'][$key]) ? $this->data['state_get'][$key] + 1 : 1; + } + + /** + * Twig callback to show all requested state keys. + */ + public function getStateKeysCount() { + return count($this->data['state_get']); + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('State'); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'state'; + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Total: @variables', ['@variables' => $this->getStateKeysCount()]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2Njc3QTVERTkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2Njc3QTVERjkxNkMxMUUzQjA3OUEzQTNEMUVGMjVDOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY2NzdBNURDOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY2NzdBNUREOTE2QzExRTNCMDc5QTNBM0QxRUYyNUM4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+tji6wwAAAUFJREFUeNpi/P//PwO1ARMDDcDQMZSBgZExE4h/AvF/KuA/QFzKCCS+TZ40iVNeXp5i9718+ZIhNS3tL5OoqChVDAQBcXFxBqBZzDQJU0Z2Do7/FubmVDPwzNmzwymdhoSEgDEM8PDwgPkJCQk49bDgMxCkEWQAyKAXL14wXLhwgWHChAlguTVr1pBnKMiwgoICsEEgC+7cuQPGHR0deH3HzMLC0iAjI4NV8tSpUwzv3r1jYGNjY3BycoJbgg88e/6cuIg6cuQImAZ5n2qx7+HhAaZtbGzArqXYUJCBIAwKR1jMq6ioMBgYGJAeURUVFWCNsHAERRAsKYFofJGF01CQIaBktGPHDjANAjU1NWBXg8IWX/gOobz/8+fP7x8/fqSKYV++fGH4+vXrX1B1EgXET6hUnTwH4jTG0Xqf6gAgwABnDJn0eW/JMwAAAABJRU5ErkJggg=='; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ThemeDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ThemeDataCollector.php new file mode 100644 index 000000000..717cfdfd6 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ThemeDataCollector.php @@ -0,0 +1,257 @@ +themeManager = $themeManager; + $this->themeNegotiator = $themeNegotiator; + $this->profile = $profile; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $activeTheme = $this->themeManager->getActiveTheme(); + + $this->data['activeTheme'] = [ + 'name' => $activeTheme->getName(), + 'path' => $activeTheme->getPath(), + 'engine' => $activeTheme->getEngine(), + 'owner' => $activeTheme->getOwner(), + 'baseThemes' => $activeTheme->getBaseThemes(), + 'extension' => $activeTheme->getExtension(), + 'styleSheetsRemove' => $activeTheme->getStyleSheetsRemove(), + 'libraries' => $activeTheme->getLibraries(), + 'regions' => $activeTheme->getRegions(), + ]; + + if ($this->themeNegotiator instanceof ThemeNegotiatorWrapper) { + $this->data['negotiator'] = [ + 'class' => $this->getMethodData($this->themeNegotiator->getNegotiator(), 'determineActiveTheme'), + 'id' => $this->themeNegotiator->getNegotiator()->_serviceId, + ]; + } + } + + /** + * {@inheritdoc} + */ + public function lateCollect() { + $this->data['twig'] = serialize($this->profile); + } + + /** + * @return string + */ + public function getActiveTheme() { + return $this->data['activeTheme']; + } + + /** + * @return array + */ + public function getThemeNegotiator() { + return $this->data['negotiator']; + } + + /** + * @return int + */ + public function getTime() { + return $this->getProfile()->getDuration() * 1000; + } + + /** + * @return mixed + */ + public function getTemplateCount() { + return $this->getComputedData('template_count'); + } + + /** + * @return mixed + */ + public function getTemplates() { + return $this->getComputedData('templates'); + } + + /** + * @return mixed + */ + public function getBlockCount() { + return $this->getComputedData('block_count'); + } + + /** + * @return mixed + */ + public function getMacroCount() { + return $this->getComputedData('macro_count'); + } + + /** + * @return \Twig_Markup + */ + public function getHtmlCallGraph() { + $dumper = new HtmlDumper(); + + return new \Twig_Markup($dumper->dump($this->getProfile()), 'UTF-8'); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'theme'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Theme'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Name: @name', ['@name' => $this->getActiveTheme()['name']]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlVJREFUeNrMlk1IVFEUx53K6AOjUBQri6SihQiRziaRQegDlKJFBCpSiZtoE4hWtFFRwcCVBqEWfaAE7SoXajXLQoRcFAUSaBgUJQxKYUrT78D/wevx3psaE7rw49x377ln7vl4500kmUxm/OuxJmMVxrqwzVgsth5xHKogCnthCd7BU7gdj8envOciQe5j8CyiFQq0tAgDsA+OaG0eLmF4INQoxrYg+iEXhqEGiqGBw/3SGUeU6MgC61mBMUU5GzEKJ6ES5S5knbbXui/jmv8MjCkGMxEPFbsFyAeL1y6pXEenSO4fctlIhCXqqtmGj5ADcblZoX1z8aLn/Es475soDhcyfwt3LPDK8jPYFlIcZrCCEH0LqlP7NXN/CCUL/CvmkynKcczPoNv9Y5LXuHVC2Y6mMFoetOG4/0kl9LfDquMyN076ue/I11ANHSqVH0pgLUz7GK2HjUHuf1DGm/jVYZVYJeILz5163oy46Tn/GL5r396yc+hXOzcckiyVQp7CsVNvmI2DHoODcMFcR6eZ+RO4747pJuYjcFilssMM6vB7+Or8IGMWWjDWx7ntzHvglCWZtfbf3n0UtiJ6FVO/8QbuQh+H59C37nUD9sAj1k6ENZQylctuJWsGJswDDibY32C3gitKsCUwyt7nlK0vpMc2Wh/Q4zIcxeDztDu/QjSoPmujzWswZef3GLRXuV2xPa3u/yDwjfoDgxE1nP1aquOG91b64StWlp0xtaKvKbfMUS1maukWvEj7a6pxBg6oL1iddnsbSFox/S/+TKyK0V8CDABrCdI/1oTqiQAAAABJRU5ErkJggg=='; + } + + /** + * @return array + */ + public function getData() { + $data = $this->data; + + $data['twig'] = [ + 'callgraph' => (string) $this->getHtmlCallGraph(), + 'render_time' => $this->getTime(), + 'template_count' => $this->getTemplateCount(), + 'templates' => $this->getTemplates(), + 'block_count' => $this->getBlockCount(), + 'macro_count' => $this->getMacroCount(), + ]; + + return $data; + } + + /** + * @return mixed|\Twig_Profiler_Profile + */ + private function getProfile() { + if (NULL === $this->profile) { + $this->profile = unserialize($this->data['twig']); + } + + return $this->profile; + } + + /** + * @param $index + * + * @return mixed + */ + private function getComputedData($index) { + if (NULL === $this->computed) { + $this->computed = $this->computeData($this->getProfile()); + } + + return $this->computed[$index]; + } + + /** + * @param \Twig_Profiler_Profile $profile + * + * @return array + */ + private function computeData(\Twig_Profiler_Profile $profile) { + $data = [ + 'template_count' => 0, + 'block_count' => 0, + 'macro_count' => 0, + ]; + + $templates = []; + foreach ($profile as $p) { + $d = $this->computeData($p); + + $data['template_count'] += ($p->isTemplate() ? 1 : 0) + $d['template_count']; + $data['block_count'] += ($p->isBlock() ? 1 : 0) + $d['block_count']; + $data['macro_count'] += ($p->isMacro() ? 1 : 0) + $d['macro_count']; + + if ($p->isTemplate()) { + if (!isset($templates[$p->getTemplate()])) { + $templates[$p->getTemplate()] = 1; + } + else { + $templates[$p->getTemplate()]++; + } + } + + foreach ($d['templates'] as $template => $count) { + if (!isset($templates[$template])) { + $templates[$template] = $count; + } + else { + $templates[$template] += $count; + } + } + } + $data['templates'] = $templates; + + return $data; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/TimeDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/TimeDataCollector.php new file mode 100644 index 000000000..d2aa0442d --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/TimeDataCollector.php @@ -0,0 +1,157 @@ +data['memory_limit'] = $this->convertToBytes(ini_get('memory_limit')); + $this->updateMemoryUsage(); + } + + /** + * {@inheritdoc} + */ + public function lateCollect() { + parent::lateCollect(); + + $this->updateMemoryUsage(); + } + + /** + * Gets the memory. + * + * @return int + * The memory + */ + public function getMemory() { + return $this->data['memory']; + } + + /** + * Gets the PHP memory limit. + * + * @return int + * The memory limit + */ + public function getMemoryLimit() { + return $this->data['memory_limit']; + } + + /** + * Updates the memory usage data. + */ + public function updateMemoryUsage() { + $this->data['memory'] = memory_get_peak_usage(TRUE); + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Timeline'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Duration: @duration', ['@duration' => sprintf('%.0f ms', $this->getDuration())]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAcCAYAAABoMT8aAAABqUlEQVR42t2Vv0sCYRyHX9OmEhsMx/YKGlwLQ69DTEUSBJEQEy5J3FRc/BsuiFqEIIcQIRo6ysUhoaBBWhoaGoJwiMJLglRKrs8bXgienmkQdPDAwX2f57j3fhFJkkbiPwTK5bIiFoul3kmPud8MqKMewDXpwuGww+12n9hsNhFnlijYf/Z4PDmO45Yxo+10ZFGTyWRMEItU6AdCx7lczkgd6n7J2Wx2xm63P6jJMk6n80YQBBN1aUDv9XqvlAbbm2LE7/cLODRB0un0VveAeoDC8/waCQQC18MGQqHQOcEKvw8bcLlcL6TfYnVtCrGRAlartUUYhmn1jKg/E3USjUYfhw3E4/F7ks/nz4YNFIvFQ/ogbUYikdefyqlU6gnuOg2YK5XKvs/n+xhUDgaDTVEUt+HO04ABOBA5isViDTU5kUi81Wq1AzhWMEkDGmAEq2C3UCjcYXGauDvfEsuyUjKZbJRKpVvM8IABU9SVX+cxYABmwIE9cFqtVi9xtgvsC2AHbIAFoKey0gdlHEyDObAEWLACFsEsMALdIJ80+dK0bTS95v7+v/AJnis0eO906QwAAAAASUVORK5CYII='; + } + + /** + * {@inheritdoc} + */ + public function getLibraries() { + return [ + 'webprofiler/timeline', + ]; + } + + /** + * {@inheritdoc} + */ + public function getDrupalSettings() { + /** @var StopwatchEvent[] $collectedEvents */ + $collectedEvents = $this->getEvents(); + + if (!empty($collectedEvents)) { + $sectionPeriods = $collectedEvents['__section__']->getPeriods(); + $endTime = end($sectionPeriods)->getEndTime(); + $events = []; + + foreach ($collectedEvents as $key => $collectedEvent) { + if ('__section__' != $key) { + $periods = []; + foreach ($collectedEvent->getPeriods() as $period) { + $periods[] = [ + 'start' => sprintf("%F", $period->getStartTime()), + 'end' => sprintf("%F", $period->getEndTime()), + ]; + } + + $events[] = [ + "name" => $key, + "category" => $collectedEvent->getCategory(), + "origin" => sprintf("%F", $collectedEvent->getOrigin()), + "starttime" => sprintf("%F", $collectedEvent->getStartTime()), + "endtime" => sprintf("%F", $collectedEvent->getEndTime()), + "duration" => sprintf("%F", $collectedEvent->getDuration()), + "memory" => sprintf("%.1F", $collectedEvent->getMemory() / 1024 / 1024), + "periods" => $periods, + ]; + } + } + + return ['time' => ['events' => $events, 'endtime' => $endTime]]; + } + else { + return ['time' => ['events' => [], 'endtime' => 0]]; + } + } + + /** + * @return array + */ + public function getData() { + $data = $this->data; + + $data['duration'] = $this->getDuration(); + $data['initTime'] = $this->getInitTime(); + + return $data; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/TranslationsDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/TranslationsDataCollector.php new file mode 100644 index 000000000..0918aafb4 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/TranslationsDataCollector.php @@ -0,0 +1,99 @@ +translation = $translation; + $this->urlGenerator = $urlGenerator; + + $this->data['translations']['translated'] = []; + $this->data['translations']['untranslated'] = []; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + if($this->translation instanceof TranslationManagerWrapper) { + /** \Drupal\webprofiler\StringTranslation\TranslationManagerWrapper $this->translation */ + $this->data['translations']['translated'] = $this->translation->getTranslated(); + $this->data['translations']['untranslated'] = $this->translation->getUntranslated(); + } + $data['user_interface_translations_path'] = $this->urlGenerator->generateFromRoute('locale.translate_page'); + } + + /** + * @return int + */ + public function getTranslatedCount() { + return count($this->data['translations']['translated']); + } + + /** + * @return int + */ + public function getUntranslatedCount() { + return count($this->data['translations']['untranslated']); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'translations'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Translations'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Translated: @translated, untranslated: @untranslated', [ + '@translated' => $this->getTranslatedCount(), + '@untranslated' => $this->getUntranslatedCount() + ]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAgpJREFUeNrUVrFuwjAQPUcZiAQDQ6WEbgwwMCJVqhjgBxhYGSvEDzBkZYSBH+ADoFJHlg4MdIMKxiLBXKAbUmEm9bPiyAREEgRDTzqZ2Mfzu7tnJ8xxHLq1aXQHuwsos2078p9arVYg0yL3CXcnwCdubKDp3F+5myFin9xYK0xNzQiZm1c3KpVKUb1ep2QyebvuFwoFSqfTlM/nbweay+VoOBxeDar7JwC03W49UO5fs9nsZTwef8qYUqkUjSmAYrGYqKlhGFSpVH45oBFFdkdM0RjUst1uC7Z45ofjWdO0t8Ph8CDjyuUyZTIZ6na7tNvtTmSn+2s5n88FIMwdPzhgUWYBA2A2mxVjIpEQarEsiwaDAS2XSxPH9OI1xVNnbmo0Go28eTDkAOL3YrGg/X4v1tfr9WmjLplsULVaFawbjYbHkjfzqPsr7o9RwJE2HOkifbAGS1ljdL/G/ScsWK/XE+NmsxGMkW6/3xfgWMMIpu9hLgkEN5tNDwClgAoADjAYRszr8m7kD2gGU5uh1hEjmoGUUU/oGLVEXaVhA2yoKYAwRwFx/Ezj8bjHEIYaSgVgDptNp1NiePG5QCfS4qyZXANop9MR7JANWAFA6hWgWEcmqqTYOWBVl0jZf0ViU+hUNk0AyVf0ObYK0+8IslupF8qlkxVWdoipsaCPiaBr7uwr+t98ofwJMADuIP9IDjFbLwAAAABJRU5ErkJggg=='; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/UserDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/UserDataCollector.php new file mode 100644 index 000000000..e2b7a1015 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/UserDataCollector.php @@ -0,0 +1,141 @@ +currentUser = $currentUser; + $this->entityManager = $entityManager; + $this->configFactory = $configFactory; + $this->providerCollector = $providerCollector; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $this->data['name'] = $this->currentUser->getDisplayName(); + $this->data['authenticated'] = $this->currentUser->isAuthenticated(); + + $this->data['roles'] = []; + $storage = $this->entityManager->getStorage('user_role'); + foreach ($this->currentUser->getRoles() as $role) { + $entity = $storage->load($role); + $this->data['roles'][] = $entity->label(); + } + + foreach ($this->providerCollector->getSortedProviders() as $provider_id => $provider) { + if ($provider->applies($request)) { + $this->data['provider'] = $provider_id; + } + } + + $this->data['anonymous'] = $this->configFactory->get('user.settings') + ->get('anonymous'); + } + + /** + * @return \Drupal\Core\Session\AccountInterface + */ + public function getUserName() { + return $this->data['name']; + } + + /** + * @return bool + */ + public function getAuthenticated() { + return $this->data['authenticated']; + } + + /** + * @return array + */ + public function getRoles() { + return $this->data['roles']; + } + + /** + * @return string + */ + public function getProvider() { + return $this->data['provider']; + } + + /** + * @return string + */ + public function getAnonymous() { + return $this->data['anonymous']; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'user'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('User'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->getUserName(); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAcCAYAAAB75n/uAAAC70lEQVR42u2V3UtTYRzHu+mFwCwK+gO6CEryPlg7yiYx50vDqUwjFIZDSYUk2ZTmCysHvg9ZVggOQZiRScsR4VwXTjEwdKZWk8o6gd5UOt0mbev7g/PAkLONIOkiBx+25/v89vuc85zn2Q5Fo9F95UDwnwhS5HK5TyqVRv8m1JN6k+AiC+fn54cwbgFNIrTQ/J9IqDcJJDGBHsgDgYBSq9W6ysvLPf39/SSUUU7zsQ1yc3MjmN90OBzfRkZG1umzQqGIxPSTkIBjgdDkaGNjoza2kcFgUCE/QvMsq6io2PV6vQu1tbV8Xl7etkql2qqvr/+MbDE/Pz8s9OP2Cjhwwmw29+4R3Kec1WZnZ4fn5uamc3Jyttra2qbH8ero6JgdHh5+CvFHq9X6JZHgzODgoCVW0NPTY0N+ltU2Nzdv4GqXsYSrPp+vDw80aLFYxru6uhyQ/rDb7a8TCVJDodB1jUazTVlxcXGQ5/mbyE+z2u7u7veY38BVT3Z2djopm5qa6isrK/tQWVn5qb29fSGR4DC4PDAwMEsZHuArjGnyGKutq6v7ajQaF6urq9/MzMz0QuSemJiwQDwGkR0POhhXgILjNTU1TaWlpTxlOp1uyWQyaUjMajMzM8Nut/tJQUHBOpZppbCwkM/KytrBznuL9xDVxBMo8KXHYnu6qKjIivmrbIy67x6Px4Yd58W672ApfzY0NCyNjo7OZmRkiAv8fr+O47iwmABXtoXaG3uykF6vX7bZbF6cgZWqqiqezYkKcNtmjO+CF2AyhufgjsvlMiU7vXEF+4C4ALf9CwdrlVAqlcFkTdRqdQSHLUDgBEeSCrArAsiGwENs0XfJBE6ncxm1D8Aj/B6tigkkJSUlmxSwLYhMDeRsyyUCd+lHrWxtbe2aTCbbZTn1ZD92F0Cr8GBfgnsgDZwDt8EzMBmHMXBLqD0PDMAh9Gql3iRIESQSIAXp4CRIBZeEjIvDFZAm1J4C6UK9ROiZcvCn/+8FvwHtDdJEaRY+oQAAAABJRU5ErkJggg=='; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ViewsDataCollector.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ViewsDataCollector.php new file mode 100644 index 000000000..64706f72f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/ViewsDataCollector.php @@ -0,0 +1,118 @@ +entityManager = $entityManager; + $this->view_executable_factory = $view_executable_factory; + + $this->data['views'] = []; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $views = $this->view_executable_factory->getViews(); + $storage = $this->entityManager->getStorage('view'); + + /** @var TraceableViewExecutable $view */ + foreach ($views as $view) { + if ($view->executed) { + $data = [ + 'id' => $view->storage->id(), + 'current_display' => $view->current_display, + 'build_time' => $view->getBuildTime(), + 'execute_time' => $view->getExecuteTime(), + 'render_time' => $view->getRenderTime(), + ]; + + $entity = $storage->load($view->storage->id()); + if ($entity->hasLinkTemplate('edit-display-form')) { + $route = $entity->toUrl('edit-display-form'); + $route->setRouteParameter('display_id', $view->current_display); + $data['route'] = $route->toString(); + } + + $this->data['views'][] = $data; + } + } + +// TODO: also use those data. +// $loaded = $this->entityManager->getLoaded('view'); +// +// if ($loaded) { +// /** @var \Drupal\webprofiler\Entity\EntityStorageDecorator $views */ +// foreach ($loaded->getEntities() as $views) { +// $this->data['views'][] = array( +// 'id' => $views->get('id'), +// ); +// } +// } + } + + /** + * @return int + */ + public function getViewsCount() { + return count($this->data['views']); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'views'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Views'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Total: @count', ['@count' => $this->getViewsCount()]); + } + + /** + * {@inheritdoc} + */ + public function getIcon() { + return 'iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNDgwMTE3NDA3MjA2ODExOEY2MkNCNjI0NDY3NzkwRCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozNEVFREM2NkQ4MUMxMUUzQkMwRUNBMkQwMzE4QjVBMyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozNEVFREM2NUQ4MUMxMUUzQkMwRUNBMkQwMzE4QjVBMyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDQ4MDExNzQwNzIwNjgxMThGNjJDQjYyNDQ2Nzc5MEQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDQ4MDExNzQwNzIwNjgxMThGNjJDQjYyNDQ2Nzc5MEQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6vqYfFAAAAXUlEQVR42mL8//8/A7UBEwMNwKih1AcsIGLz5s1USwK+vr6MLMgcSg2EOW6IhSkycHR0BHth//79jMh8fACmlr4uRbcVnT8apqNhOhqmAxZR1CyoGUfrfaoDgAADAA4QNs9x67RnAAAAAElFTkSuQmCC'; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Decorator.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Decorator.php new file mode 100644 index 000000000..270117d1e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Decorator.php @@ -0,0 +1,94 @@ +object = $object; + } + + /** + * Return the original (i.e. non decorated) object. + * + * @return mixed + * The original object. + */ + public function getOriginalObject() { + $object = $this->object; + while ($object instanceof Decorator) { + $object = $object->getOriginalObject(); + } + return $object; + } + + /** + * Returns true if $method is a PHP callable. + * + * @param string $method + * The method name. + * @param bool $checkSelf + * + * @return bool|mixed + */ + public function isCallable($method, $checkSelf = FALSE) { + //Check the original object + $object = $this->getOriginalObject(); + if (is_callable([$object, $method])) { + return $object; + } + // Check Decorators. + $object = $checkSelf ? $this : $this->object; + while ($object instanceof Decorator) { + if (is_callable([$object, $method])) { + return $object; + } + $object = $this->object; + } + return FALSE; + } + + /** + * @param $method + * @param $args + * + * @return mixed + * + * @throws \Exception + */ + public function __call($method, $args) { + if ($object = $this->isCallable($method)) { + return call_user_func_array([$object, $method], $args); + } + throw new \Exception( + 'Undefined method - ' . get_class($this->getOriginalObject()) . '::' . $method + ); + } + + /** + * @param $property + * + * @return null + */ + public function __get($property) { + $object = $this->getOriginalObject(); + if (property_exists($object, $property)) { + return $object->$property; + } + return NULL; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DependencyInjection/TraceableContainer.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DependencyInjection/TraceableContainer.php new file mode 100644 index 000000000..adea886e7 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DependencyInjection/TraceableContainer.php @@ -0,0 +1,66 @@ +stopwatch && $this->has('stopwatch')) { + $this->stopwatch = parent::get('stopwatch'); + $this->stopwatch->openSection(); + $this->hasStopwatch = TRUE; + } + + if ('stopwatch' === $id) { + return $this->stopwatch; + } + + Timer::start($id); + if ($this->hasStopwatch) { + $e = $this->stopwatch->start($id, 'service'); + } + + $service = parent::get($id, $invalidBehavior); + + $this->tracedData[$id] = Timer::stop($id); + if ($this->hasStopwatch && $e->isStarted()) { + $e->stop(); + } + + return $service; + } + + /** + * @return array + */ + public function getTracedData() { + return $this->tracedData; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/DrupalDataCollectorInterface.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/DrupalDataCollectorInterface.php new file mode 100644 index 000000000..8a02b353f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/DrupalDataCollectorInterface.php @@ -0,0 +1,67 @@ +entities = []; + } + + /** + * {@inheritdoc} + */ + public function resetCache(array $ids = NULL) { + $this->getOriginalObject()->resetCache($ids); + } + + /** + * {@inheritdoc} + */ + public function loadMultiple(array $ids = NULL) { + $entities = $this->getOriginalObject()->loadMultiple($ids); + + $this->entities = array_merge($this->entities, $entities); + + return $entities; + } + + /** + * {@inheritdoc} + */ + public function load($id) { + $entity = $this->getOriginalObject()->load($id); + + $this->entities[$id] = $entity; + + return $entity; + } + + /** + * {@inheritdoc} + */ + public function loadUnchanged($id) { + return $this->getOriginalObject()->loadUnchanged($id); + } + + /** + * {@inheritdoc} + */ + public function loadRevision($revision_id) { + return $this->getOriginalObject()->loadRevision($revision_id); + } + + /** + * {@inheritdoc} + */ + public function deleteRevision($revision_id) { + $this->getOriginalObject()->deleteRevision($revision_id); + } + + /** + * {@inheritdoc} + */ + public function loadByProperties(array $values = []) { + $entities = $this->getOriginalObject()->loadByProperties($values); + + $this->entities = array_merge($this->entities, $entities); + + return $entities; + } + + /** + * {@inheritdoc} + */ + public function create(array $values = []) { + return $this->getOriginalObject()->create($values); + } + + /** + * {@inheritdoc} + */ + public function delete(array $entities) { + $this->getOriginalObject()->delete($entities); + } + + /** + * {@inheritdoc} + */ + public function save(EntityInterface $entity) { + return $this->getOriginalObject()->save($entity); + } + + /** + * {@inheritdoc} + */ + public function getQuery($conjunction = 'AND') { + return $this->getOriginalObject()->getQuery($conjunction); + } + + /** + * {@inheritdoc} + */ + public function getEntityTypeId() { + return $this->getOriginalObject()->getEntityTypeId(); + } + + /** + * {@inheritdoc} + */ + public function getEntityType() { + return $this->getOriginalObject()->getEntityType(); + } + + /** + * {@inheritdoc} + */ + public static function getIDFromConfigName($config_name, $config_prefix) { + return substr($config_name, strlen($config_prefix . '.')); + } + + /** + * {@inheritdoc} + */ + public function createFromStorageRecord(array $values) { + return $this->getOriginalObject()->createFromStorageRecord($values); + } + + /** + * {@inheritdoc} + */ + public function updateFromStorageRecord(ConfigEntityInterface $entity, array $values) { + return $this->getOriginalObject() + ->updateFromStorageRecord($entity, $values); + } + + /** + * {@inheritdoc} + */ + public function getAggregateQuery($conjunction = 'AND') { + return $this->getOriginalObject()->getAggregateQuery($conjunction); + } + + /** + * {@inheritdoc} + */ + public function loadOverrideFree($id) { + return $this->getOriginalObject()->loadOverrideFree($id); + } + + /** + * {@inheritdoc} + */ + public function loadMultipleOverrideFree(array $ids = NULL) { + return $this->getOriginalObject()->loadMultipleOverrideFree($ids); + } + + /** + * {@inheritdoc} + */ + public function importCreate($name, Config $new_config, Config $old_config) { + $this->getOriginalObject()->importCreate($name, $new_config, $old_config); + } + + /** + * {@inheritdoc} + */ + public function importUpdate($name, Config $new_config, Config $old_config) { + $this->getOriginalObject()->importUpdate($name, $new_config, $old_config); + } + + /** + * {@inheritdoc} + */ + public function importDelete($name, Config $new_config, Config $old_config) { + $this->getOriginalObject()->importDelete($name, $new_config, $old_config); + } + + /** + * {@inheritdoc} + */ + public function importRename($old_name, Config $new_config, Config $old_config) { + $this->getOriginalObject()->importRename($old_name, $new_config, $old_config); + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $entity_type, + $container->get('config.factory'), + $container->get('uuid'), + $container->get('language_manager') + ); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/RoleStorageDecorator.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/RoleStorageDecorator.php new file mode 100644 index 000000000..ede1e502a --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/RoleStorageDecorator.php @@ -0,0 +1,19 @@ +getOriginalObject()->isPermissionInRoles($permission, $rids); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/ShortcutSetStorageDecorator.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/ShortcutSetStorageDecorator.php new file mode 100644 index 000000000..1371009d8 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/ShortcutSetStorageDecorator.php @@ -0,0 +1,56 @@ +getOriginalObject()->getToplevelTids($vids); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityDecorator.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityDecorator.php new file mode 100644 index 000000000..3ec186db9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityDecorator.php @@ -0,0 +1,24 @@ +entities; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityManagerWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityManagerWrapper.php new file mode 100644 index 000000000..c6381a0d2 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityManagerWrapper.php @@ -0,0 +1,239 @@ +entityManager = $entityManager; + } + + /** + * {@inheritdoc} + */ + public function getStorage($entity_type) { + /** @var ConfigEntityStorageInterface $handler */ + $handler = $this->getHandler($entity_type, 'storage'); + $type = ($handler instanceof ConfigEntityStorageInterface) ? 'config' : 'content'; + + if (!isset($this->loaded[$type][$entity_type])) { + $handler = $this->getStorageDecorator($entity_type, $handler); + $this->loaded[$type][$entity_type] = $handler; + } + else { + $handler = $this->loaded[$type][$entity_type]; + } + + return $handler; + } + + /** + * {@inheritdoc} + */ + public function getViewBuilder($entity_type) { + /** @var EntityViewBuilderInterface $handler */ + $handler = $this->getHandler($entity_type, 'view_builder'); + + if ($handler instanceof EntityViewBuilderInterface) { + if (!isset($this->rendered[$entity_type])) { + $handler = new EntityViewBuilderDecorator($handler); + $this->rendered[$entity_type] = $handler; + } + else { + $handler = $this->rendered[$entity_type]; + } + } + + return $handler; + } + + /** + * @param $entity_type + * @param $handler + * + * @return \Drupal\webprofiler\Entity\EntityDecorator + */ + private function getStorageDecorator($entity_type, $handler) { + if ($handler instanceof ConfigEntityStorageInterface) { + switch ($entity_type) { + case 'taxonomy_vocabulary': + return new VocabularyStorageDecorator($handler); + break; + case 'user_role': + return new RoleStorageDecorator($handler); + break; + case 'shortcut_set': + return new ShortcutSetStorageDecorator($handler); + break; + default: + return new ConfigEntityStorageDecorator($handler); + break; + } + } + return $handler; + } + + /** + * @param $type + * @param $entity_type + * + * @return array + */ + public function getLoaded($type, $entity_type) { + return isset($this->loaded[$type][$entity_type]) ? $this->loaded[$type][$entity_type] : NULL; + } + + /** + * @param $entity_type + * + * @return array + */ + public function getRendered( $entity_type) { + return isset($this->rendered[$entity_type]) ? $this->rendered[$entity_type] : NULL; + } + + /** + * {@inheritdoc} + */ + public function useCaches($use_caches = FALSE) { + $this->entityManager->useCaches($use_caches); + } + + /** + * {@inheritdoc} + */ + public function hasDefinition($plugin_id) { + return $this->entityManager->hasDefinition($plugin_id); + } + + /** + * {@inheritdoc} + */ + public function getAccessControlHandler($entity_type) { + return $this->entityManager->getAccessControlHandler($entity_type); + } + + /** + * {@inheritdoc} + */ + public function clearCachedDefinitions() { + $this->entityManager->clearCachedDefinitions(); + } + + /** + * {@inheritdoc} + */ + public function getListBuilder($entity_type) { + return $this->entityManager->getListBuilder($entity_type); + } + + /** + * {@inheritdoc} + */ + public function getFormObject($entity_type, $operation) { + return $this->entityManager->getFormObject($entity_type, $operation); + } + + /** + * {@inheritdoc} + */ + public function getRouteProviders($entity_type) { + return $this->entityManager->getRouteProviders($entity_type); + } + + /** + * {@inheritdoc} + */ + public function hasHandler($entity_type, $handler_type) { + return $this->entityManager->hasHandler($entity_type, $handler_type); + } + + /** + * {@inheritdoc} + */ + public function getHandler($entity_type, $handler_type) { + return $this->entityManager->getHandler($entity_type, $handler_type); + } + + /** + * {@inheritdoc} + */ + public function createHandlerInstance( + $class, + EntityTypeInterface $definition = NULL + ) { + return $this->entityManager->createHandlerInstance($class, $definition); + } + + /** + * {@inheritdoc} + */ + public function getDefinition($entity_type_id, $exception_on_invalid = TRUE) { + return $this->entityManager->getDefinition( + $entity_type_id, + $exception_on_invalid + ); + } + + /** + * {@inheritdoc} + */ + public function getDefinitions() { + return $this->entityManager->getDefinitions(); + } + + /** + * {@inheritdoc} + */ + public function createInstance($plugin_id, array $configuration = []) { + return $this->entityManager->createInstance($plugin_id, $configuration); + } + + /** + * {@inheritdoc} + */ + public function getInstance(array $options) { + return $this->entityManager->getInstance($options); + } + + /** + * {@inheritdoc} + */ + public function setContainer(ContainerInterface $container = NULL) { + $this->entityManager->setContainer($container); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityViewBuilderDecorator.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityViewBuilderDecorator.php new file mode 100644 index 000000000..0b201ac0d --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/EntityViewBuilderDecorator.php @@ -0,0 +1,92 @@ +entities = []; + } + + /** + * {@inheritdoc} + */ + public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) { + $this->getOriginalObject() + ->buildComponents($build, $entities, $displays, $view_mode, $langcode); + } + + /** + * {@inheritdoc} + */ + public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { + $this->entities[] = $entity; + + return $this->getOriginalObject()->view($entity, $view_mode, $langcode); + } + + /** + * {@inheritdoc} + */ + public function viewMultiple(array $entities = [], $view_mode = 'full', $langcode = NULL) { + $this->entities = array_merge($this->entities, $entities); + + return $this->getOriginalObject() + ->viewMultiple($entities, $view_mode, $langcode); + } + + /** + * {@inheritdoc} + */ + public function resetCache(array $entities = NULL) { + $this->getOriginalObject()->resetCache($entities); + } + + /** + * {@inheritdoc} + */ + public function viewField(FieldItemListInterface $items, $display_options = []) { + return $this->getOriginalObject()->viewField($items, $display_options); + } + + /** + * {@inheritdoc} + */ + public function viewFieldItem(FieldItemInterface $item, $display_options = []) { + return $this->getOriginalObject()->viewFieldItem($item, $display_options); + } + + /** + * {@inheritdoc} + */ + public function getCacheTags() { + return $this->getOriginalObject()->getCacheTag(); + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $entity_type, + $container->get('entity.manager'), + $container->get('language_manager') + ); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/EventSubscriber/WebprofilerEventSubscriber.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/EventSubscriber/WebprofilerEventSubscriber.php new file mode 100644 index 000000000..46a42a17c --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/EventSubscriber/WebprofilerEventSubscriber.php @@ -0,0 +1,97 @@ +currentUser = $currentUser; + $this->urlGenerator = $urlGenerator; + $this->renderer = $renderer; + } + + /** + * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event + */ + public function onKernelResponse(FilterResponseEvent $event) { + $response = $event->getResponse(); + $request = $event->getRequest(); + + if ($response->headers->has('X-Debug-Token') && NULL !== $this->urlGenerator) { + $response->headers->set( + 'X-Debug-Token-Link', + $this->urlGenerator->generate('webprofiler.dashboard', ['profile' => $response->headers->get('X-Debug-Token')]) + ); + } + + // do not capture redirects or modify XML HTTP Requests + if ($request->isXmlHttpRequest()) { + return; + } + + if ($this->currentUser->hasPermission('view webprofiler toolbar')) { + $this->injectToolbar($response); + } + } + + /** + * @param \Symfony\Component\HttpFoundation\Response $response + */ + protected function injectToolbar(Response $response) { + $content = $response->getContent(); + $pos = mb_strripos($content, ''); + + if (FALSE !== $pos) { + if ($token = $response->headers->get('X-Debug-Token')) { + $loader = [ + '#theme' => 'webprofiler_loader', + '#token' => $token, + '#profiler_url' => $this->urlGenerator->generate('webprofiler.toolbar', ['profile' => $token]), + ]; + + $content = mb_substr($content, 0, $pos) . $this->renderer->renderRoot($loader) . mb_substr($content, $pos); + $response->setContent($content); + } + } + } + + /** + * @return array + */ + public static function getSubscribedEvents() { + return [ + KernelEvents::RESPONSE => ['onKernelResponse', -128], + ]; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ConfigForm.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ConfigForm.php new file mode 100644 index 000000000..ae7c60126 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ConfigForm.php @@ -0,0 +1,237 @@ +get('config.factory'), + $container->get('profiler'), + $container->get('profiler.storage_manager'), + $container->getParameter('data_collector.templates') + ); + } + + /** + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * @param Profiler $profiler + * @param \Drupal\webprofiler\Profiler\ProfilerStorageManager $storageManager + * @param array $templates + */ + public function __construct(ConfigFactoryInterface $config_factory, Profiler $profiler, ProfilerStorageManager $storageManager, $templates) { + parent::__construct($config_factory); + + $this->profiler = $profiler; + $this->templates = $templates; + $this->storageManager = $storageManager; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'webprofiler_config'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $config = $this->config('webprofiler.config'); + + $form['purge_on_cache_clear'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Purge on cache clear'), + '#description' => $this->t('Deletes all profiler files during cache clear.'), + '#default_value' => $config->get('purge_on_cache_clear'), + ]; + + $storages = $this->storageManager->getStorages(); + + $form['storage'] = [ + '#type' => 'select', + '#title' => $this->t('Storage backend'), + '#description' => $this->t('Choose were to store profiler data.'), + '#options' => $storages, + '#default_value' => $config->get('storage'), + ]; + + $form['exclude'] = [ + '#type' => 'textarea', + '#title' => $this->t('Exclude'), + '#default_value' => $config->get('exclude'), + '#description' => $this->t('Paths to exclude for profiling. One path per line.'), + ]; + + $form['active_toolbar_items'] = [ + '#type' => 'checkboxes', + '#title' => $this->t('Active toolbar items'), + '#options' => $this->getCollectors(), + '#description' => $this->t('Choose which items to show into the toolbar.'), + '#default_value' => $config->get('active_toolbar_items'), + ]; + + $form['ide_settings'] = [ + '#type' => 'details', + '#title' => $this->t('IDE settings'), + '#open' => FALSE, + ]; + + $form['ide_settings']['ide_link'] = [ + '#type' => 'textfield', + '#title' => $this->t('IDE link'), + '#description' => $this->t('IDE link for open files.'), + '#default_value' => $config->get('ide_link'), + ]; + + $form['ide_settings']['ide_link_remote'] = [ + '#type' => 'textfield', + '#title' => $this->t('IDE link remote path'), + '#description' => $this->t('The path of the remote docroot. Leave blank if the docroot is on the same machine of the IDE. No trailing slash.'), + '#default_value' => $config->get('ide_link_remote'), + ]; + + $form['ide_settings']['ide_link_local'] = [ + '#type' => 'textfield', + '#title' => $this->t('IDE link local path'), + '#description' => $this->t('The path of the local docroot. Leave blank if the docroot is on the same machine of IDE. No trailing slash.'), + '#default_value' => $config->get('ide_link_local'), + ]; + + $form['database'] = [ + '#type' => 'details', + '#title' => $this->t('Database settings'), + '#open' => FALSE, + '#states' => array( + 'visible' => array( + array( + ':input[name="active_toolbar_items[database]' => array('checked' => TRUE), + ), + ), + ), + ]; + + $form['database']['query_sort'] = [ + '#type' => 'radios', + '#title' => $this->t('Sort query log'), + '#options' => ['source' => 'by source', 'duration' => 'by duration'], + '#description' => $this->t('The query table can be sorted in the order that the queries were executed or by descending duration.'), + '#default_value' => $config->get('query_sort'), + ]; + + $form['database']['query_highlight'] = [ + '#type' => 'number', + '#title' => $this->t('Slow query highlighting'), + '#description' => $this->t('Enter an integer in milliseconds. Any query which takes longer than this many milliseconds will be highlighted in the query log. This indicates a possibly inefficient query, or a candidate for caching.'), + '#default_value' => $config->get('query_highlight'), + '#min' => 0, + ]; + + $storageId = $this->config('webprofiler.config')->get('storage'); + $storage = $this->storageManager->getStorage($storageId); + + $form['purge'] = [ + '#type' => 'details', + '#title' => $this->t('Purge profiles'), + '#open' => FALSE, + ]; + + $form['purge']['purge'] = [ + '#type' => 'submit', + '#value' => $this->t('Purge'), + '#submit' => [[$this, 'purge']], + ]; + + $form['purge']['purge-help'] = [ + '#type' => 'inline_template', + '#template' => '
      {{ message }}
      ', + '#context' => [ + 'message' => $this->t('Purge %storage profiles.', ['%storage' => $storage['title']]), + ], + ]; + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $this->config('webprofiler.config') + ->set('purge_on_cache_clear', $form_state->getValue('purge_on_cache_clear')) + ->set('storage', $form_state->getValue('storage')) + ->set('exclude', $form_state->getValue('exclude')) + ->set('active_toolbar_items', $form_state->getValue('active_toolbar_items')) + ->set('ide_link', $form_state->getValue('ide_link')) + ->set('ide_link_remote', $form_state->getValue('ide_link_remote')) + ->set('ide_link_local', $form_state->getValue('ide_link_local')) + ->set('query_sort', $form_state->getValue('query_sort')) + ->set('query_highlight', $form_state->getValue('query_highlight')) + ->save(); + + parent::submitForm($form, $form_state); + } + + /** + * Purges profiles. + */ + public function purge(array &$form, FormStateInterface $form_state) { + $this->profiler->purge(); + drupal_set_message($this->t('Profiles purged')); + } + + /** + * @return array + */ + private function getCollectors() { + $options = []; + foreach ($this->templates as $template) { + // drupal collector should not be disabled + if ($template[0] != 'drupal') { + $options[$template[0]] = $template[2]; + } + } + + asort($options); + + return $options; + } + + /** + * {@inheritdoc} + */ + public function getEditableConfigNames() { + return [ + 'webprofiler.config', + ]; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Form/FormBuilderWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Form/FormBuilderWrapper.php new file mode 100644 index 000000000..48064b801 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Form/FormBuilderWrapper.php @@ -0,0 +1,61 @@ +buildForms; + } + + /** + * {@inheritdoc} + */ + public function prepareForm($form_id, &$form, FormStateInterface &$form_state) { + parent::prepareForm($form_id, $form, $form_state); + + if (!$this->buildForms) { + $this->buildForms = []; + } + + $elements = []; + foreach ($form as $key => $value) { + if (strpos($key, '#') !== 0) { + $elements[$key]['#title'] = isset($value['#title']) ? $value['#title'] : NULL; + $elements[$key]['#access'] = isset($value['#access']) ? $value['#access'] : NULL; + $elements[$key]['#type'] = isset($value['#type']) ? $value['#type'] : NULL; + } + } + + $buildInfo = $form_state->getBuildInfo(); + + $class = get_class($buildInfo['callback_object']); + $method = new \ReflectionMethod($class, 'buildForm'); + + $this->buildForms[$buildInfo['form_id']] = [ + 'class' => [ + 'class' => $class, + 'method' => 'buildForm', + 'file' => $method->getFilename(), + 'line' => $method->getStartLine(), + ], + 'form' => $elements, + ]; + + return $form; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ProfilesFilterForm.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ProfilesFilterForm.php new file mode 100644 index 000000000..88d85eed0 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ProfilesFilterForm.php @@ -0,0 +1,86 @@ + 'textfield', + '#title' => $this->t('IP'), + '#size' => 30, + '#default_value' => $this->getRequest()->query->get('ip'), + '#prefix' => '
      ', + ]; + + $form['url'] = [ + '#type' => 'textfield', + '#title' => $this->t('Url'), + '#size' => 30, + '#default_value' => $this->getRequest()->query->get('url'), + ]; + + $form['method'] = [ + '#type' => 'select', + '#title' => $this->t('Method'), + '#options' => ['GET' => 'GET', 'POST' => 'POST'], + '#default_value' => $this->getRequest()->query->get('method'), + ]; + + $limits = [10, 50, 100]; + $form['limit'] = [ + '#type' => 'select', + '#title' => $this->t('Limit'), + '#options' => array_combine($limits, $limits), + '#default_value' => $this->getRequest()->query->get('limit'), + ]; + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['filter'] = [ + '#type' => 'submit', + '#value' => t('Filter'), + '#attributes' => ['class' => ['button--primary']], + '#suffix' => '
      ', + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $ip = $form_state->getValue('ip');// ['values']['ip']; + $url = $form_state->getValue('url'); + $method = $form_state->getValue('method'); + $limit = $form_state->getValue('limit'); + + $url = new Url('webprofiler.admin_list', [], [ + 'query' => [ + 'ip' => $ip, + 'url' => $url, + 'method' => $method, + 'limit' => $limit, + ] + ]); + + $form_state->setRedirectUrl($url); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Frontend/PerformanceTimingData.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Frontend/PerformanceTimingData.php new file mode 100644 index 000000000..da5565cac --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Frontend/PerformanceTimingData.php @@ -0,0 +1,81 @@ +data = $data; + } + + /** + * @return int + */ + public function getDNSTiming() { + if (isset($this->data['domainLookupEnd']) && isset($this->data['domainLookupStart'])) { + return $this->data['domainLookupEnd'] - $this->data['domainLookupStart']; + } + else { + return 0; + } + } + + /** + * @return int + */ + public function getTCPTiming() { + if (isset($this->data['connectEnd']) && isset($this->data['connectStart'])) { + return $this->data['connectEnd'] - $this->data['connectStart']; + } + else { + return 0; + } + } + + /** + * @return int + */ + public function getTtfbTiming() { + if (isset($this->data['responseStart']) && isset($this->data['connectEnd'])) { + return $this->data['responseStart'] - $this->data['connectEnd']; + } + else { + return 0; + } + } + + /** + * @return int + */ + public function getDataTiming() { + if (isset($this->data['responseEnd']) && isset($this->data['responseStart'])) { + return $this->data['responseEnd'] - $this->data['responseStart']; + } + else { + return 0; + } + } + + /** + * @return int + */ + public function getDomTiming() { + if (isset($this->data['loadEventStart']) && isset($this->data['responseEnd'])) { + return $this->data['loadEventStart'] - $this->data['responseEnd']; + } + else { + return 0; + } + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/ClassShortener.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/ClassShortener.php new file mode 100644 index 000000000..204de3281 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/ClassShortener.php @@ -0,0 +1,34 @@ + $part) { + if ($key < $size) { + $result[] = substr($part, 0, 1); + } + else { + $result[] = $part; + } + } + + return new FormattableMarkup("@short", [ + '@class' => $class, + '@short' => implode('\\', $result) + ]); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/ClassShortenerInterface.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/ClassShortenerInterface.php new file mode 100644 index 000000000..e1b2af504 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/ClassShortenerInterface.php @@ -0,0 +1,17 @@ +config_factory = $config_factory; + } + + /** + * {@inheritdoc} + */ + public function generateLink($file, $line) { + $ide_link = $this->config_factory->get('webprofiler.config') + ->get('ide_link'); + $ide_link_remote = $this->config_factory->get('webprofiler.config') + ->get('ide_link_remote'); + $ide_link_local = $this->config_factory->get('webprofiler.config') + ->get('ide_link_local'); + + $file = str_replace($ide_link_remote, $ide_link_local, $file); + + return new FormattableMarkup($ide_link, ['@file' => $file, '@line' => $line]); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/IdeLinkGeneratorInterface.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/IdeLinkGeneratorInterface.php new file mode 100644 index 000000000..062620847 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Helper/IdeLinkGeneratorInterface.php @@ -0,0 +1,17 @@ +completedRequests = []; + $this->failedRequests = []; + } + + /** + * {@inheritdoc} + */ + public function __invoke() { + return function ($handler) { + return function (RequestInterface $request, array $options) use ($handler) { + + $options['on_stats'] = function (TransferStats $stats) use ($request) { + $request->stats = $stats; + }; + + return $handler($request, $options)->then( + function ($response) use ($request) { + + $this->completedRequests[] = [ + 'request' => $request, + 'response' => $response, + ]; + + return $response; + }, + function ($reason) use ($request) { + $response = $reason instanceof RequestException + ? $reason->getResponse() + : null; + + $this->failedRequests[] = [ + 'request' => $request, + 'response' => $response, + 'message' => $reason->getMessage(), + ]; + + return \GuzzleHttp\Promise\rejection_for($reason); + } + ); + }; + }; + } + + /** + * @return array + */ + public function getCompletedRequests() { + return $this->completedRequests; + } + + /** + * @return array + */ + public function getFailedRequests() { + return $this->failedRequests; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Mail/MailManagerWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Mail/MailManagerWrapper.php new file mode 100644 index 000000000..58ecddcea --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Mail/MailManagerWrapper.php @@ -0,0 +1,94 @@ +alterInfo('mail_backend_info'); + $this->setCacheBackend($cache_backend, 'mail_backend_plugins'); + $this->configFactory = $config_factory; + $this->loggerFactory = $logger_factory; + $this->stringTranslation = $string_translation; + $this->dataCollector = $dataCollector; + $this->mailManager = $mailManager; + } + + /** + * {@inheritdoc} + */ + public function mail($module, $key, $to, $langcode, $params = array(), $reply = NULL, $send = TRUE) { + $message = $this->mailManager->mail($module, $key, $to, $langcode, $params, $reply, $send); + + $instance = $this->mailManager->getInstance(['module' => $module, 'key' => $key]); + $this->dataCollector->addMessage($message, $instance); + + return $message; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/DatabaseProfilerStorage.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/DatabaseProfilerStorage.php new file mode 100644 index 000000000..d86045da8 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/DatabaseProfilerStorage.php @@ -0,0 +1,158 @@ +database = $database; + } + + /** + * {@inheritdoc} + */ + public function find($ip, $url, $limit, $method, $start = NULL, $end = NULL) { + $select = $this->database->select('webprofiler', 'wp', ['fetch' => \PDO::FETCH_ASSOC]); + + if (NULL === $start) { + $start = 0; + } + + if (NULL === $end) { + $end = time(); + } + + if ($ip = preg_replace('/[^\d\.]/', '', $ip)) { + $select->condition('ip', '%' . $this->database->escapeLike($ip) . '%', 'LIKE'); + } + + if ($url) { + $select->condition('url', '%' . $this->database->escapeLike(addcslashes($url, '%_\\')) . '%', 'LIKE'); + } + + if ($method) { + $select->condition('method', $method); + } + + if (!empty($start)) { + $select->condition('time', $start, '>='); + } + + if (!empty($end)) { + $select->condition('time', $end, '<='); + } + + $select->fields('wp', [ + 'token', + 'ip', + 'method', + 'url', + 'time', + 'parent', + 'status_code' + ]); + $select->orderBy('time', 'DESC'); + $select->range(0, $limit); + return $select->execute() + ->fetchAllAssoc('token'); + } + + /** + * {@inheritdoc} + */ + public function read($token) { + $record = $this->database->select('webprofiler', 'w') + ->fields('w') + ->condition('token', $token) + ->execute() + ->fetch(); + if (isset($record->data)) { + return $this->createProfileFromData($token, $record); + } + } + + /** + * {@inheritdoc} + */ + public function write(Profile $profile) { + $args = [ + 'token' => $profile->getToken(), + 'parent' => $profile->getParentToken(), + 'data' => base64_encode(serialize($profile->getCollectors())), + 'ip' => $profile->getIp(), + 'method' => $profile->getMethod(), + 'url' => $profile->getUrl(), + 'time' => $profile->getTime(), + 'created_at' => time(), + 'status_code' => $profile->getStatusCode(), + ]; + + try { + $query = $this->database->select('webprofiler', 'w') + ->fields('w', ['token']); + $query->condition('token', $profile->getToken()); + $count = $query->countQuery()->execute()->fetchAssoc(); + + if ($count['expression']) { + $this->database->update('webprofiler') + ->fields($args) + ->condition('token', $profile->getToken()) + ->execute(); + } + else { + $this->database->insert('webprofiler')->fields($args)->execute(); + } + + $status = TRUE; + } catch (\Exception $e) { + $status = FALSE; + } + + return $status; + } + + /** + * {@inheritdoc} + */ + public function purge() { + $this->database->truncate('webprofiler')->execute(); + } + + /** + * @param string $token + * @param $data + * + * @return Profile + */ + private function createProfileFromData($token, $data) { + $profile = new Profile($token); + $profile->setIp($data->ip); + $profile->setMethod($data->method); + $profile->setUrl($data->url); + $profile->setTime($data->time); + $profile->setCollectors(unserialize(base64_decode($data->data))); + $profile->setStatusCode($data->status_code); + + return $profile; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/FileProfilerStorage.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/FileProfilerStorage.php new file mode 100644 index 000000000..7eb63aca9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/FileProfilerStorage.php @@ -0,0 +1,24 @@ +setIp($data['ip']); + $profile->setMethod($data['method']); + $profile->setUrl($data['url']); + $profile->setTime($data['time']); + $profile->setCollectors($data['data']); + $profile->setStatusCode($data['status_code']); + + return $profile; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/Profiler.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/Profiler.php new file mode 100644 index 000000000..438dcb4e0 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/Profiler.php @@ -0,0 +1,77 @@ +localStorage = $storage; + $this->localLogger = $logger; + + $this->config = $config; + $this->activeToolbarItems = $this->config->get('webprofiler.config') + ->get('active_toolbar_items'); + } + + /** + * {@inheritdoc} + */ + public function add(DataCollectorInterface $collector) { + // drupal collector should not be disabled + if ($collector->getName() == 'drupal') { + parent::add($collector); + } + else { + if ($this->activeToolbarItems && array_key_exists($collector->getName(), $this->activeToolbarItems) && $this->activeToolbarItems[$collector->getName()] !== '0') { + parent::add($collector); + } + } + } + + /** + * @param \Symfony\Component\HttpKernel\Profiler\Profile $profile + * + * @return bool + */ + public function updateProfile(Profile $profile) { + if (!($ret = $this->localStorage->write($profile)) && NULL !== $this->localLogger) { + $this->localLogger->warning('Unable to store the profiler information.'); + } + + return $ret; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/ProfilerStorageFactory.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/ProfilerStorageFactory.php new file mode 100644 index 000000000..5ba154528 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/ProfilerStorageFactory.php @@ -0,0 +1,26 @@ +get('webprofiler.config') + ->get('storage') ?: 'profiler.database_storage'; + + return $container->get($storage); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/ProfilerStorageManager.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/ProfilerStorageManager.php new file mode 100644 index 000000000..fcd3683dc --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/ProfilerStorageManager.php @@ -0,0 +1,52 @@ +storages as $id => $storage) { + $output[$id] = $storage['title']; + } + + return $output; + } + + /** + * @param $id + * + * @return array + */ + public function getStorage($id) { + return $this->storages[$id]; + } + + /** + * @param $id + * @param $title + * @param \Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface $storage + */ + public function addStorage($id, $title, ProfilerStorageInterface $storage) { + $this->storages[$id] = [ + 'title' => $title, + 'class' => $storage, + ]; + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/TemplateManager.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/TemplateManager.php new file mode 100644 index 000000000..87aa68539 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Profiler/TemplateManager.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Drupal\webprofiler\Profiler; + +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Profiler\Profiler as SymfonyProfiler; +use Symfony\Component\HttpKernel\Profiler\Profile; + +/** + * Profiler Templates Manager + */ +class TemplateManager { + + /** + * @var \Twig_Environment + */ + protected $twig; + + /** + * @var \Twig_Loader_Chain + */ + protected $twigLoader; + + /** + * @var array + */ + protected $templates; + + /** + * @var \Symfony\Component\HttpKernel\Profiler\Profiler + */ + protected $profiler; + + /** + * Constructor. + * + * @param SymfonyProfiler $profiler + * @param \Twig_Environment $twig + * @param \Twig_Loader_Chain $twigLoader + * @param array $templates + */ + public function __construct(SymfonyProfiler $profiler, \Twig_Environment $twig, \Twig_Loader_Chain $twigLoader, array $templates) { + $this->profiler = $profiler; + $this->twig = $twig; + $this->twigLoader = $twigLoader; + $this->templates = $templates; + } + + /** + * Gets the template name for a given panel. + * + * @param Profile $profile + * @param string $panel + * + * @return mixed + * + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public function getName(Profile $profile, $panel) { + $templates = $this->getNames($profile); + + if (!isset($templates[$panel])) { + throw new NotFoundHttpException(sprintf('Panel "%s" is not registered in profiler or is not present in viewed profile.', $panel)); + } + + return $templates[$panel]; + } + + /** + * Gets the templates for a given profile. + * + * @param \Symfony\Component\HttpKernel\Profiler\Profile $profile + * + * @return array + */ + public function getTemplates(Profile $profile) { + $templates = $this->getNames($profile); + foreach ($templates as $name => $template) { + $templates[$name] = $this->twig->loadTemplate($template); + } + + return $templates; + } + + /** + * Gets template names of templates that are present in the viewed profile. + * + * @param \Symfony\Component\HttpKernel\Profiler\Profile $profile + * + * @return array + * + * @throws \UnexpectedValueException + */ + protected function getNames(Profile $profile) { + $templates = []; + + foreach ($this->templates as $arguments) { + if (NULL === $arguments) { + continue; + } + + list($name, $template) = $arguments; + + if (!$this->profiler->has($name) || !$profile->hasCollector($name)) { + continue; + } + + if ('.html.twig' === substr($template, -10)) { + $template = substr($template, 0, -10); + } + + if (!$this->twigLoader->exists($template . '.html.twig')) { + throw new \UnexpectedValueException(sprintf('The profiler template "%s.html.twig" for data collector "%s" does not exist.', $template, $name)); + } + + $templates[$name] = $template . '.html.twig'; + } + + return $templates; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/RequestMatcher/WebprofilerRequestMatcher.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/RequestMatcher/WebprofilerRequestMatcher.php new file mode 100644 index 000000000..17087cc84 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/RequestMatcher/WebprofilerRequestMatcher.php @@ -0,0 +1,50 @@ +configFactory = $configFactory; + $this->pathMatcher = $pathMatcher; + } + + /** + * {@inheritdoc} + */ + public function matches(Request $request) { + $path = $request->getPathInfo(); + + $patterns = $this->configFactory->get('webprofiler.config')->get('exclude'); + + // never add Webprofiler to phpinfo page. + $patterns .= "\r\n/admin/reports/status/php"; + + // never add Webprofiler to uninstall confirm page. + $patterns .= "\r\n/admin/modules/uninstall/*"; + + return !$this->pathMatcher->matchPath($path, $patterns); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Routing/TokenConverter.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Routing/TokenConverter.php new file mode 100644 index 000000000..ea168cd18 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Routing/TokenConverter.php @@ -0,0 +1,54 @@ +profiler = $profiler; + } + + /** + * {@inheritdoc} + */ + public function convert($value, $definition, $name, array $defaults) { + if (NULL === $this->profiler) { + return NULL; + } + + $profile = $this->profiler->loadProfile($value); + + if (NULL === $profile) { + return NULL; + } + + return $profile; + } + + /** + * {@inheritdoc} + */ + public function applies($definition, $name, Route $route) { + if (!empty($definition['type']) && $definition['type'] === 'webprofiler:token') { + return TRUE; + } + return FALSE; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/StackMiddleware/WebprofilerMiddleware.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/StackMiddleware/WebprofilerMiddleware.php new file mode 100644 index 000000000..dac9198e4 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/StackMiddleware/WebprofilerMiddleware.php @@ -0,0 +1,41 @@ +httpKernel = $http_kernel; + } + + /** + * {@inheritdoc} + */ + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) { + foreach (Database::getAllConnectionInfo() as $key => $info) { + Database::startLog('webprofiler', $key); + } + return $this->httpKernel->handle($request, $type, $catch); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/State/StateWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/State/StateWrapper.php new file mode 100644 index 000000000..89daa7c66 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/State/StateWrapper.php @@ -0,0 +1,86 @@ +state = $state; + $this->dataCollector = $dataCollector; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = NULL) { + $this->dataCollector->addState($key); + + return $this->state->get($key, $default); + } + + /** + * {@inheritdoc} + */ + public function getMultiple(array $keys) { + foreach ($keys as $key) { + $this->dataCollector->addState($key); + } + + return $this->state->getMultiple($keys); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) { + $this->state->set($key, $value); + } + + /** + * {@inheritdoc} + */ + public function setMultiple(array $data) { + $this->state->setMultiple($data); + } + + /** + * {@inheritdoc} + */ + public function delete($key) { + $this->state->delete($key); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple(array $keys) { + $this->state->deleteMultiple($keys); + } + + /** + * {@inheritdoc} + */ + public function resetCache() { + $this->state->resetCache(); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Stopwatch.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Stopwatch.php new file mode 100644 index 000000000..35714bffa --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Stopwatch.php @@ -0,0 +1,543 @@ +sections = $this->activeSections = ['__root__' => new Section('__root__')]; + } + + /** + * Creates a new section or re-opens an existing section. + * + * @param string|null $id The id of the session to re-open, null to create a new one + * + * @throws \LogicException When the section to re-open is not reachable + */ + public function openSection($id = NULL) { + $current = end($this->activeSections); + + if (NULL !== $id && NULL === $current->get($id)) { + throw new \LogicException(sprintf('The section "%s" has been started at an other level and can not be opened.', $id)); + } + + $this->start('__section__.child', 'section'); + $this->activeSections[] = $current->open($id); + $this->start('__section__'); + } + + /** + * Stops the last started section. + * + * The id parameter is used to retrieve the events from this section. + * + * @see getSectionEvents + * + * @param string $id The identifier of the section + * + * @throws \LogicException When there's no started section to be stopped + */ + public function stopSection($id) { + $this->stop('__section__'); + + if (1 == count($this->activeSections)) { + throw new \LogicException('There is no started section to stop.'); + } + + $this->sections[$id] = array_pop($this->activeSections)->setId($id); + $this->stop('__section__.child'); + } + + /** + * Starts an event. + * + * @param string $name The event name + * @param string $category The event category + * + * @return StopwatchEvent A StopwatchEvent instance + */ + public function start($name, $category = NULL) { + return end($this->activeSections)->startEvent($name, $category); + } + + /** + * Checks if the event was started + * + * @param string $name The event name + * + * @return bool + */ + public function isStarted($name) { + return end($this->activeSections)->isEventStarted($name); + } + + /** + * Stops an event. + * + * @param string $name The event name + * + * @return StopwatchEvent A StopwatchEvent instance + */ + public function stop($name) { + return end($this->activeSections)->stopEvent($name); + } + + /** + * Stops then restarts an event. + * + * @param string $name The event name + * + * @return StopwatchEvent A StopwatchEvent instance + */ + public function lap($name) { + return end($this->activeSections)->stopEvent($name)->start(); + } + + /** + * Gets all events for a given section. + * + * @param string $id A section identifier + * + * @return StopwatchEvent[] An array of StopwatchEvent instances + */ + public function getSectionEvents($id) { + return isset($this->sections[$id]) ? $this->sections[$id]->getEvents() : []; + } + } + + + /** + * @internal This class is for internal usage only + * + * @author Fabien Potencier + */ + class Section { + /** + * @var StopwatchEvent[] + */ + private $events = []; + + /** + * @var null|float + */ + private $origin; + + /** + * @var string + */ + private $id; + + /** + * @var Section[] + */ + private $children = []; + + /** + * Constructor. + * + * @param float|null $origin Set the origin of the events in this section, use null to set their origin to their start time + */ + public function __construct($origin = NULL) { + $this->origin = is_numeric($origin) ? $origin : NULL; + } + + /** + * Returns the child section. + * + * @param string $id The child section identifier + * + * @return Section|null The child section or null when none found + */ + public function get($id) { + foreach ($this->children as $child) { + if ($id === $child->getId()) { + return $child; + } + } + + return NULL; + } + + /** + * Creates or re-opens a child section. + * + * @param string|null $id null to create a new section, the identifier to re-open an existing one. + * + * @return Section A child section + */ + public function open($id) { + if (NULL === $session = $this->get($id)) { + $session = $this->children[] = new self(microtime(TRUE) * 1000); + } + + return $session; + } + + /** + * @return string The identifier of the section + */ + public function getId() { + return $this->id; + } + + /** + * Sets the session identifier. + * + * @param string $id The session identifier + * + * @return Section The current section + */ + public function setId($id) { + $this->id = $id; + + return $this; + } + + /** + * Starts an event. + * + * @param string $name The event name + * @param string $category The event category + * + * @return StopwatchEvent The event + */ + public function startEvent($name, $category) { + if (!isset($this->events[$name])) { + $this->events[$name] = new StopwatchEvent($this->origin ?: microtime(TRUE) * 1000, $category); + } + + return $this->events[$name]->start(); + } + + /** + * Checks if the event was started + * + * @param string $name The event name + * + * @return bool + */ + public function isEventStarted($name) { + return isset($this->events[$name]) && $this->events[$name]->isStarted(); + } + + /** + * Stops an event. + * + * @param string $name The event name + * + * @return StopwatchEvent The event + * + * @throws \LogicException When the event has not been started + */ + public function stopEvent($name) { + if (!isset($this->events[$name])) { + throw new \LogicException(sprintf('Event "%s" is not started.', $name)); + } + + return $this->events[$name]->stop(); + } + + /** + * Stops then restarts an event. + * + * @param string $name The event name + * + * @return StopwatchEvent The event + * + * @throws \LogicException When the event has not been started + */ + public function lap($name) { + return $this->stopEvent($name)->start(); + } + + /** + * Returns the events from this section. + * + * @return StopwatchEvent[] An array of StopwatchEvent instances + */ + public function getEvents() { + return $this->events; + } + } + + /** + * Class StopwatchEvent + */ + class StopwatchEvent { + /** + * @var StopwatchPeriod[] + */ + private $periods = []; + + /** + * @var float + */ + private $origin; + + /** + * @var string + */ + private $category; + + /** + * @var float[] + */ + private $started = []; + + /** + * Constructor. + * + * @param float $origin The origin time in milliseconds + * @param string|null $category The event category or null to use the default + * + * @throws \InvalidArgumentException When the raw time is not valid + */ + public function __construct($origin, $category = NULL) { + $this->origin = $this->formatTime($origin); + $this->category = is_string($category) ? $category : 'default'; + } + + /** + * Gets the category. + * + * @return string The category + */ + public function getCategory() { + return $this->category; + } + + /** + * Gets the origin. + * + * @return float The origin in milliseconds + */ + public function getOrigin() { + return $this->origin; + } + + /** + * Starts a new event period. + * + * @return StopwatchEvent The event + */ + public function start() { + $this->started[] = $this->getNow(); + + return $this; + } + + /** + * Stops the last started event period. + * + * @throws \LogicException When start wasn't called before stopping + * + * @return StopwatchEvent The event + * + * @throws \LogicException When stop() is called without a matching call to start() + */ + public function stop() { + if (!count($this->started)) { + throw new \LogicException('stop() called but start() has not been called before.'); + } + + $this->periods[] = new StopwatchPeriod(array_pop($this->started), $this->getNow()); + + return $this; + } + + /** + * Checks if the event was started + * + * @return bool + */ + public function isStarted() { + return !empty($this->started); + } + + /** + * Stops the current period and then starts a new one. + * + * @return StopwatchEvent The event + */ + public function lap() { + return $this->stop()->start(); + } + + /** + * Stops all non already stopped periods. + */ + public function ensureStopped() { + while (count($this->started)) { + $this->stop(); + } + } + + /** + * Gets all event periods. + * + * @return StopwatchPeriod[] An array of StopwatchPeriod instances + */ + public function getPeriods() { + return $this->periods; + } + + /** + * Gets the relative time of the start of the first period. + * + * @return integer The time (in milliseconds) + */ + public function getStartTime() { + return isset($this->periods[0]) ? $this->periods[0]->getStartTime() : 0; + } + + /** + * Gets the relative time of the end of the last period. + * + * @return integer The time (in milliseconds) + */ + public function getEndTime() { + return ($count = count($this->periods)) ? $this->periods[$count - 1]->getEndTime() : 0; + } + + /** + * Gets the duration of the events (including all periods). + * + * @return integer The duration (in milliseconds) + */ + public function getDuration() { + $total = 0; + foreach ($this->periods as $period) { + $total += $period->getDuration(); + } + + return $total; + } + + /** + * Gets the max memory usage of all periods. + * + * @return integer The memory usage (in bytes) + */ + public function getMemory() { + $memory = 0; + foreach ($this->periods as $period) { + if ($period->getMemory() > $memory) { + $memory = $period->getMemory(); + } + } + + return $memory; + } + + /** + * Return the current time relative to origin. + * + * @return float Time in ms + */ + protected function getNow() { + return $this->formatTime(microtime(TRUE) * 1000 - $this->origin); + } + + /** + * Formats a time. + * + * @param integer|float $time A raw time + * + * @return float The formatted time + * + * @throws \InvalidArgumentException When the raw time is not valid + */ + private function formatTime($time) { + if (!is_numeric($time)) { + throw new \InvalidArgumentException('The time must be a numerical value'); + } + + return round($time, 1); + } + } + + /** + * Class StopwatchPeriod + */ + class StopwatchPeriod { + private $start; + private $end; + private $memory; + + /** + * Constructor. + * + * @param integer $start The relative time of the start of the period (in milliseconds) + * @param integer $end The relative time of the end of the period (in milliseconds) + */ + public function __construct($start, $end) { + $this->start = (integer) $start; + $this->end = (integer) $end; + $this->memory = memory_get_usage(TRUE); + } + + /** + * Gets the relative time of the start of the period. + * + * @return integer The time (in milliseconds) + */ + public function getStartTime() { + return $this->start; + } + + /** + * Gets the relative time of the end of the period. + * + * @return integer The time (in milliseconds) + */ + public function getEndTime() { + return $this->end; + } + + /** + * Gets the time spent in this period. + * + * @return integer The period duration (in milliseconds) + */ + public function getDuration() { + return $this->end - $this->start; + } + + /** + * Gets the memory usage. + * + * @return integer The memory usage (in bytes) + */ + public function getMemory() { + return $this->memory; + } + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/StringTranslation/TranslationManagerWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/StringTranslation/TranslationManagerWrapper.php new file mode 100644 index 000000000..7967646ae --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/StringTranslation/TranslationManagerWrapper.php @@ -0,0 +1,69 @@ +translationManager = $translationManager; + } + + /** + * {@inheritdoc} + */ + protected function doTranslate($string, array $options = array()) { + // Merge in defaults. + if (empty($options['langcode'])) { + $options['langcode'] = $this->defaultLangcode; + } + if (empty($options['context'])) { + $options['context'] = ''; + } + $translation = $this->getStringTranslation($options['langcode'], $string, $options['context']); + + if($translation) { + $this->translated[$string] = $translation; + } else { + $this->untranslated[$string] = $string; + } + + return $translation === FALSE ? $string : $translation; + } + + /** + * @return array + */ + public function getTranslated() { + return $this->translated; + } + + /** + * @return array + */ + public function getUntranslated() { + return $this->untranslated; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Theme/ThemeNegotiatorWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Theme/ThemeNegotiatorWrapper.php new file mode 100644 index 000000000..7b427f180 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Theme/ThemeNegotiatorWrapper.php @@ -0,0 +1,39 @@ +getSortedNegotiators() as $negotiator) { + if ($negotiator->applies($route_match)) { + $theme = $negotiator->determineActiveTheme($route_match); + if ($theme !== NULL && $this->themeAccess->checkAccess($theme)) { + $this->negotiator = $negotiator; + return $theme; + } + } + } + } + + /** + * @return \Drupal\Core\Theme\ThemeNegotiatorInterface + */ + public function getNegotiator() { + return $this->negotiator; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/TraceableEventDispatcher.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/TraceableEventDispatcher.php new file mode 100644 index 000000000..44c5cf3df --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/TraceableEventDispatcher.php @@ -0,0 +1,70 @@ +stopwatch->isStarted('controller')) { + $this->stopwatch->stop('controller'); + } + break; + } + } + + /** + * {@inheritdoc} + */ + protected function postDispatch($eventName, Event $event) { + switch ($eventName) { + case KernelEvents::CONTROLLER: + $this->stopwatch->start('controller', 'section'); + break; + case KernelEvents::RESPONSE: + $token = $event->getResponse()->headers->get('X-Debug-Token'); + try { + $this->stopwatch->stopSection($token); + } catch (\LogicException $e) { + } + break; + case KernelEvents::TERMINATE: + // In the special case described in the `preDispatch` method above, the `$token` section + // does not exist, then closing it throws an exception which must be caught. + $token = $event->getResponse()->headers->get('X-Debug-Token'); + try { + $this->stopwatch->stopSection($token); + } catch (\LogicException $e) { + } + break; + } + } + + /** + * {@inheritdoc} + */ + public function getListenerPriority($eventName, $listener) { + if (!isset($this->listeners[$eventName])) { + return; + } + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (FALSE !== ($key = array_search($listener, $listeners, TRUE))) { + return $priority; + } + } + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Twig/Dumper/HtmlDumper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Twig/Dumper/HtmlDumper.php new file mode 100644 index 000000000..7b3ac02b0 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Twig/Dumper/HtmlDumper.php @@ -0,0 +1,35 @@ + '#dfd', + 'macro' => '#ddf', + 'template' => '#ffd', + 'big' => '#d44', + ); + + /** + * {@inheritdoc} + */ + public function dump(\Twig_Profiler_Profile $profile) { + return '
      ' . parent::dump($profile) . '
      '; + } + + protected function formatTemplate(\Twig_Profiler_Profile $profile, $prefix) { + return sprintf('%s└ %s', $prefix, self::$colors['template'], $profile->getTemplate()); + } + + protected function formatNonTemplate(\Twig_Profiler_Profile $profile, $prefix) { + return sprintf('%s└ %s::%s(%s)', $prefix, $profile->getTemplate(), $profile->getType(), isset(self::$colors[$profile->getType()]) ? self::$colors[$profile->getType()] : 'auto', $profile->getName()); + } + + protected function formatTime(\Twig_Profiler_Profile $profile, $percent) { + return sprintf('%.2fms/%.0f%%', $percent > 20 ? self::$colors['big'] : 'auto', $profile->getDuration() * 1000, $percent); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Twig/Extension/ProfilerExtension.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Twig/Extension/ProfilerExtension.php new file mode 100644 index 000000000..ed598e4db --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Twig/Extension/ProfilerExtension.php @@ -0,0 +1,93 @@ +ideLink = $ideLink; + $this->classShortener = $classShortener; + + $this->stopwatch = $stopwatch; + $this->events = new \SplObjectStorage(); + } + + /** + * {@inheritdoc} + */ + public function enter(\Twig_Profiler_Profile $profile) { + if ($this->stopwatch && $profile->isTemplate()) { + $this->events[$profile] = $this->stopwatch->start($profile->getName(), 'template'); + } + + parent::enter($profile); + } + + /** + * {@inheritdoc} + */ + public function leave(\Twig_Profiler_Profile $profile) { + parent::leave($profile); + + if ($this->stopwatch && $profile->isTemplate()) { + $this->events[$profile]->stop(); + unset($this->events[$profile]); + } + } + + /** + * {@inheritdoc} + */ + public function getFunctions() { + return [ + new \Twig_SimpleFunction('abbr', [$this, 'getAbbr']), + new \Twig_SimpleFunction('idelink', [$this, 'getIdeLink']), + ]; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'native_profiler'; + } + + /** + * @param $class + * + * @return string + */ + public function getAbbr($class) { + return $this->classShortener->shortenClass($class); + } + + /** + * @param $file + * @param $line + * + * @return string + */ + public function getIdeLink($file, $line) { + return $this->ideLink->generateLink($file, $line); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Views/TraceableViewExecutable.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Views/TraceableViewExecutable.php new file mode 100644 index 000000000..3f41dc5e0 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Views/TraceableViewExecutable.php @@ -0,0 +1,156 @@ +build_time; + } + + /** + * Gets the execute time. + * + * @return float + */ + public function getExecuteTime() { + return $this->execute_time; + } + + /** + * Gets the render time. + * + * @return float + */ + public function getRenderTime() { + return $this->render_time; + } + + /** + * {@inheritdoc} + */ + public function render($display_id = NULL) { + $start = microtime(TRUE); + + $this->execute($display_id); + + // Check to see if the build failed. + if (!empty($this->build_info['fail'])) { + return; + } + if (!empty($this->build_info['denied'])) { + return; + } + + $exposed_form = $this->display_handler->getPlugin('exposed_form'); + $exposed_form->preRender($this->result); + + $module_handler = \Drupal::moduleHandler(); + + // @TODO In the longrun, it would be great to execute a view without + // the theme system at all. See https://www.drupal.org/node/2322623. + $active_theme = \Drupal::theme()->getActiveTheme(); + $themes = array_keys($active_theme->getBaseThemes()); + $themes[] = $active_theme->getName(); + + // Check for already-cached output. + if (!empty($this->live_preview)) { + $cache = FALSE; + } + else { + /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */ + $cache = $this->display_handler->getPlugin('cache'); + } + + // Run preRender for the pager as it might change the result. + if (!empty($this->pager)) { + $this->pager->preRender($this->result); + } + + // Initialize the style plugin. + $this->initStyle(); + + if (!isset($this->response)) { + // Set the response so other parts can alter it. + $this->response = new Response('', 200); + } + + // Give field handlers the opportunity to perform additional queries + // using the entire resultset prior to rendering. + if ($this->style_plugin->usesFields()) { + foreach ($this->field as $id => $handler) { + if (!empty($this->field[$id])) { + $this->field[$id]->preRender($this->result); + } + } + } + + $this->style_plugin->preRender($this->result); + + // Let each area handler have access to the result set. + $areas = ['header', 'footer']; + // Only call preRender() on the empty handlers if the result is empty. + if (empty($this->result)) { + $areas[] = 'empty'; + } + foreach ($areas as $area) { + foreach ($this->{$area} as $handler) { + $handler->preRender($this->result); + } + } + + // Let modules modify the view just prior to rendering it. + $module_handler->invokeAll('views_pre_render', [$this]); + + // Let the themes play too, because pre render is a very themey thing. + foreach ($themes as $theme_name) { + $function = $theme_name . '_views_pre_render'; + if (function_exists($function)) { + $function($this); + } + } + + $this->display_handler->output = $this->display_handler->render(); + + $exposed_form->postRender($this->display_handler->output); + + if ($cache) { + $cache->postRender($this->display_handler->output); + } + + // Let modules modify the view output after it is rendered. + $module_handler->invokeAll('views_post_render', [ + $this, + &$this->display_handler->output, + $cache + ]); + + // Let the themes play too, because post render is a very themey thing. + foreach ($themes as $theme_name) { + $function = $theme_name . '_views_post_render'; + if (function_exists($function)) { + $function($this); + } + } + + $this->render_time = microtime(TRUE) - $start; + + return $this->display_handler->output; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/Views/ViewExecutableFactoryWrapper.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/Views/ViewExecutableFactoryWrapper.php new file mode 100644 index 000000000..6aa363fad --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/Views/ViewExecutableFactoryWrapper.php @@ -0,0 +1,47 @@ +views = []; + } + + /** + * {@inheritdoc} + */ + public function get(ViewEntityInterface $view) { + $view_executable = new TraceableViewExecutable($view, $this->user, $this->viewsData, $this->routeProvider); + $view_executable->setRequest($this->requestStack->getCurrentRequest()); + $this->views[] = $view_executable; + + return $view_executable; + } + + /** + * @return TraceableViewExecutable + */ + public function getViews() { + return $this->views; + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/src/WebprofilerServiceProvider.php b/sites/all/modules/contrib/dev/devel/webprofiler/src/WebprofilerServiceProvider.php new file mode 100644 index 000000000..20b67f0b5 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/src/WebprofilerServiceProvider.php @@ -0,0 +1,109 @@ +addCompilerPass(new ProfilerPass()); + + // Add a compiler pass to discover all available storage backend. + $container->addCompilerPass(new StoragePass()); + + $container->addCompilerPass(new ServicePass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new EventPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new DecoratorPass(), PassConfig::TYPE_AFTER_REMOVING); + + $modules = $container->getParameter('container.modules'); + + // Add ViewsDataCollector only if Views module is enabled. + if (isset($modules['views'])) { + $container->register('webprofiler.views', 'Drupal\webprofiler\DataCollector\ViewsDataCollector') + ->addArgument(new Reference(('views.executable'))) + ->addArgument(new Reference(('entity.manager'))) + ->addTag('data_collector', [ + 'template' => '@webprofiler/Collector/views.html.twig', + 'id' => 'views', + 'title' => 'Views', + 'priority' => 75, + ]); + } + + // Add BlockDataCollector only if Block module is enabled. + if (isset($modules['block'])) { + $container->register('webprofiler.blocks', 'Drupal\webprofiler\DataCollector\BlocksDataCollector') + ->addArgument(new Reference(('entity_type.manager'))) + ->addTag('data_collector', [ + 'template' => '@webprofiler/Collector/blocks.html.twig', + 'id' => 'blocks', + 'title' => 'Blocks', + 'priority' => 78, + ]); + } + + // Add TranslationsDataCollector only if Locale module is enabled. + if (isset($modules['locale'])) { + $container->register('webprofiler.translations', 'Drupal\webprofiler\DataCollector\TranslationsDataCollector') + ->addArgument(new Reference(('string_translation'))) + ->addArgument(new Reference(('url_generator'))) + ->addTag('data_collector', [ + 'template' => '@webprofiler/Collector/translations.html.twig', + 'id' => 'translations', + 'title' => 'Translations', + 'priority' => 210, + ]); + } + } + + /** + * {@inheritdoc} + */ + public function alter(ContainerBuilder $container) { + $modules = $container->getParameter('container.modules'); + + // Alter the views.executable service only if Views module is enabled. + if (isset($modules['views'])) { + $container->getDefinition('views.executable') + ->setClass('Drupal\webprofiler\Views\ViewExecutableFactoryWrapper'); + } + + // Replace the regular form_builder service with a traceable one. + $container->getDefinition('form_builder') + ->setClass('Drupal\webprofiler\Form\FormBuilderWrapper'); + + // Replace the regular access_manager service with a traceable one. + $container->getDefinition('access_manager') + ->setClass('Drupal\webprofiler\Access\AccessManagerWrapper') + ->addMethodCall('setDataCollector', [new Reference('webprofiler.request')]); + + // Replace the regular theme.negotiator service with a traceable one. + $container->getDefinition('theme.negotiator') + ->setClass('Drupal\webprofiler\Theme\ThemeNegotiatorWrapper'); + + // Replace the regular config.factory service with a traceable one. + $container->getDefinition('config.factory') + ->setClass('Drupal\webprofiler\Config\ConfigFactoryWrapper') + ->addMethodCall('setDataCollector', [new Reference('webprofiler.config')]); + + // Replace the regular string_translation service with a traceable one. + $container->getDefinition('string_translation') + ->setClass('Drupal\webprofiler\StringTranslation\TranslationManagerWrapper'); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/assets.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/assets.html.twig new file mode 100644 index 000000000..d53754140 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/assets.html.twig @@ -0,0 +1,133 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Assets'|t }} + {{ collector.getcsscount + collector.getjscount }} + + {% endset %} + {% set text %} + +
      + CSS + {{ collector.getcsscount }} +
      +
      + JS + {{ collector.getjscount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/blocks.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/blocks.html.twig new file mode 100644 index 000000000..4146f2749 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/blocks.html.twig @@ -0,0 +1,113 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Blocks'|t }} + {{ collector.renderedBlocksCount }} + + {% endset %} + {% set text %} + +
      + {{ 'Loaded'|t }} + {{ collector.loadedBlocksCount }} +
      +
      + {{ 'Rendered'|t }} + {{ collector.renderedBlocksCount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/cache.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/cache.html.twig new file mode 100644 index 000000000..61539b6e3 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/cache.html.twig @@ -0,0 +1,63 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Cache'|t }} + {{ collector.getCacheHitsCount }} + / {{ collector.getCacheMissesCount }} + + {% endset %} + {% set text %} + + {% for bin, cids in collector.getCacheHits %} +
      + {{ bin }} + {{ cids }} +
      + {% endfor %} + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/config.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/config.html.twig new file mode 100644 index 000000000..954ea5f12 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/config.html.twig @@ -0,0 +1,37 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Config'|t }} + {{ collector.getConfigNames|length }} + + {% endset %} + {% set text %} +
      + {{ 'Config get'|t }} + {{ collector.getConfigNames|length }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/database.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/database.html.twig new file mode 100644 index 000000000..cbe155134 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/database.html.twig @@ -0,0 +1,168 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Database'|t }} + {{ collector.querycount }} + {% if collector.querycount > 0 %} + in {{ '%0.2f ms'|format(collector.time) }} + {% endif %} + + {% endset %} + {% set text %} +
      + {{ 'DB Queries'|t }} + {{ collector.querycount }} +
      +
      + {{ 'Query time'|t }} + {{ '%0.2f ms'|format(collector.time) }} +
      +
      + {{ 'Default database'|t }} + {{ collector.database.driver }}://{{ collector.database.host }}:{{ collector.database.port }} + /{{ collector.database.database }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/devel.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/devel.html.twig new file mode 100644 index 000000000..2c2e8c51f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/devel.html.twig @@ -0,0 +1,24 @@ +{% block toolbar %} + {% set icon %} + + {{ collector.title }} + + {% endset %} + + {% set text %} + {% spaceless %} + {% for item in collector.links %} + + {% endfor %} + {% endspaceless %} + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/drupal.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/drupal.html.twig new file mode 100644 index 000000000..1c1dc48bc --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/drupal.html.twig @@ -0,0 +1,66 @@ +{% block toolbar %} + {% set icon %} + + Drupal + + {{ collector.version }} + + + {% endset %} + {% set text %} + {% spaceless %} +
      + {{ 'Drupal version'|t }} + {{ collector.version }} +
      +
      + {{ 'Installed profile'|t }} + {{ collector.profile }} +
      + + {% if collector.getGitCommit %} +
      + {{ 'Git commit'|t }} + {{ collector.getAbbrGitCommit }} +
      + {% endif %} + + + +
      + + + {% endspaceless %} + {% endset %} + + {% if link %} + {% set icon %} + {{ icon }} + {% endset %} + {% endif %} +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} + diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/events.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/events.html.twig new file mode 100644 index 000000000..9167413af --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/events.html.twig @@ -0,0 +1,79 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Events'|t }} + {{ collector.getCalledListenersCount }} + + {% endset %} + + {% set text %} +
      + {{ 'Triggered'|t }} + {{ collector.getCalledListenersCount }} +
      +
      + {{ 'Not triggered'|t }} + {{ collector.getNotCalledListenersCount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/extensions.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/extensions.html.twig new file mode 100644 index 000000000..987a9feeb --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/extensions.html.twig @@ -0,0 +1,95 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Extensions'|t }} + {{ collector.getExtensionsCount }} + + {% endset %} + {% set text %} +
      + {{ 'Active Modules'|t }} + {{ collector.getModulesCount }} +
      +
      + {{ 'Active Themes'|t }} + {{ collector.getThemesCount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/forms.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/forms.html.twig new file mode 100644 index 000000000..a602f4558 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/forms.html.twig @@ -0,0 +1,67 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Forms'|t }} + {{ collector.getFormsCount }} + + {% endset %} + {% set text %} + {% spaceless %} +
      + {% for keys, form in collector.getForms %} + {{ keys }} + + {% endfor %} +
      + {% endspaceless %} + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/http.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/http.html.twig new file mode 100644 index 000000000..ea7ad0496 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/http.html.twig @@ -0,0 +1,77 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Http'|t }} + {{ collector.completedRequestsCount }} + + {% endset %} + {% set text %} +
      + {{ 'Completed'|t }} + {{ collector.completedRequestsCount }} +
      +
      + {{ 'Failed'|t }} + {{ collector.failedRequestsCount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/mail.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/mail.html.twig new file mode 100644 index 000000000..3b34062b6 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/mail.html.twig @@ -0,0 +1,76 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Mail'|t }} + {{ collector.mailsent }} + + {% endset %} + {% set text %} +
      + Mail sent + {{ collector.mailsent }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/performance_timing.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/performance_timing.html.twig new file mode 100644 index 000000000..5e8cf41c9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/performance_timing.html.twig @@ -0,0 +1,76 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Performance Timing'|t }} + + + {% endset %} + {% set text %} + +
      + {{ 'DNS lookup'|t }} + +
      +
      + {{ 'TCP handshake'|t }} + +
      +
      + {{ 'TTFB' }} + +
      +
      + {{ 'Data download'|t }} + +
      +
      + {{ 'DOM building'|t }} + +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      + + +{% endblock %} + +{% block panel %} + +{% endblock %} \ No newline at end of file diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/php_config.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/php_config.html.twig new file mode 100644 index 000000000..c91f9f321 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/php_config.html.twig @@ -0,0 +1,52 @@ +{% block toolbar %} + {# PHP Information #} + {% set icon %} + + {{ 'PHP config'|t }} + + {% endset %} + {% set text %} + {% spaceless %} + +
      + {{ 'PHP Extensions'|t }} + xdebug + accel +
      +
      + {{ 'PHP SAPI'|t }} + {{ collector.sapiName }} +
      +
      + Token + {{ token }} +
      + {% endspaceless %} + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/request.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/request.html.twig new file mode 100644 index 000000000..4a1e1e308 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/request.html.twig @@ -0,0 +1,206 @@ +{% block toolbar %} + {% set request_handler %} + {{ abbr(collector.controller.class)|raw }} + ::{{ collector.controller.method }} + {% endset %} + + {% set request_status_code_color = (400 > collector.statuscode) ? ((200 == collector.statuscode) ? 'green' : 'yellow') : 'red' %} + + {% set request_route = collector.route ? collector.route : 'NONE' %} + + {% set icon %} + + {{ 'Request'|t }} + {{ collector.statuscode }} + + {% endset %} + + {% set text %} + {% spaceless %} +
      + {{ 'Status'|t }} + {{ collector.statuscode }} {{ collector.statustext }} +
      +
      + {{ 'Controller'|t }} + {{ request_handler }} +
      +
      + {{ 'Route name'|t }} + {{ request_route }} +
      + {% endspaceless %} + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/routing.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/routing.html.twig new file mode 100644 index 000000000..76700112a --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/routing.html.twig @@ -0,0 +1,50 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Routing'|t }} + {{ collector.getRoutesCount }} + + + {% endset %} + + {% set text %} +
      + {{ 'Routes'|t }} + {{ collector.getRoutesCount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/services.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/services.html.twig new file mode 100644 index 000000000..dab3e55dd --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/services.html.twig @@ -0,0 +1,149 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Services'|t }} + {{ collector.getInitializedServicesCount }} + + {% endset %} + {% set text %} +
      + {{ 'Initialized'|t }} + {{ collector.getInitializedServicesCount }} ({{ collector.getInitializedServicesWithoutWebprofilerCount }} + ) +
      +
      + {{ 'Available'|t }} + {{ collector.getServicesCount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/state.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/state.html.twig new file mode 100644 index 000000000..e19b13baf --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/state.html.twig @@ -0,0 +1,36 @@ +{% block toolbar %} + {% set icon %} + + {{ 'State'|t }} + {{ collector.getStateKeysCount }} + + {% endset %} + {% set text %} +
      + {{ 'State get'|t }} + {{ collector.getStateKeysCount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/theme.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/theme.html.twig new file mode 100644 index 000000000..66fa5fdfe --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/theme.html.twig @@ -0,0 +1,147 @@ +{% block toolbar %} + {% set negotiator %} + {{ abbr(collector.themeNegotiator.class.class)|raw }} + ::{{ collector.themeNegotiator.class.method }} + {% endset %} + {% set time = collector.templatecount ? '%0.0f ms'|format(collector.time) : 'n/a' %} + {% set icon %} + + {{ 'Theme'|t }} + {{ collector.activeTheme.name }} + + {% endset %} + {% set text %} + +
      + {{ 'Name'|t }} + {{ collector.activeTheme.name }} +
      +
      + {{ 'Engine'|t }} + {{ collector.activeTheme.engine }} +
      +
      + {{ 'Negotiator'|t }} + {{ negotiator }} +
      +
      + {{ 'Render Time'|t }} + {{ time }} +
      +
      + {{ 'Template Calls'|t }} + {{ collector.templatecount }} +
      +
      + {{ 'Block Calls'|t }} + {{ collector.blockcount }} +
      +
      + {{ 'Macro Calls'|t }} + {{ collector.macrocount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/time.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/time.html.twig new file mode 100644 index 000000000..9aeb76a37 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/time.html.twig @@ -0,0 +1,59 @@ +{% block toolbar %} + {% set duration = collector.events|length ? '%.0f ms'|format(collector.duration) : 'n/a' %} + {% set memory = '%.1f MB'|format(collector.memory / 1024 / 1024) %} + {% set total_memory = collector.memoryLimit == -1 ? '∞' : '%.1f MB'|format(collector.memoryLimit / 1024)|escape %} + {% set icon %} + + {{ 'Timeline'|t }} + {{ duration }} / {{ memory }} + + {% endset %} + {% set text %} +
      + {{ 'Total time'|t }} + {{ duration }} +
      +
      + {{ 'Memory'|t }} + {{ memory }} / {{ total_memory }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/translations.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/translations.html.twig new file mode 100644 index 000000000..08683ba91 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/translations.html.twig @@ -0,0 +1,85 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Translations'|t }} + {{ collector.untranslatedCount }} + + {% endset %} + {% set text %} +
      + {{ 'Translated'|t }} + {{ collector.translatedCount }} +
      +
      + {{ 'Untranslated'|t }} + {{ collector.untranslatedCount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/user.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/user.html.twig new file mode 100644 index 000000000..ef369975e --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/user.html.twig @@ -0,0 +1,63 @@ +{% block toolbar %} + {% set color_code = (collector.authenticated) ? 'green' : 'red' %} + {% set icon %} + + {{ 'User'|t }} + + {% if collector.authenticated %} + {{ collector.username }} + {% endif %} + + + {% endset %} + {% set text %} + {% if collector.authenticated %} +
      + {{ 'Logged in as'|t }} + {{ collector.username }} +
      +
      + {{ 'Roles'|t }} + {{ collector.roles|join(', ') }} +
      +
      + {{ 'Authenticated by'|t }} + {{ collector.provider }} +
      + {% else %} + {{ collector.anonymous }} + {% endif %} + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/views.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/views.html.twig new file mode 100644 index 000000000..d4a2e2f5c --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Collector/views.html.twig @@ -0,0 +1,58 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Views'|t }} + {{ collector.getViewsCount }} + + {% endset %} + {% set text %} + +
      + {{ 'Views'|t }} + {{ collector.getViewsCount }} +
      + {% endset %} + +
      +
      {{ icon|default('') }}
      +
      {{ text|default('') }}
      +
      +{% endblock %} + +{% block panel %} + +{% endblock %} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_dashboard.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_dashboard.html.twig new file mode 100644 index 000000000..be4fb68c9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_dashboard.html.twig @@ -0,0 +1,57 @@ +{% set resume %} +
      + + {{ profile.method|upper }} + {% if profile.method|upper in ['GET', 'HEAD'] %} + {{ profile.url }} + {% else %} + {{ profile.url }} + {% endif %} + + + by {{ profile.ip }} at {{ profile.time|date('r') }} + + + {{ 'View latest'|t }} +
      +{% endset %} + +
      + + {{ resume }} + +
      +
      + + + + + + {{ panels }} + + +
      diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_loader.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_loader.html.twig new file mode 100644 index 000000000..ad8da9bd0 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_loader.html.twig @@ -0,0 +1,73 @@ + + + diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_panel.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_panel.html.twig new file mode 100644 index 000000000..b06daff3f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_panel.html.twig @@ -0,0 +1 @@ +{{ panel|raw }} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_toolbar.css.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_toolbar.css.twig new file mode 100644 index 000000000..fccb64b0f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_toolbar.css.twig @@ -0,0 +1,311 @@ +.sf-minitoolbar { +display: none; + +position: fixed; +bottom: 0; +right: 0; + +padding: 5px 5px 0; + +background-color: #f7f7f7; +background-image: -moz-linear-gradient(top, #e4e4e4, #ffffff); +background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#e4e4e4), to(#ffffff)); +background-image: -o-linear-gradient(top, #e4e4e4, #ffffff); +background: linear-gradient(top, #e4e4e4, #ffffff); + +border-radius: 16px 0 0 0; + +z-index: 6000000; +} + +.sf-toolbarreset { +position: fixed; +background-color: #f7f7f7; +left: 0; +right: 0; +height: 38px; +margin: 0; +padding: 0 40px 0 0; +z-index: 6000000; +font: 11px Verdana, Arial, sans-serif; +text-align: left; +color: #2f2f2f; + +background-image: -moz-linear-gradient(top, #e4e4e4, #ffffff); +background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#e4e4e4), to(#ffffff)); +background-image: -o-linear-gradient(top, #e4e4e4, #ffffff); +background: linear-gradient(top, #e4e4e4, #ffffff); + +bottom: 0; +border-top: 1px solid #bbb; +} +.sf-toolbarreset abbr { +border-bottom: 1px dotted #000000; +} +.sf-toolbarreset span, +.sf-toolbarreset div { +font-size: 11px; +} +.sf-toolbarreset img { +width: auto; +display: inline; +} + +.sf-toolbarreset .hide-button { +display: block; +position: absolute; +top: 0; +right: 0; +width: 40px; +height: 40px; +cursor: pointer; +background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAAAllBMVEXDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PExMTPz8/Q0NDR0dHT09Pb29vc3Nzf39/h4eHi4uLj4+P6+vr7+/v8/Pz9/f3///+Nh2QuAAAAIXRSTlMABgwPGBswMzk8QktRV4SKjZOWmaKlq7TAxszb3urt+fy1vNEpAAAAiklEQVQIHUXBBxKCQBREwRFzDqjoGh+C2YV//8u5Sll2S0E/dof1tKdKM6GyqCto7PjZRJIS/mbSELgXOSd/BzpKIH1ZefVWpDDTHsi8mZVnwImPi5ndCLbaAZk3M58Bay0h9VbeSvMpjDUAHj4jL55AW1rxN5fU2PLjIgVRzNdxVFOlGzvnJi0Fb1XNGBHA9uQOAAAAAElFTkSuQmCC'); +background-repeat: no-repeat; +background-position: 13px 11px; +} +.sf-toolbarreset a { +color: #000 !important; +border: none; +} + +.sf-toolbar-block { +white-space: nowrap; +color: #2f2f2f; +display: block; +min-height: 28px; +border-right: 1px solid #e4e4e4; +padding: 0; +float: left; +cursor: default; +} + +.sf-toolbar-block span { +display: inline-block; +} + +.sf-toolbar-block .sf-toolbar-info-piece { +line-height: 19px; +margin-bottom: 5px; +} + +.sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status { +padding: 0px 5px; +border-radius: 5px; +margin-bottom: 0px; +vertical-align: top; +} + +.sf-toolbar-block .sf-toolbar-info-piece:last-child { +margin-bottom: 0; +} + +.sf-toolbar-block .sf-toolbar-info-piece a, +.sf-toolbar-block .sf-toolbar-info-piece abbr { +color: #2f2f2f; +} + +.sf-toolbar-block .sf-toolbar-info-piece b { +display: inline-block; +width: 150px; +vertical-align: top; +} + +.sf-toolbar-block .sf-toolbar-info-with-next-pointer:after { +content: ' :: '; +color: #999; +} + +.sf-toolbar-block .sf-toolbar-info-with-delimiter { +border-right: 1px solid #999; +padding-right: 5px; +margin-right: 5px; +} + +.sf-toolbar-block .sf-toolbar-info { +display: none; +position: absolute; +background-color: #fff; +border: 1px solid #bbb; +padding: 9px 0; +margin-left: -1px; + +bottom: 38px; +border-bottom-width: 0; +border-bottom: 1px solid #bbb; +border-radius: 4px 4px 0 0; +} + +.sf-toolbarreset > div:last-of-type .sf-toolbar-info { +right: -1px; +} + +.sf-toolbar-block .sf-toolbar-info:empty { +visibility: hidden; +} + +.sf-toolbar-block .sf-toolbar-status { +display: inline-block; +color: #fff; +background-color: #666; +padding: 3px 6px; +border-radius: 3px; +margin-bottom: 2px; +vertical-align: middle; +min-width: 6px; +min-height: 13px; +} + +.sf-toolbar-block .sf-toolbar-status abbr { +color: #fff; +border-bottom: 1px dotted black; +} + +.sf-toolbar-block .sf-toolbar-status-green { +background-color: #759e1a; +} + +.sf-toolbar-block .sf-toolbar-status-red { +background-color: #a33; +} + +.sf-toolbar-block .sf-toolbar-status-yellow { +background-color: #ffcc00; +color: #000; +} + +.sf-toolbar-block .sf-toolbar-status-black { +background-color: #000; +} + +.sf-toolbar-block .sf-toolbar-icon { +display: block; +} + +.sf-toolbar-block .sf-toolbar-icon > a, +.sf-toolbar-block .sf-toolbar-icon > span { +display: block; +font-weight: normal; +text-decoration: none; +margin: 0; +padding: 5px 6px; +min-width: 20px; +text-align: center; +vertical-align: middle; +} + +.sf-toolbar-block .sf-toolbar-icon > a, +.sf-toolbar-block .sf-toolbar-icon > a:link +.sf-toolbar-block .sf-toolbar-icon > a:hover { +color: black !important; +} + +.sf-toolbar-block .sf-toolbar-icon > a[href]:after { +content: ""; +} + +.sf-toolbar-block .sf-toolbar-icon img { +border-width: 0; +vertical-align: middle; +} + +.sf-toolbar-block .sf-toolbar-icon img + span { +margin-left: 0; +margin-top: 2px; +} + +.sf-toolbar-block .sf-toolbar-icon .sf-toolbar-status { +border-radius: 12px; +border-bottom-left-radius: 0; +margin-top: 0; +} + +.sf-toolbar-block .sf-toolbar-info-method { +cursor: hand; +} + +.sf-toolbar-block .sf-toolbar-info-method[onclick=""] { +border-bottom: none; +cursor: inherit; +} + +.sf-toolbar-info-php {} +.sf-toolbar-info-php-ext {} + +.sf-toolbar-info-php-ext .sf-toolbar-status { +margin-left: 2px; +} + +.sf-toolbar-info-php-ext .sf-toolbar-status:first-child { +margin-left: 0; +} + +.sf-toolbar-block a .sf-toolbar-info-piece-additional, +.sf-toolbar-block a .sf-toolbar-info-piece-additional-detail { +display: inline-block; +} + +.sf-toolbar-block a .sf-toolbar-info-piece-additional:empty, +.sf-toolbar-block a .sf-toolbar-info-piece-additional-detail:empty { +display: none; +} + +.sf-toolbarreset:hover { +box-shadow: rgba(0, 0, 0, 0.3) 0 0 50px; +} + +.sf-toolbar-block:hover { +box-shadow: rgba(0, 0, 0, 0.35) 0 0 5px; +border-right: none; +margin-right: 1px; +position: relative; +} + +.sf-toolbar-block:hover .sf-toolbar-icon { +background-color: #fff; +border-top: 1px dotted #DDD; +position: relative; +margin-top: -1px; +z-index: 10002; +} + +.sf-toolbar-block:hover .sf-toolbar-info { +display: block; +min-width: -webkit-calc(100% + 2px); +min-width: calc(100% + 2px); +z-index: 10001; +box-sizing: border-box; +padding: 9px; +line-height: 19px; +} + +/***** Media query *****/ +@media screen and (max-width: 779px) { +.sf-toolbar-block .sf-toolbar-icon > * > :first-child ~ * { +display: none; +} + +.sf-toolbar-block .sf-toolbar-icon > * > .sf-toolbar-info-piece-additional, +.sf-toolbar-block .sf-toolbar-icon > * > .sf-toolbar-info-piece-additional-detail { +display: none !important; +} +} + +@media screen and (max-width: 1179px) { +.sf-toolbar-block .sf-toolbar-icon > * > .sf-toolbar-info-piece-additional { +display: none; +} +} + +@media screen and (max-width: 1439px) { +.sf-toolbar-block .sf-toolbar-icon > * > .sf-toolbar-info-piece-additional-detail { +display: none; +} +} + +/***** Media query print: Do not print the Toolbar. *****/ +@media print { +.sf-toolbar { +display: none; +visibility: hidden; +} +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_toolbar.html.twig b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_toolbar.html.twig new file mode 100644 index 000000000..f452aec02 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/templates/Profiler/webprofiler_toolbar.html.twig @@ -0,0 +1,35 @@ + + +
      + +
      + {{ toolbar|raw }} + + +
      diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/tests/src/Unit/Cache/CacheDataCollectorTest.php b/sites/all/modules/contrib/dev/devel/webprofiler/tests/src/Unit/Cache/CacheDataCollectorTest.php new file mode 100644 index 000000000..f4673eea7 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/tests/src/Unit/Cache/CacheDataCollectorTest.php @@ -0,0 +1,69 @@ +cacheDataCollector = new CacheDataCollector(); + $this->cacheBackendInterface = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + } + + /** + * Tests the collection of a cache miss. + */ + public function testCacheCollectorMiss() { + $this->cacheBackendInterface->expects($this->once()) + ->method('get') + ->will($this->returnValue(FALSE)); + + $cacheBackendWrapper = new CacheBackendWrapper($this->cacheDataCollector, $this->cacheBackendInterface, 'default'); + $cache = $cacheBackendWrapper->get('cache_id'); + + $this->assertFalse($cache); + + $this->assertEquals(1, $this->cacheDataCollector->getCacheMissesCount()); + } + + /** + * Tests the collection of a cache hit. + */ + public function testCacheCollectorHit() { + $cache = new \StdClass(); + $cache->cid = 'cache_id'; + $cache->expire = 1; + $cache->tags = ['tag1', 'tag2']; + $this->cacheBackendInterface->expects($this->once()) + ->method('get') + ->will($this->returnValue($cache)); + + $cacheBackendWrapper = new CacheBackendWrapper($this->cacheDataCollector, $this->cacheBackendInterface, 'default'); + $cache2 = $cacheBackendWrapper->get('cache_id'); + + $this->assertNotNull($cache2); + + $this->assertEquals(1, $this->cacheDataCollector->getCacheHitsCount()); + } + +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.info.yml b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.info.yml new file mode 100644 index 000000000..39b35b0e4 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.info.yml @@ -0,0 +1,16 @@ +name: Web Profiler +type: module +description: 'Drupal Web Profiler.' +package: Development +# core: 8.x +configure: webprofiler.settings +tags: + - developer +dependencies: + - devel + +# Information added by Drupal.org packaging script on 2016-09-03 +version: '8.x-1.0-alpha1+23-dev' +core: '8.x' +project: 'devel' +datestamp: 1472897643 diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.install b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.install new file mode 100644 index 000000000..48c6e956f --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.install @@ -0,0 +1,190 @@ + 'Webprofiler profiles storage.', + 'fields' => [ + 'token' => [ + 'description' => 'Profile token.', + 'type' => 'varchar', + 'length' => 6, + 'not null' => TRUE, + ], + 'data' => [ + 'description' => 'Profile data.', + 'type' => 'text', + 'size' => 'big', + 'not null' => TRUE, + ], + 'ip' => [ + 'description' => 'Request IP.', + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + ], + 'method' => [ + 'description' => 'Request method.', + 'type' => 'varchar', + 'length' => 6, + 'not null' => TRUE, + ], + 'url' => [ + 'description' => 'Requested URL.', + 'type' => 'varchar', + 'length' => 2048, + 'not null' => TRUE, + ], + 'time' => [ + 'description' => 'Request time.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'parent' => [ + 'description' => 'Profile parent.', + 'type' => 'varchar', + 'length' => 6, + 'not null' => FALSE, + ], + 'created_at' => [ + 'description' => 'Profile created time.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + 'status_code' => [ + 'description' => 'Profile status code.', + 'type' => 'int', + 'size' => 'small', + 'unsigned' => TRUE, + 'not null' => TRUE, + ], + ], + 'indexes' => [ + 'created_at' => ['created_at'], + 'ip' => ['ip'], + 'method' => ['method'], + 'parent' => ['parent'], + ], + 'primary key' => ['token'], + ]; + + return $schema; +} + +/** + * Implements hook_requirements(). + */ +function webprofiler_requirements($phase) { + $requirements = []; + + if ('runtime' == $phase) { + $has_d3 = _webprofiler_verify_library('webprofiler', 'd3'); + $requirements['d3js'] = [ + 'title' => t('D3.js library'), + 'value' => $has_d3 ? t('Enabled') : t('Not found'), + ]; + + if (!$has_d3) { + $requirements['d3js']['severity'] = REQUIREMENT_WARNING; + $d3_url = Url::fromUri('https://github.com/mbostock/d3', ['attributes' => ['target' => '_blank']]); + $requirements['d3js']['description'] = [ + '#prefix' => ' ', + '#markup' => t('Webprofiler module requires D3.js library to properly render data. Download D3.js into /libraries/d3/d3.min.js.', [':d3_link' => $d3_url->toUriString()]) + ]; + } + + $has_highlight = _webprofiler_verify_library('webprofiler', 'highlightjs'); + $requirements['highlightjs'] = [ + 'title' => t('highlight.js library'), + 'value' => $has_highlight ? t('Enabled') : t('Not found'), + ]; + + if (!$has_highlight) { + $requirements['highlightjs']['severity'] = REQUIREMENT_WARNING; + $highlight_url = Url::fromUri('http://highlightjs.org', ['attributes' => ['target' => '_blank']]); + $requirements['highlightjs']['description'] = [ + '#prefix' => ' ', + '#markup' => t('Webprofiler module requires highlight.js library to syntax highlight collected queries. Download highlight.js assets into /libraries/highlight.', [':highlight_link' => $highlight_url->toUriString()]) + ]; + } + } + + return $requirements; +} + +/** + * Verify that the library files exist. + * + * @param string $extension + * The name of the extension that registered a library. + * @param string $name + * The name of a registered library to retrieve. + * + * @return bool + * TRUE if all files of this library exists, FALSE otherwise + * + * @see https://drupal.org/node/2231385 + */ +function _webprofiler_verify_library($extension, $name) { + /** @var Drupal\Core\Asset\LibraryDiscovery $library_discovery */ + $library_discovery = \Drupal::service('library.discovery'); + $library = $library_discovery->getLibraryByName($extension, $name); + + $exist = TRUE; + if ($library['js']) { + foreach ($library['js'] as $js) { + if ($js['type'] == 'file') { + if (!file_exists(DRUPAL_ROOT . '/' . $js['data'])) { + $exist = FALSE; + } + } + } + } + + if ($library['css']) { + foreach ($library['css'] as $css) { + if ($css['type'] == 'file') { + if (!file_exists(DRUPAL_ROOT . '/' . $css['data'])) { + $exist = FALSE; + } + } + } + } + + if ($library['dependencies']) { + foreach ($library['dependencies'] as $dependency) { + $parts = explode('/', $dependency); + $exist = _webprofiler_verify_library($parts[0], $parts[1]); + } + } + + return $exist; +} + +/** + * Add a status_code column to the webprofiler table. + */ +function webprofiler_update_8001() { + $database = \Drupal::database(); + $schema = $database->schema(); + + $spec = array( + 'description' => 'Profile status code.', + 'type' => 'int', + 'size' => 'small', + 'unsigned' => TRUE, + 'not null' => TRUE, + ); + $schema->addField('webprofiler', 'status_code', $spec); +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.libraries.yml b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.libraries.yml new file mode 100644 index 000000000..484cf8895 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.libraries.yml @@ -0,0 +1,63 @@ +d3: + remote: https://github.com/mbostock/d3 + version: 3.4.4 + license: + name: BSD-3 + url: https://github.com/mbostock/d3/blob/master/LICENSE + gpl-compatible: true + js: + libraries/d3/d3.min.js: {} +highlight: + remote: https://github.com/isagalaev/highlight.js + version: 8.0 + license: + name: BSD-3 + url: https://github.com/isagalaev/highlight.js/blob/master/LICENSE + gpl-compatible: true + js: + libraries/highlightjs/highlight.pack.js: {} + css: + component: + libraries/highlightjs/styles/idea.css: {} +dashboard: + version: 2.0 + js: + js/app/main.js: {} + js/app/helpers.js: {} + js/app/models/collector.js: {} + js/app/collections/collectors.js: {} + js/app/views/collector.js: {} + js/app/views/collectorsList.js: {} + js/app/views/layout.js: {} + js/app/views/details.js: {} + js/app/routers/collectors.js: {} + css: + component: + css/app/dashboard.css: {} + dependencies: + - core/jquery + - core/jquery.once + - core/drupal + - core/drupalSettings + - core/backbone +database: + version: 2.0 + js: + js/database.js: {} + dependencies: + - core/jquery + - core/drupal + - core/drupalSettings + - webprofiler/highlight +timeline: + version: 2.0 + js: + js/timeline.js: {} + css: + component: + css/timeline.css: {} + dependencies: + - core/jquery + - core/drupal + - core/drupalSettings + - webprofiler/d3 diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.links.menu.yml b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.links.menu.yml new file mode 100644 index 000000000..b2d4a7307 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.links.menu.yml @@ -0,0 +1,5 @@ +webprofiler.admin_list: + title: 'Webprofiler' + description: 'List saved profiles.' + route_name: webprofiler.admin_list + parent: system.admin_reports diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.links.task.yml b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.links.task.yml new file mode 100644 index 000000000..741eb15a9 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.links.task.yml @@ -0,0 +1,9 @@ +webprofiler.settings: + title: 'Webprofiler' + route_name: webprofiler.settings + base_route: devel.admin_settings + weight: 10 +webprofiler.dashboard: + title: 'dashboard' + route_name: webprofiler.dashboard + base_route: webprofiler.dashboard diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.make.yml b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.make.yml new file mode 100644 index 000000000..decfe2c19 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.make.yml @@ -0,0 +1,12 @@ +core: "8.x" +api: 2 + +libraries: + highlight: + download: + type: "git" + url: "git@github.com:mbostock/d3.git" + d3: + download: + type: "git" + url: "git@github.com:mbostock/d3.git" diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.module b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.module new file mode 100644 index 000000000..01e9b95f1 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.module @@ -0,0 +1,51 @@ + [ + 'template' => 'Profiler/webprofiler_loader', + 'variables' => ['token' => NULL, 'profiler_url' => NULL], + ], + 'webprofiler_toolbar' => [ + 'template' => 'Profiler/webprofiler_toolbar', + 'variables' => [ + 'token' => NULL, + 'toolbar' => NULL, + ], + ], + 'webprofiler_panel' => [ + 'template' => 'Profiler/webprofiler_panel', + 'variables' => [ + 'panel' => NULL, + ], + ], + 'webprofiler_dashboard' => [ + 'template' => 'Profiler/webprofiler_dashboard', + 'variables' => [ + 'profile' => [], + 'panels' => [], + 'spinner_path' => NULL, + ], + ], + ]; +} + +/** + * Implements hook_cache_flush(). + */ +function webprofiler_cache_flush() { + $config = \Drupal::configFactory()->get('webprofiler.config'); + + if ($config->get('purge_on_cache_clear')) { + $profiler = \Drupal::service('profiler'); + $profiler->purge(); + } +} diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.permissions.yml b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.permissions.yml new file mode 100644 index 000000000..b39752527 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.permissions.yml @@ -0,0 +1,7 @@ +'access webprofiler': + title: 'Access webprofiler' + description: 'Access webprofiler collected data.' + 'restrict access': TRUE +'view webprofiler toolbar': + title: 'View webprofiler toolbar' + description: 'View webprofiler toolbar.' diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.routing.yml b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.routing.yml new file mode 100644 index 000000000..200bc3973 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.routing.yml @@ -0,0 +1,81 @@ +# toolbar +webprofiler.toolbar: + path: '/profiler/{profile}' + defaults: + _controller: '\Drupal\webprofiler\Controller\ToolbarController::toolbarAction' + options: + parameters: + profile: + type: 'webprofiler:token' + requirements: + _permission: 'view webprofiler toolbar' + +# save frontend data +webprofiler.frontend.save: + path: '/admin/reports/profiler/frontend/{profile}/save' + defaults: + _controller: '\Drupal\webprofiler\Controller\ToolbarController::savePerformanceTimingAction' + _title: 'Save performance timing data' + options: + parameters: + profile: + type: 'webprofiler:token' + methods: [POST] + requirements: + _permission: 'access webprofiler' + +# view profile +webprofiler.dashboard: + path: '/admin/reports/profiler/view/{profile}' + defaults: + _controller: '\Drupal\webprofiler\Controller\DashboardController::dashboardAction' + _title: 'Webprofiler' + options: + parameters: + profile: + type: 'webprofiler:token' + requirements: + _permission: 'access webprofiler' + +# list stored profiles +webprofiler.admin_list: + path: '/admin/reports/profiler/list' + defaults: + _controller: '\Drupal\webprofiler\Controller\DashboardController::listAction' + _title: 'Webprofiler' + requirements: + _permission: 'access webprofiler' + +webprofiler.rest.collector: + path: '/admin/reports/profiler/view/{profile}/collectors/{collector}' + defaults: + _controller: '\Drupal\webprofiler\Controller\DashboardController::restCollectorAction' + _title: 'Webprofiler' + options: + parameters: + profile: + type: 'webprofiler:token' + requirements: + _permission: 'access webprofiler' + +# get query explain +webprofiler.database.explain: + path: '/admin/reports/profiler/database_explain/{profile}/{qid}' + defaults: + _controller: '\Drupal\webprofiler\Controller\DatabaseController::explainAction' + _title: 'Query explain' + options: + parameters: + profile: + type: 'webprofiler:token' + requirements: + _permission: 'access webprofiler' + +# configure webprofiler +webprofiler.settings: + path: '/admin/config/development/devel/webprofiler' + defaults: + _form: 'Drupal\webprofiler\Form\ConfigForm' + _title: 'Webprofiler settings' + requirements: + _permission: 'access webprofiler' diff --git a/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.services.yml b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.services.yml new file mode 100644 index 000000000..19b7e31f8 --- /dev/null +++ b/sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.services.yml @@ -0,0 +1,267 @@ +parameters: + webprofiler.only_exceptions: false + webprofiler.only_master_requests: false + +services: + +# profiler services + logger.channel.webprofiler: + class: Drupal\Core\Logger\LoggerChannel + factory: logger.factory:get + arguments: ['webprofiler'] + + profiler.file_storage: + class: Drupal\webprofiler\Profiler\FileProfilerStorage + arguments: ['%data_collector.storage%'] + tags: + - { name: webprofiler_storage, title: 'File storage' } + + profiler.database_storage: + class: Drupal\webprofiler\Profiler\DatabaseProfilerStorage + arguments: ['@database'] + tags: + - { name: webprofiler_storage, title: 'Database storage' } + + profiler.storage_manager: + class: Drupal\webprofiler\Profiler\ProfilerStorageManager + + profiler.storage: + class: Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface + factory: Drupal\webprofiler\Profiler\ProfilerStorageFactory::getProfilerStorage + arguments: ['@config.factory', '@service_container'] + + profiler: + class: Drupal\webprofiler\Profiler\Profiler + arguments: ['@profiler.storage', '@logger.channel.webprofiler', '@config.factory'] + +# template manager + template_manager: + class: Drupal\webprofiler\Profiler\TemplateManager + arguments: ['@profiler', '@twig', '@twig.loader', '%data_collector.templates%'] + +# request matcher + webprofiler.matcher: + class: Drupal\webprofiler\RequestMatcher\WebprofilerRequestMatcher + arguments: ['@config.factory', '@path.matcher'] + +# event subscribers + webprofiler.profiler_listener: + class: Symfony\Component\HttpKernel\EventListener\ProfilerListener + arguments: ['@profiler', '@?webprofiler.matcher', '%webprofiler.only_exceptions%', '%webprofiler.only_master_requests%', '@request_stack'] + tags: + - { name: event_subscriber } + + webprofiler.webprofiler_event_subscriber: + class: Drupal\webprofiler\EventSubscriber\WebprofilerEventSubscriber + arguments: ['@current_user', '@url_generator', '@renderer'] + tags: + - { name: event_subscriber } + +# twig profiler + twig.profile: + class: Twig_Profiler_Profile + + twig.extension.profiler: + class: Drupal\webprofiler\Twig\Extension\ProfilerExtension + arguments: ['@twig.profile', '@stopwatch', '@webprofiler.ide_link_generator', '@webprofiler.class_shortener'] + tags: + - { name: twig.extension, priority: 100 } + +# datacollector services + webprofiler.drupal: + class: Drupal\webprofiler\DataCollector\DrupalDataCollector + arguments: ['@redirect.destination', '@url_generator'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/drupal.html.twig', id: 'drupal', title: 'Drupal', priority: 10 } + + webprofiler.devel: + class: Drupal\webprofiler\DataCollector\DevelDataCollector + arguments: ['@menu.link_tree'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/devel.html.twig', id: 'devel', title: 'Devel', priority: 15 } + + webprofiler.php_config: + class: Drupal\webprofiler\DataCollector\PhpConfigDataCollector + tags: + - { name: data_collector, template: '@webprofiler/Collector/php_config.html.twig', id: 'php_config', title: 'PHP Config', priority: 20 } + + webprofiler.request: + class: Drupal\webprofiler\DataCollector\RequestDataCollector + arguments: ['@controller_resolver'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/request.html.twig', id: 'request', title: 'Request', priority: 30 } + - { name: event_subscriber } + + webprofiler.time: + class: Drupal\webprofiler\DataCollector\TimeDataCollector + arguments: [NULL, '@stopwatch'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/time.html.twig', id: 'time', title: 'Timeline', priority: 40 } + + webprofiler.performance_timing: + class: Drupal\webprofiler\DataCollector\PerformanceTimingDataCollector + tags: + - { name: data_collector, template: '@webprofiler/Collector/performance_timing.html.twig', id: 'performance_timing', title: 'Performance Timing', priority: 50 } + + webprofiler.database: + class: Drupal\webprofiler\DataCollector\DatabaseDataCollector + arguments: ['@database', '@config.factory'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/database.html.twig', id: 'database', title: 'Database', priority: 60 } + + webprofiler.user: + class: Drupal\webprofiler\DataCollector\UserDataCollector + arguments: ['@current_user', '@entity.manager', '@config.factory', '@authentication_collector'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/user.html.twig', id: 'user', title: 'User', priority: 70 } + + webprofiler.forms: + class: Drupal\webprofiler\DataCollector\FormsDataCollector + arguments: ['@form_builder'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/forms.html.twig', id: 'forms', title: 'Forms', priority: 80 } + + webprofiler.drupal_extensions: + class: Drupal\webprofiler\DataCollector\ExtensionDataCollector + arguments: ['@module_handler', '@theme_handler', '@app.root'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/extensions.html.twig', id: 'drupal_extension', title: 'Extensions', priority: 90 } + + webprofiler.routing: + class: Drupal\webprofiler\DataCollector\RoutingDataCollector + arguments: ['@router.route_provider'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/routing.html.twig', id: 'routing', title: 'Routing', priority: 100 } + + webprofiler.cache: + class: Drupal\webprofiler\DataCollector\CacheDataCollector + tags: + - { name: data_collector, template: '@webprofiler/Collector/cache.html.twig', id: 'cache', title: 'Cache', priority: 110 } + + webprofiler.assets: + class: Drupal\webprofiler\DataCollector\AssetsDataCollector + arguments: ['@app.root'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/assets.html.twig', id: 'assets', title: 'Assets', priority: 120} + + webprofiler.config: + class: Drupal\webprofiler\DataCollector\ConfigDataCollector + tags: + - { name: data_collector, template: '@webprofiler/Collector/config.html.twig', id: 'config', title: 'Config', priority: 130 } + + webprofiler.state: + class: Drupal\webprofiler\DataCollector\StateDataCollector + tags: + - { name: data_collector, template: '@webprofiler/Collector/state.html.twig', id: 'state', title: 'State', priority: 140 } + + webprofiler.events: + class: Drupal\webprofiler\DataCollector\EventsDataCollector + arguments: ['@event_dispatcher'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/events.html.twig', id: 'events', title: 'Events', priority: 150 } + + webprofiler.services: + class: Drupal\webprofiler\DataCollector\ServicesDataCollector + arguments: ['@service_container'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/services.html.twig', id: 'services', title: 'Services', priority: 160 } + + webprofiler.http: + class: Drupal\webprofiler\DataCollector\HttpDataCollector + arguments: ['@http_client_middleware.webprofiler'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/http.html.twig', id: 'http', title: 'Http', priority: 170 } + + webprofiler.theme: + class: Drupal\webprofiler\DataCollector\ThemeDataCollector + arguments: ['@theme.manager', '@theme.negotiator', '@twig.profile'] + tags: + - { name: data_collector, template: '@webprofiler/Collector/theme.html.twig', id: 'theme', title: 'Theme', priority: 180 } + + webprofiler.mail: + class: Drupal\webprofiler\DataCollector\MailDataCollector + tags: + - { name: data_collector, template: '@webprofiler/Collector/mail.html.twig', id: 'mail', title: 'Mail', priority: 190 } + +# debug services + stopwatch: + class: Drupal\webprofiler\Stopwatch + + webprofiler.debug.event_dispatcher.default: + class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher + arguments: ['@service_container'] + + webprofiler.debug.plugin.manager.mail.default: + class: Drupal\Core\Mail\MailManager + arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@config.factory', '@logger.factory', '@string_translation'] + + webprofiler.debug.controller_resolver: + class: Symfony\Component\HttpKernel\Controller\TraceableControllerResolver + arguments: ['@controller_resolver', '@stopwatch'] + + webprofiler.debug.cache_factory: + class: Drupal\webprofiler\Cache\CacheFactoryWrapper + public: false + decorates: cache_factory + arguments: ['@webprofiler.debug.cache_factory.inner', '@webprofiler.cache'] + properties: + _serviceId: 'cache_factory' + + webprofiler.debug.asset.css.collection_renderer: + class: Drupal\webprofiler\Asset\CssCollectionRendererWrapper + public: false + decorates: asset.css.collection_renderer + arguments: ['@webprofiler.debug.asset.css.collection_renderer.inner', '@webprofiler.assets'] + properties: + _serviceId: 'asset.css.collection_renderer' + + webprofiler.debug.asset.js.collection_renderer: + class: Drupal\webprofiler\Asset\JsCollectionRendererWrapper + public: false + decorates: asset.js.collection_renderer + arguments: ['@webprofiler.debug.asset.js.collection_renderer.inner', '@webprofiler.assets'] + properties: + _serviceId: 'asset.js.collection_renderer' + + webprofiler.debug.entity_type.manager: + class: Drupal\webprofiler\Entity\EntityManagerWrapper + public: false + decorates: entity_type.manager + arguments: ['@webprofiler.debug.entity_type.manager.inner'] + properties: + _serviceId: 'entity_type.manager' + + webprofiler.debug.state: + class: Drupal\webprofiler\State\StateWrapper + public: false + decorates: state + arguments: ['@webprofiler.debug.state.inner', '@webprofiler.state'] + properties: + _serviceId: 'state' + +# middleware + http_middleware.webprofiler: + class: Drupal\webprofiler\StackMiddleware\WebprofilerMiddleware + tags: + - { name: http_middleware, priority: 350 } + + http_client_middleware.webprofiler: + class: Drupal\webprofiler\Http\HttpClientMiddleware + tags: + - { name: http_client_middleware } + +# parameter converter service for profile token + webprofiler.token_converter: + class: Drupal\webprofiler\Routing\TokenConverter + arguments: ['@profiler'] + tags: + - { name: paramconverter } + +# IDE link generator service + webprofiler.ide_link_generator: + class: Drupal\webprofiler\Helper\IdeLinkGenerator + arguments: ['@config.factory'] + +# class shortener service + webprofiler.class_shortener: + class: Drupal\webprofiler\Helper\ClassShortener diff --git a/sites/all/modules/contrib/dev/entity/.travis.yml b/sites/all/modules/contrib/dev/entity/.travis.yml new file mode 100644 index 000000000..052224c78 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/.travis.yml @@ -0,0 +1,58 @@ +language: php +sudo: false + +php: + - 5.5 + - 5.6 + - 7 + - hhvm + +matrix: + allow_failures: + # We cannot use hhvm-nightly since that does not work in Travis CI's old + # Ubuntu 12.04. + - php: hhvm + # Don't wait for the allowed failures to build. + fast_finish: true + +mysql: + database: entity + username: root + encoding: utf8 + +before_script: + # Remove Xdebug as we don't need it and it causes + # PHP Fatal error: Maximum function nesting level of '256' reached. + # We also don't care if that file exists or not on PHP 7. + - phpenv config-rm xdebug.ini || true + + # Remember the current entity test directory for later use in the Drupal + # installation. + - TESTDIR=$(pwd) + # Navigate out of module directory to prevent blown stack by recursive module + # lookup. + - cd .. + + # Create database. + - mysql -e 'create database entity' + # Export database variable for kernel tests. + - export SIMPLETEST_DB=mysql://root:@127.0.0.1/entity + # Download Drupal 8 core. + - travis_retry git clone --branch 8.1.x --depth 1 http://git.drupal.org/project/drupal.git + - cd drupal + - composer self-update + - composer install -n + + # Reference entity in build site. + - ln -s $TESTDIR modules/entity + + # Start a web server on port 8888, run in the background; wait for + # initialization. + - nohup php -S localhost:8888 > /dev/null 2>&1 & + + # Export web server URL for browser tests. + - export SIMPLETEST_BASE_URL=http://localhost:8888 + +script: + # Run the PHPUnit tests which also include the kernel tests. + - ./vendor/phpunit/phpunit/phpunit -c ./core/phpunit.xml.dist --verbose ./modules/entity diff --git a/sites/all/modules/contrib/dev/entity/LICENSE.txt b/sites/all/modules/contrib/dev/entity/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General +Public License instead of this License. diff --git a/sites/all/modules/contrib/dev/entity/README.txt b/sites/all/modules/contrib/dev/entity/README.txt new file mode 100644 index 000000000..c19422cd3 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/README.txt @@ -0,0 +1,11 @@ +Entity API module +----------------- + +This module contains improvements and extensions to the Drupal 8 Entity system. +The goal is to bring useful improvements to upcoming Drupal 8 minor releases +(e.g. 8.1, 8.2, ..) while maintaining backwards compatibility in future Entity +API module versions (based on the improvements which went into core). + +@todo: Explain version compatibility pattern. +@todo: Add overview of all items and maintainers. + diff --git a/sites/all/modules/contrib/dev/entity/composer.json b/sites/all/modules/contrib/dev/entity/composer.json new file mode 100644 index 000000000..b41c15ad7 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/composer.json @@ -0,0 +1,10 @@ +{ + "name": "drupal/entity", + "type": "drupal-module", + "description": "Provides expanded entity APIs, which will be moved to Drupal core one day.", + "homepage": "http://drupal.org/project/entity", + "license": "GPL-2.0+", + "require": { + "drupal/core": "~8.1" + } +} diff --git a/sites/all/modules/contrib/dev/entity/config/schema/entity.schema.yml b/sites/all/modules/contrib/dev/entity/config/schema/entity.schema.yml new file mode 100644 index 000000000..b3c192358 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/config/schema/entity.schema.yml @@ -0,0 +1,11 @@ +action.configuration.entity_delete_action:*: + type: action_configuration_default + label: 'Delete entity configuration' + +views.field.rendered_entity: + type: views_field + label: 'Rendered entity' + mapping: + view_mode: + type: string + label: 'View mode' diff --git a/sites/all/modules/contrib/dev/entity/entity.info.yml b/sites/all/modules/contrib/dev/entity/entity.info.yml new file mode 100644 index 000000000..5f3bdd3e8 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/entity.info.yml @@ -0,0 +1,12 @@ +name: Entity +description: Provides expanded entity APIs, which will be moved to Drupal core one day. +type: module +# core: 8.x +dependencies: + - system (>=8.1.0) + +# Information added by Drupal.org packaging script on 2016-06-23 +version: '8.x-1.0-alpha3+2-dev' +core: '8.x' +project: 'entity' +datestamp: 1466693538 diff --git a/sites/all/modules/contrib/dev/entity/entity.links.task.yml b/sites/all/modules/contrib/dev/entity/entity.links.task.yml new file mode 100644 index 000000000..8116ae326 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/entity.links.task.yml @@ -0,0 +1,3 @@ +entity.revisions_overview: + deriver: 'Drupal\entity\Plugin\Derivative\RevisionsOverviewDeriver' + weight: 100 diff --git a/sites/all/modules/contrib/dev/entity/entity.module b/sites/all/modules/contrib/dev/entity/entity.module new file mode 100644 index 000000000..8f330b28e --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/entity.module @@ -0,0 +1,6 @@ +getDefinitions(); + $entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) { + return $entity_type->isSubclassOf(ContentEntityInterface::class); + }); + + $data = []; + foreach ($entity_types as $entity_type) { + /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */ + $base_table = $entity_type->getBaseTable() ?: $entity_type->id(); + + if ($entity_type->hasViewBuilderClass()) { + $data[$base_table]['rendered_entity'] = [ + 'field' => [ + 'title' => t('Rendered entity'), + 'help' => t('Renders an entity in a view mode.'), + 'id' => 'rendered_entity', + ], + ]; + } + } + + return $data; +} diff --git a/sites/all/modules/contrib/dev/entity/src/Access/EntityRevisionRouteAccessChecker.php b/sites/all/modules/contrib/dev/entity/src/Access/EntityRevisionRouteAccessChecker.php new file mode 100644 index 000000000..d9556e1dc --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Access/EntityRevisionRouteAccessChecker.php @@ -0,0 +1,159 @@ +entityTypeManager = $entity_type_manager; + $this->requestStack = $request_stack; + } + + /** + * {@inheritdoc} + */ + public function access(Route $route, AccountInterface $account, Request $request = NULL) { + if (empty($request)) { + $request = $this->requestStack->getCurrentRequest(); + } + + $operation = $route->getRequirement('_entity_access_revision'); + list(, $operation) = explode('.', $operation, 2); + + if ($operation === 'list') { + $_entity = $request->attributes->get('_entity', $request->attributes->get($route->getOption('entity_type_id'))); + return AccessResult::allowedIf($this->checkAccess($_entity, $account, $operation))->cachePerPermissions(); + } + else { + $_entity_revision = $request->attributes->get('_entity_revision'); + return AccessResult::allowedIf($_entity_revision && $this->checkAccess($_entity_revision, $account, $operation))->cachePerPermissions(); + } + } + + protected function checkAccess(ContentEntityInterface $entity, AccountInterface $account, $operation = 'view') { + $entity_type = $entity->getEntityType(); + $entity_type_id = $entity->getEntityTypeId(); + $entity_access = $this->entityTypeManager->getAccessControlHandler($entity_type_id); + + /** @var \Drupal\Core\Entity\EntityStorageInterface $entity_storage */ + $entity_storage = $this->entityTypeManager->getStorage($entity_type_id); + + $map = [ + 'view' => "view all $entity_type_id revisions", + 'list' => "view all $entity_type_id revisions", + 'update' => "revert all $entity_type_id revisions", + 'delete' => "delete all $entity_type_id revisions", + ]; + $bundle = $entity->bundle(); + $type_map = [ + 'view' => "view $entity_type_id $bundle revisions", + 'list' => "view $entity_type_id $bundle revisions", + 'update' => "revert $entity_type_id $bundle revisions", + 'delete' => "delete $entity_type_id $bundle revisions", + ]; + + if (!$entity || !isset($map[$operation]) || !isset($type_map[$operation])) { + // If there was no node to check against, or the $op was not one of the + // supported ones, we return access denied. + return FALSE; + } + + // Statically cache access by revision ID, language code, user account ID, + // and operation. + $langcode = $entity->language()->getId(); + $cid = $entity->getRevisionId() . ':' . $langcode . ':' . $account->id() . ':' . $operation; + + if (!isset($this->accessCache[$cid])) { + // Perform basic permission checks first. + if (!$account->hasPermission($map[$operation]) && !$account->hasPermission($type_map[$operation]) && !$account->hasPermission('administer nodes')) { + $this->accessCache[$cid] = FALSE; + return FALSE; + } + + if (($admin_permission = $entity_type->getAdminPermission()) && $account->hasPermission($admin_permission)) { + $this->accessCache[$cid] = TRUE; + } + else { + // First check the access to the default revision and finally, if the + // node passed in is not the default revision then access to that, too. + $this->accessCache[$cid] = $entity_access->access($entity_storage->load($entity->id()), $operation, $account) && ($entity->isDefaultRevision() || $entity_access->access($entity, $operation, $account)); + } + } + + return $this->accessCache[$cid]; + } + + + /** + * Counts the number of revisions in the default language. + * + * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * The entity. + * @param \Drupal\Core\Entity\EntityStorageInterface $entity_storage + * The entity storage. + * + * @return int + * The number of revisions in the default language. + */ + protected function countDefaultLanguageRevisions(ContentEntityInterface $entity, EntityStorageInterface $entity_storage) { + $entity_type = $entity->getEntityType(); + $count = $entity_storage->getQuery() + ->allRevisions() + ->condition($entity_type->getKey('id'), $entity->id()) + ->condition($entity_type->getKey('default_langcode'), 1) + ->count() + ->execute(); + return $count; + } + + /** + * Resets the access cache. + * + * @return $this + */ + public function resetAccessCache() { + $this->accessCache = []; + return $this; + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Controller/RevisionControllerTrait.php b/sites/all/modules/contrib/dev/entity/src/Controller/RevisionControllerTrait.php new file mode 100644 index 000000000..9751b17ae --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Controller/RevisionControllerTrait.php @@ -0,0 +1,200 @@ +getEntityType(); + $result = $this->entityTypeManager()->getStorage($entity_type->id())->getQuery() + ->allRevisions() + ->condition($entity_type->getKey('id'), $entity->id()) + ->sort($entity_type->getKey('revision'), 'DESC') + ->execute(); + return array_keys($result); + } + + /** + * Generates an overview table of older revisions of an entity. + * + * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * An entity object. + * + * @return array + * A render array. + */ + protected function revisionOverview(ContentEntityInterface $entity) { + $langcode = $this->languageManager() + ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT) + ->getId(); + $entity_storage = $this->entityTypeManager() + ->getStorage($entity->getEntityTypeId()); + + $header = [$this->t('Revision'), $this->t('Operations')]; + $rows = []; + + $revision_ids = $this->revisionIds($entity); + // @todo Expand the entity storage to load multiple revisions. + $entity_revisions = array_combine($revision_ids, array_map(function($vid) use ($entity_storage) { + return $entity_storage->loadRevision($vid); + }, $revision_ids)); + + foreach ($entity_revisions as $revision) { + $row = []; + /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */ + if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode) + ->isRevisionTranslationAffected() + ) { + $row[] = $this->getRevisionDescription($revision, $revision->isDefaultRevision()); + + if ($revision->isDefaultRevision()) { + $row[] = [ + 'data' => [ + '#prefix' => '', + '#markup' => $this->t('Current revision'), + '#suffix' => '', + ], + ]; + foreach ($row as &$current) { + $current['class'] = ['revision-current']; + } + } + else { + $links = $this->getOperationLinks($revision); + $row[] = [ + 'data' => [ + '#type' => 'operations', + '#links' => $links, + ], + ]; + } + } + + $rows[] = $row; + } + + $build[$entity->getEntityTypeId() . '_revisions_table'] = [ + '#theme' => 'table', + '#rows' => $rows, + '#header' => $header, + ]; + + // We have no clue about caching yet. + $build['#cache']['max-age'] = 0; + + return $build; + } + + /** + * Get the links of the operations for an entity revision. + * + * @param \Drupal\Core\Entity\EntityInterface $entity_revision + * The entity to build the revision links for. + * + * @return array + * The operation links. + */ + protected function getOperationLinks(EntityInterface $entity_revision) { + $links = []; + if ($this->hasRevertRevisionAccess($entity_revision)) { + $links['revert'] = $this->buildRevertRevisionLink($entity_revision); + } + + if ($this->hasDeleteRevisionAccess($entity_revision)) { + $links['delete'] = $this->buildDeleteRevisionLink($entity_revision); + } + + return array_filter($links); + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Controller/RevisionOverviewController.php b/sites/all/modules/contrib/dev/entity/src/Controller/RevisionOverviewController.php new file mode 100644 index 000000000..502a67cb8 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Controller/RevisionOverviewController.php @@ -0,0 +1,161 @@ +dateFormatter = $date_formatter; + $this->renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('date.formatter'), $container->get('renderer')); + } + + /** + * {@inheritdoc} + */ + protected function hasDeleteRevisionAccess(EntityInterface $entity) { + return $this->currentUser()->hasPermission("delete all {$entity->id()} revisions"); + } + + /** + * {@inheritdoc} + */ + protected function buildRevertRevisionLink(EntityInterface $entity_revision) { + if ($entity_revision->hasLinkTemplate('revision-revert-form')) { + return [ + 'title' => t('Revert'), + 'url' => $entity_revision->toUrl('revision-revert-form'), + ]; + } + } + + /** + * {@inheritdoc} + */ + protected function buildDeleteRevisionLink(EntityInterface $entity_revision) { + if ($entity_revision->hasLinkTemplate('revision-delete-form')) { + return [ + 'title' => t('Delete'), + 'url' => $entity_revision->toUrl('revision-delete-form'), + ]; + } + } + + /** + * Generates an overview table of older revisions of an entity. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. + * + * @return array + * A render array. + */ + public function revisionOverviewController(RouteMatchInterface $route_match) { + return $this->revisionOverview($route_match->getParameter($route_match->getRouteObject()->getOption('entity_type_id'))); + } + + /** + * {@inheritdoc} + */ + protected function getRevisionDescription(ContentEntityInterface $revision, $is_default = FALSE) { + /** @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\user\EntityOwnerInterface|\Drupal\Core\Entity\RevisionLogInterface $revision */ + if ($revision instanceof RevisionLogInterface) { + // Use revision link to link to revisions that are not active. + $date = $this->dateFormatter->format($revision->getRevisionCreationTime(), 'short'); + $link = $revision->toLink($date, 'revision'); + + // @todo: Simplify this when https://www.drupal.org/node/2334319 lands. + $username = [ + '#theme' => 'username', + '#account' => $revision->getRevisionUser(), + ]; + $username = $this->renderer->render($username); + } + else { + $link = $revision->toLink($revision->label(), 'revision'); + $username = ''; + + } + + $markup = ''; + if ($revision instanceof RevisionLogInterface) { + $markup = $revision->getRevisionLogMessage(); + } + + if ($username) { + $template = '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}

      {{ message }}

      {% endif %}'; + } + else { + $template = '{% trans %} {{ date }} {% endtrans %}{% if message %}

      {{ message }}

      {% endif %}'; + } + + $column = [ + 'data' => [ + '#type' => 'inline_template', + '#template' => $template, + '#context' => [ + 'date' => $link->toString(), + 'username' => $username, + 'message' => ['#markup' => $markup, '#allowed_tags' => Xss::getHtmlTagList()], + ], + ], + ]; + return $column; + } + + /** + * {@inheritdoc} + */ + protected function hasRevertRevisionAccess(EntityInterface $entity) { + return AccessResult::allowedIfHasPermission($this->currentUser(), "revert all {$entity->getEntityTypeId()} revisions")->orIf( + AccessResult::allowedIfHasPermission($this->currentUser(), "revert {$entity->bundle()} {$entity->getEntityTypeId()} revisions") + ); + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Entity/RevisionableEntityBundleInterface.php b/sites/all/modules/contrib/dev/entity/src/Entity/RevisionableEntityBundleInterface.php new file mode 100644 index 000000000..6e543b17d --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Entity/RevisionableEntityBundleInterface.php @@ -0,0 +1,17 @@ +getEntityTypeId(); + if (($entity instanceof ContentEntityInterface && $entity->isDefaultRevision()) || !$entity->getEntityType()->isRevisionable()) { + $build['#contextual_links'][$entity_type_id] = [ + 'route_parameters' => [ + $entity_type_id => $entity->id() + ], + ]; + } + else { + $build['#contextual_links'][$entity_type_id . '_revision'] = [ + 'route_parameters' => [ + $entity_type_id => $entity->id(), + $entity_type_id . '_revision' => $entity->getRevisionId(), + ], + ]; + } + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Form/DeleteMultiple.php b/sites/all/modules/contrib/dev/entity/src/Form/DeleteMultiple.php new file mode 100644 index 000000000..6268a95ac --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Form/DeleteMultiple.php @@ -0,0 +1,226 @@ + langcodes format. + * + * @var array + */ + protected $selection = []; + + /** + * Constructs a new DeleteMultiple object. + * + * @param \Drupal\Core\Session\AccountInterface $current_user + * The current user. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * The tempstore factory. + */ + public function __construct(AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory) { + $this->currentUser = $current_user; + $this->entityTypeManager = $entity_type_manager; + $this->tempStore = $temp_store_factory->get('entity_delete_multiple_confirm'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('current_user'), + $container->get('entity_type.manager'), + $container->get('user.private_tempstore') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'entity_delete_multiple_confirm'; + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->formatPlural(count($this->selection), 'Are you sure you want to delete this item?', 'Are you sure you want to delete these items?'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('entity.' . $this->entityTypeId . '.collection'); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Delete'); + } + + /** + * {@inheritdoc} + * + * @param string $entity_type_id + * The entity type id. + */ + public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) { + $this->entityTypeId = $entity_type_id; + $this->selection = $this->tempStore->get($this->currentUser->id()); + if (empty($this->entityTypeId) || empty($this->selection)) { + return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString()); + } + + $storage = $this->entityTypeManager->getStorage($this->entityTypeId); + /** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */ + $entities = $storage->loadMultiple(array_keys($this->selection)); + $items = []; + foreach ($this->selection as $id => $langcodes) { + foreach ($langcodes as $langcode) { + $entity = $entities[$id]->getTranslation($langcode); + $key = $id . ':' . $langcode; + $default_key = $id . ':' . $entity->getUntranslated()->language()->getId(); + + // If we have a translated entity we build a nested list of translations + // that will be deleted. + $languages = $entity->getTranslationLanguages(); + if (count($languages) > 1 && $entity->isDefaultTranslation()) { + $names = []; + foreach ($languages as $translation_langcode => $language) { + $names[] = $language->getName(); + unset($items[$id . ':' . $translation_langcode]); + } + $items[$default_key] = [ + 'label' => [ + '#markup' => $this->t('@label (Original translation) - The following translations will be deleted:', ['@label' => $entity->label()]), + ], + 'deleted_translations' => [ + '#theme' => 'item_list', + '#items' => $names, + ], + ]; + } + elseif (!isset($items[$default_key])) { + $items[$key] = $entity->label(); + } + } + } + + $form['entities'] = [ + '#theme' => 'item_list', + '#items' => $items, + ]; + $form = parent::buildForm($form, $form_state); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $total_count = 0; + $delete_entities = []; + $delete_translations = []; + $storage = $this->entityTypeManager->getStorage($this->entityTypeId); + /** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */ + $entities = $storage->loadMultiple(array_keys($this->selection)); + + foreach ($this->selection as $id => $langcodes) { + foreach ($langcodes as $langcode) { + $entity = $entities[$id]->getTranslation($langcode); + if ($entity->isDefaultTranslation()) { + $delete_entities[$id] = $entity; + unset($delete_translations[$id]); + $total_count += count($entity->getTranslationLanguages()); + } + elseif (!isset($delete_entities[$id])) { + $delete_translations[$id][] = $entity; + } + } + } + + if ($delete_entities) { + $storage->delete($delete_entities); + $this->logger('content')->notice('Deleted @count @entity_type items.', [ + '@count' => count($delete_entities), + '@entity_type' => $this->entityTypeId, + ]); + } + + if ($delete_translations) { + $count = 0; + /** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */ + foreach ($delete_translations as $id => $translations) { + $entity = $entities[$id]->getUntranslated(); + foreach ($translations as $translation) { + $entity->removeTranslation($translation->language()->getId()); + } + $entity->save(); + $count += count($translations); + } + if ($count) { + $total_count += $count; + $this->logger('content')->notice('Deleted @count @entity_type translations.', [ + '@count' => $count, + '@entity_type' => $this->entityTypeId, + ]); + } + } + + if ($total_count) { + drupal_set_message($this->formatPlural($total_count, 'Deleted 1 item.', 'Deleted @count items.')); + } + $this->tempStore->delete($this->currentUser->id()); + $form_state->setRedirectUrl($this->getCancelUrl()); + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Form/RevisionRevertForm.php b/sites/all/modules/contrib/dev/entity/src/Form/RevisionRevertForm.php new file mode 100644 index 000000000..c94c53a0c --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Form/RevisionRevertForm.php @@ -0,0 +1,167 @@ +dateFormatter = $date_formatter; + $this->bundleInformation = $bundle_information; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('date.formatter'), + $container->get('entity_type.bundle.info') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'entity_revision_revert_confirm'; + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + if ($this->revision instanceof RevisionLogInterface) { + return $this->t('Are you sure you want to revert to the revision from %revision-date?', ['%revision-date' => $this->dateFormatter->format($this->revision->getRevisionCreationTime())]); + } + return $this->t('Are you sure you want to revert the revision?'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + if ($this->revision->getEntityType()->hasLinkTemplate('version-history')) { + return $this->revision->toUrl('version-history'); + } + return $this->revision->toUrl(); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return t('Revert'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return ''; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, $_entity_revision = NULL, Request $request = NULL) { + $this->revision = $_entity_revision; + $form = parent::buildForm($form, $form_state); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // The revision timestamp will be updated when the revision is saved. Keep + // the original one for the confirmation message. + $this->revision = $this->prepareRevision($this->revision); + if ($this->revision instanceof RevisionLogInterface) { + $original_revision_timestamp = $this->revision->getRevisionCreationTime(); + + $this->revision->setRevisionLogMessage($this->t('Copy of the revision from %date.', ['%date' => $this->dateFormatter->format($original_revision_timestamp)])); + drupal_set_message(t('@type %title has been reverted to the revision from %revision-date.', ['@type' => $this->getBundleLabel($this->revision), '%title' => $this->revision->label(), '%revision-date' => $this->dateFormatter->format($original_revision_timestamp)])); + } + else { + drupal_set_message(t('@type %title has been reverted', ['@type' => $this->getBundleLabel($this->revision), '%title' => $this->revision->label()])); + } + + $this->revision->save(); + + $this->logger('content')->notice('@type: reverted %title revision %revision.', ['@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId()]); + $form_state->setRedirect( + "entity.{$this->revision->getEntityTypeId()}.version_history", + [$this->revision->getEntityTypeId() => $this->revision->id()] + ); + } + + /** + * Prepares a revision to be reverted. + * + * @param \Drupal\Core\Entity\RevisionableInterface $revision + * The revision to be reverted. + * + * @return \Drupal\Core\Entity\RevisionableInterface + * The prepared revision ready to be stored. + */ + protected function prepareRevision(RevisionableInterface $revision) { + $revision->setNewRevision(); + $revision->isDefaultRevision(TRUE); + + return $revision; + } + + /** + * Returns a bundle label. + * + * @param \Drupal\Core\Entity\RevisionableInterface $revision + * The entity revision. + * + * @return string + */ + protected function getBundleLabel(RevisionableInterface $revision) { + /** @var \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface $revision */ + $bundle_info = $this->bundleInformation->getBundleInfo($revision->getEntityTypeId()); + return $bundle_info[$revision->bundle()]['label']; + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Form/RevisionableContentEntityForm.php b/sites/all/modules/contrib/dev/entity/src/Form/RevisionableContentEntityForm.php new file mode 100644 index 000000000..00c67c93b --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Form/RevisionableContentEntityForm.php @@ -0,0 +1,159 @@ +getBundleEntity(); + + // Set up default values, if required. + if (!$this->entity->isNew()) { + $this->entity->setRevisionLogMessage(NULL); + } + + if ($bundle_entity instanceof RevisionableEntityBundleInterface) { + // Always use the default revision setting. + $this->entity->setNewRevision($bundle_entity && $bundle_entity->shouldCreateNewRevision()); + } + } + + /** + * Returns the bundle entity of the entity, or NULL if there is none. + * + * @return \Drupal\Core\Entity\EntityInterface|null + */ + protected function getBundleEntity() { + if ($bundle_key = $this->entity->getEntityType()->getKey('bundle')) { + return $this->entity->{$bundle_key}->referencedEntities()[0]; + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function form(array $form, FormStateInterface $form_state) { + $entity_type = $this->entity->getEntityType(); + $bundle_entity = $this->getBundleEntity(); + $account = $this->currentUser(); + + if ($this->operation == 'edit') { + $form['#title'] = $this->t('Edit %bundle_label @label', [ + '%bundle_label' => $bundle_entity ? $bundle_entity->label() : '', + '@label' => $this->entity->label(), + ]); + } + + $form['advanced'] = [ + '#type' => 'vertical_tabs', + '#weight' => 99, + ]; + + // Add a log field if the "Create new revision" option is checked, or if the + // current user has the ability to check that option. + // @todo Could we autogenerate this form by using some widget on the + // revision info field. + $form['revision_information'] = [ + '#type' => 'details', + '#title' => $this->t('Revision information'), + // Open by default when "Create new revision" is checked. + '#open' => $this->entity->isNewRevision(), + '#group' => 'advanced', + '#weight' => 20, + '#access' => $this->entity->isNewRevision() || $account->hasPermission($entity_type->get('admin_permission')), + ]; + + $form['revision_information']['revision'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Create new revision'), + '#default_value' => $this->entity->isNewRevision(), + '#access' => $account->hasPermission($entity_type->get('admin_permission')), + ]; + + // Check the revision log checkbox when the log textarea is filled in. + // This must not happen if "Create new revision" is enabled by default, + // since the state would auto-disable the checkbox otherwise. + if (!$this->entity->isNewRevision()) { + $form['revision_information']['revision']['#states'] = [ + 'checked' => [ + 'textarea[name="revision_log"]' => ['empty' => FALSE], + ], + ]; + } + + $form['revision_information']['revision_log'] = [ + '#type' => 'textarea', + '#title' => $this->t('Revision log message'), + '#rows' => 4, + '#default_value' => $this->entity->getRevisionLogMessage(), + '#description' => $this->t('Briefly describe the changes you have made.'), + ]; + + return parent::form($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + // Save as a new revision if requested to do so. + if (!$form_state->isValueEmpty('revision')) { + $this->entity->setNewRevision(); + } + + $insert = $this->entity->isNew(); + $this->entity->save(); + $context = ['@type' => $this->entity->bundle(), '%info' => $this->entity->label()]; + $logger = $this->logger($this->entity->id()); + $bundle_entity = $this->getBundleEntity(); + $t_args = ['@type' => $bundle_entity ? $bundle_entity->label() : 'None', '%info' => $this->entity->label()]; + + if ($insert) { + $logger->notice('@type: added %info.', $context); + drupal_set_message($this->t('@type %info has been created.', $t_args)); + } + else { + $logger->notice('@type: updated %info.', $context); + drupal_set_message($this->t('@type %info has been updated.', $t_args)); + } + + if ($this->entity->id()) { + $form_state->setValue('id', $this->entity->id()); + $form_state->set('id', $this->entity->id()); + + if ($this->entity->getEntityType()->hasLinkTemplate('collection')) { + $form_state->setRedirectUrl($this->entity->toUrl('collection')); + } + else { + $form_state->setRedirectUrl($this->entity->toUrl('canonical')); + } + } + else { + // In the unlikely case something went wrong on save, the entity will be + // rebuilt and entity form redisplayed. + drupal_set_message($this->t('The entity could not be saved.'), 'error'); + $form_state->setRebuild(); + } + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Plugin/Action/DeleteAction.php b/sites/all/modules/contrib/dev/entity/src/Plugin/Action/DeleteAction.php new file mode 100644 index 000000000..d61983d43 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Plugin/Action/DeleteAction.php @@ -0,0 +1,97 @@ +currentUser = $current_user; + $this->tempStore = $temp_store_factory->get('entity_delete_multiple_confirm'); + + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('user.private_tempstore'), + $container->get('current_user') + ); + } + + /** + * {@inheritdoc} + */ + public function executeMultiple(array $entities) { + /** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */ + $selection = []; + foreach ($entities as $entity) { + $langcode = $entity->language()->getId(); + $selection[$entity->id()][$langcode] = $langcode; + } + $this->tempStore->set($this->currentUser->id(), $selection); + } + + /** + * {@inheritdoc} + */ + public function execute($object = NULL) { + $this->executeMultiple([$object]); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + return $object->access('delete', $account, $return_as_object); + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Plugin/Action/Derivative/DeleteActionDeriver.php b/sites/all/modules/contrib/dev/entity/src/Plugin/Action/Derivative/DeleteActionDeriver.php new file mode 100644 index 000000000..085bd0ee0 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Plugin/Action/Derivative/DeleteActionDeriver.php @@ -0,0 +1,78 @@ +entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static($container->get('entity_type.manager')); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + if (empty($this->derivatives)) { + $definitions = []; + foreach ($this->getParticipatingEntityTypes() as $entity_type_id => $entity_type) { + $definition = $base_plugin_definition; + $definition['label'] = t('Delete @entity_type', ['@entity_type' => $entity_type->getLowercaseLabel()]); + $definition['type'] = $entity_type_id; + $definition['confirm_form_route_name'] = 'entity.' . $entity_type_id . '.delete_multiple_form'; + $definitions[$entity_type_id] = $definition; + } + $this->derivatives = $definitions; + } + + return parent::getDerivativeDefinitions($base_plugin_definition); + } + + /** + * Gets a list of participating entity types. + * + * The list consists of all content entity types with a delete-multiple-form + * link template. + * + * @return \Drupal\Core\Entity\EntityTypeInterface[] + * The participating entity types, keyed by entity type id. + */ + protected function getParticipatingEntityTypes() { + $entity_types = $this->entityTypeManager->getDefinitions(); + $entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) { + return $entity_type->isSubclassOf(ContentEntityInterface::class) && $entity_type->hasLinkTemplate('delete-multiple-form'); + }); + + return $entity_types; + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Plugin/Derivative/RevisionsOverviewDeriver.php b/sites/all/modules/contrib/dev/entity/src/Plugin/Derivative/RevisionsOverviewDeriver.php new file mode 100644 index 000000000..5fcd21b79 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Plugin/Derivative/RevisionsOverviewDeriver.php @@ -0,0 +1,67 @@ +entityTypeManager = $entityTypeManager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + $exclude = ['node']; + + $this->derivatives = []; + foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) { + if (in_array($entity_type_id, $exclude)) { + continue; + } + + if (!$entity_type->hasLinkTemplate('version-history')) { + continue; + } + + $this->derivatives[$entity_type_id] = [ + 'route_name' => "entity.$entity_type_id.version_history", + 'title' => 'Revisions', + 'base_route' => "entity.$entity_type_id.canonical", + 'weight' => 20, + ] + $base_plugin_definition; + } + + return parent::getDerivativeDefinitions($base_plugin_definition); + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Revision/RevisionableContentEntityBase.php b/sites/all/modules/contrib/dev/entity/src/Revision/RevisionableContentEntityBase.php new file mode 100644 index 000000000..a7ec15ee9 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Revision/RevisionableContentEntityBase.php @@ -0,0 +1,30 @@ +getEntityTypeId()] = $this->id(); + } + if (strpos($this->getEntityType()->getLinkTemplate($rel), $this->getEntityTypeId() . '_revision') !== FALSE) { + $uri_route_parameters[$this->getEntityTypeId() . '_revision'] = $this->getRevisionId(); + } + + return $uri_route_parameters; + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Routing/DeleteMultipleRouteProvider.php b/sites/all/modules/contrib/dev/entity/src/Routing/DeleteMultipleRouteProvider.php new file mode 100644 index 000000000..a9ca06e79 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Routing/DeleteMultipleRouteProvider.php @@ -0,0 +1,47 @@ +deleteMultipleFormRoute($entity_type)) { + $routes->add('entity.' . $entity_type->id() . '.delete_multiple_form', $route); + } + + return $routes; + } + + /** + * Returns the delete multiple form route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function deleteMultipleFormRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('delete-multiple-form')) { + $route = new Route($entity_type->getLinkTemplate('delete-multiple-form')); + $route->setDefault('_form', '\Drupal\entity\Form\DeleteMultiple'); + $route->setDefault('entity_type_id', $entity_type->id()); + $route->setRequirement('_permission', $entity_type->getAdminPermission()); + + return $route; + } + } + +} diff --git a/sites/all/modules/contrib/dev/entity/src/Routing/RevisionRouteProvider.php b/sites/all/modules/contrib/dev/entity/src/Routing/RevisionRouteProvider.php new file mode 100644 index 000000000..e3412958c --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/src/Routing/RevisionRouteProvider.php @@ -0,0 +1,127 @@ +id(); + if ($view_route = $this->getRevisionViewRoute($entity_type)) { + $collection->add("entity.$entity_type_id.revision", $view_route); + } + if ($view_route = $this->getRevisionRevertRoute($entity_type)) { + $collection->add("entity.$entity_type_id.revision_revert_form", $view_route); + } + + if ($view_route = $this->getRevisionHistoryRoute($entity_type)) { + $collection->add("entity.$entity_type_id.version_history", $view_route); + } + + return $collection; + } + + /** + * Gets the entity revision view route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getRevisionViewRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('revision')) { + $entity_type_id = $entity_type->id(); + $route = new Route($entity_type->getLinkTemplate('revision')); + $route->addDefaults([ + '_controller' => '\Drupal\Core\Entity\Controller\EntityViewController::viewRevision', + '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::title', + ]); + $route->addRequirements([ + '_entity_access_revision' => "$entity_type_id.view", + ]); + $route->setOption('parameters', [ + $entity_type->id() => [ + 'type' => 'entity:' . $entity_type->id(), + ], + $entity_type->id() . '_revision' => [ + 'type' => 'entity_revision:' . $entity_type->id(), + ], + ]); + return $route; + } + } + + /** + * Gets the entity revision revert route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getRevisionRevertRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('revision-revert-form')) { + $entity_type_id = $entity_type->id(); + $route = new Route($entity_type->getLinkTemplate('revision-revert-form')); + $route->addDefaults([ + '_form' => '\Drupal\entity\Form\RevisionRevertForm', + 'title' => 'Revert to earlier revision', + ]); + $route->addRequirements([ + '_entity_access_revision' => "$entity_type_id.update", + ]); + $route->setOption('parameters', [ + $entity_type->id() => [ + 'type' => 'entity:' . $entity_type->id(), + ], + $entity_type->id() . '_revision' => [ + 'type' => 'entity_revision:' . $entity_type->id(), + ], + ]); + return $route; + } + } + + /** + * Gets the entity revision version history route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getRevisionHistoryRoute($entity_type) { + if ($entity_type->hasLinkTemplate('version-history')) { + $entity_type_id = $entity_type->id(); + $route = new Route($entity_type->getLinkTemplate('version-history')); + $route->addDefaults([ + '_controller' => '\Drupal\entity\Controller\RevisionOverviewController::revisionOverviewController', + '_title' => 'Revisions', + ]); + $route->setRequirement('_entity_access_revision', "$entity_type_id.list"); + $route->setOption('entity_type_id', $entity_type->id()); + $route->setOption('parameters', [ + $entity_type->id() => [ + 'type' => 'entity:' . $entity_type->id(), + ], + ]); + return $route; + } + } + +} diff --git a/sites/all/modules/contrib/dev/entity/tests/Kernel/RevisionOverviewIntegrationTest.php b/sites/all/modules/contrib/dev/entity/tests/Kernel/RevisionOverviewIntegrationTest.php new file mode 100644 index 000000000..118fea62f --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/Kernel/RevisionOverviewIntegrationTest.php @@ -0,0 +1,51 @@ +installSchema('system', 'router'); + + \Drupal::service('router.builder')->rebuild(); + } + + public function testIntegration() { + /** @var \Drupal\Core\Menu\LocalTaskManagerInterface $local_tasks_manager */ + $local_tasks_manager = \Drupal::service('plugin.manager.menu.local_task'); + + $tasks = $local_tasks_manager->getDefinitions(); + $this->assertArrayHasKey('entity.revisions_overview:entity_test_enhanced', $tasks); + $this->assertArrayNotHasKey('entity.revisions_overview:node', $tasks, 'Node should have been excluded because it provides their own'); + + $this->assertEquals('entity.entity_test_enhanced.version_history', $tasks['entity.revisions_overview:entity_test_enhanced']['route_name']); + $this->assertEquals('entity.entity_test_enhanced.canonical', $tasks['entity.revisions_overview:entity_test_enhanced']['base_route']); + + /** @var \Drupal\Core\Routing\RouteProviderInterface $route_provider */ + $route_provider = \Drupal::service('router.route_provider'); + + $route = $route_provider->getRouteByName('entity.entity_test_enhanced.version_history'); + $this->assertInstanceOf(Route::class, $route); + $this->assertEquals('\Drupal\entity\Controller\RevisionOverviewController::revisionOverviewController', $route->getDefault('_controller')); + } + +} diff --git a/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/config/schema/entity_module_test.schema.yml b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/config/schema/entity_module_test.schema.yml new file mode 100644 index 000000000..eb1c5a089 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/config/schema/entity_module_test.schema.yml @@ -0,0 +1,16 @@ +entity_module_test.entity_test_enhanced_bundle.*: + type: config_entity + label: 'Entity test with enhancements - Bundle' + mapping: + id: + type: string + label: 'Type' + label: + type: label + label: 'Label' + description: + type: text + label: 'Description' + new_revision: + type: boolean + label: 'New revision' diff --git a/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.info.yml b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.info.yml new file mode 100644 index 000000000..d25bd5ff4 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.info.yml @@ -0,0 +1,9 @@ +name: Entity test +type: module +# core: 8.x + +# Information added by Drupal.org packaging script on 2016-06-23 +version: '8.x-1.0-alpha3+2-dev' +core: '8.x' +project: 'entity' +datestamp: 1466693538 diff --git a/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.links.task.yml b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.links.task.yml new file mode 100644 index 000000000..e306c10c9 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.links.task.yml @@ -0,0 +1,9 @@ +entity.entity_test_enhanced.canonical: + title: View + route_name: entity.entity_test_enhanced.canonical + base_route: entity.entity_test_enhanced.canonical + +entity.entity_module_test.edit_form: + title: Edit + route_name: entity.entity_test_enhanced.edit_form + base_route: entity.entity_test_enhanced.canonical diff --git a/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.permissions.yml b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.permissions.yml new file mode 100644 index 000000000..b9ff91d5b --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.permissions.yml @@ -0,0 +1,7 @@ +'administer entity_test_enhanced': + title: 'Administer entity_test_enhanced' + 'restrict access': TRUE + +'view all entity_test_enhanced revisions': + title: 'View all entity_test_enhanced revisions' + 'restrict access': TRUE diff --git a/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.routing.yml b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.routing.yml new file mode 100644 index 000000000..3724a6524 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/entity_module_test.routing.yml @@ -0,0 +1,7 @@ +entity.entity_test_enhanced.collection: + path: '/entity_test_enhanced' + defaults: + _entity_list: 'entity_test_enhanced' + _title: 'Entity test with enhancements' + requirements: + _permission: 'administer entity_test_enhanced' diff --git a/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php new file mode 100644 index 000000000..c2cd80882 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php @@ -0,0 +1,76 @@ +setLabel('Name') + ->setRevisionable(TRUE) + ->setDisplayOptions('view', [ + 'label' => 'hidden', + 'type' => 'string', + 'weight' => -5, + ]); + + return $fields; + } + +} diff --git a/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/src/Entity/EnhancedEntityBundle.php b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/src/Entity/EnhancedEntityBundle.php new file mode 100644 index 000000000..43780746d --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/modules/entity_module_test/src/Entity/EnhancedEntityBundle.php @@ -0,0 +1,81 @@ +description; + } + + /** + * {@inheritdoc} + */ + public function setDescription($description) { + $this->description = $description; + return $this; + } + + /** + * {@inheritdoc} + */ + public function shouldCreateNewRevision() { + return $this->new_revision; + } + +} diff --git a/sites/all/modules/contrib/dev/entity/tests/src/Functional/DeleteMultipleFormTest.php b/sites/all/modules/contrib/dev/entity/tests/src/Functional/DeleteMultipleFormTest.php new file mode 100644 index 000000000..213113b9b --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/src/Functional/DeleteMultipleFormTest.php @@ -0,0 +1,81 @@ + 'default', + 'label' => 'Default', + ])->save(); + $this->account = $this->drupalCreateUser(['administer entity_test_enhanced']); + $this->drupalLogin($this->account); + } + + /** + * Tests the add page. + */ + public function testForm() { + $entities = []; + $selection = []; + for ($i = 0; $i < 2; $i++) { + $entity = EnhancedEntity::create([ + 'type' => 'default', + ]); + $entity->save(); + $entities[$entity->id()] = $entity; + + $langcode = $entity->language()->getId(); + $selection[$entity->id()][$langcode] = $langcode; + } + // Add the selection to the tempstore just like DeleteAction would. + $tempstore = \Drupal::service('user.private_tempstore')->get('entity_delete_multiple_confirm'); + $tempstore->set($this->account->id(), $selection); + + $this->drupalGet('/entity_test_enhanced/delete'); + $assert = $this->assertSession(); + $assert->statusCodeEquals(200); + $assert->elementTextContains('css', '.page-title', 'Are you sure you want to delete these items?'); + $delete_button = $this->getSession()->getPage()->findButton('Delete'); + $delete_button->click(); + $assert = $this->assertSession(); + $assert->addressEquals('/entity_test_enhanced'); + $assert->responseContains('Deleted 2 items.'); + + \Drupal::entityTypeManager()->getStorage('entity_test_enhanced')->resetCache(); + $remaining_entities = EnhancedEntity::loadMultiple(array_keys($selection)); + $this->assertEmpty($remaining_entities); + } + +} diff --git a/sites/all/modules/contrib/dev/entity/tests/src/Functional/RevisionRouteAccessTest.php b/sites/all/modules/contrib/dev/entity/tests/src/Functional/RevisionRouteAccessTest.php new file mode 100644 index 000000000..8eddca491 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/src/Functional/RevisionRouteAccessTest.php @@ -0,0 +1,97 @@ + 'default', + 'label' => 'Default', + ])->save(); + + $this->placeBlock('local_tasks_block'); + $this->placeBlock('system_breadcrumb_block'); + + $this->account = $this->drupalCreateUser([ + 'administer entity_test_enhanced', + 'view all entity_test_enhanced revisions', + ]); + + $this->drupalLogin($this->account); + } + + /** + * Test enhanced entity revision routes access. + */ + public function testRevisionRouteAccess() { + $entity = EnhancedEntity::create([ + 'name' => 'rev 1', + 'type' => 'default', + ]); + $entity->save(); + + $revision = clone $entity; + $revision->name->value = 'rev 2'; + $revision->setNewRevision(TRUE); + $revision->isDefaultRevision(FALSE); + $revision->save(); + + $this->drupalGet('/entity_test_enhanced/1/revisions'); + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->responseContains('Revisions'); + $collection_link = $this->getSession()->getPage()->findLink('Entity test with enhancements'); + $collection_link->click(); + $this->assertSession()->addressEquals('/entity_test_enhanced'); + $this->assertSession()->responseContains('Edit'); + $edit_link = $this->getSession()->getPage()->findLink('Edit'); + $edit_link->click(); + $this->assertSession()->addressEquals('/entity_test_enhanced/1/edit'); + // Check if we have revision tab link on edit page. + $this->getSession()->getPage()->findLink('Revisions')->click(); + $this->assertSession()->addressEquals('/entity_test_enhanced/1/revisions'); + $this->drupalGet('/entity_test_enhanced/1/revisions/2/view'); + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->responseContains('rev 2'); + $revisions_link = $this->getSession()->getPage()->findLink('Revisions'); + $revisions_link->click(); + $this->assertSession()->addressEquals('/entity_test_enhanced/1/revisions'); + $this->assertSession()->statusCodeEquals(200); + } + +} diff --git a/sites/all/modules/contrib/dev/entity/tests/src/Kernel/DeleteActionTest.php b/sites/all/modules/contrib/dev/entity/tests/src/Kernel/DeleteActionTest.php new file mode 100644 index 000000000..140473f91 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/src/Kernel/DeleteActionTest.php @@ -0,0 +1,83 @@ +installEntitySchema('user'); + $this->installEntitySchema('entity_test_enhanced'); + $this->installSchema('system', ['key_value_expire', 'sequences']); + + $bundle = EnhancedEntityBundle::create([ + 'id' => 'default', + 'label' => 'Default', + ]); + $bundle->save(); + + $this->user = User::create([ + 'name' => 'username', + 'status' => 1, + ]); + $this->user->save(); + \Drupal::service('current_user')->setAccount($this->user); + } + + public function testAction() { + /** @var \Drupal\system\ActionConfigEntityInterface $action */ + $action = Action::create([ + 'id' => 'enhanced_entity_delete_action', + 'label' => 'Delete enhanced entity', + 'plugin' => 'entity_delete_action:entity_test_enhanced', + ]); + $status = $action->save(); + $this->assertEquals(SAVED_NEW, $status); + $this->assertInstanceOf(DeleteAction::class, $action->getPlugin()); + + $entities = []; + for ($i = 0; $i < 2; $i++) { + $entity = EnhancedEntity::create([ + 'type' => 'default', + ]); + $entity->save(); + $entities[$entity->id()] = $entity; + } + + $action->execute($entities); + // Confirm that the entity ids and langcodes are now in the tempstore. + $tempstore = \Drupal::service('user.private_tempstore')->get('entity_delete_multiple_confirm'); + $selection = $tempstore->get($this->user->id()); + $this->assertEquals(array_keys($entities), array_keys($selection)); + $this->assertEquals([['en' => 'en'], ['en' => 'en']], array_values($selection)); + } + +} diff --git a/sites/all/modules/contrib/dev/entity/tests/src/Kernel/RevisionBasicUITest.php b/sites/all/modules/contrib/dev/entity/tests/src/Kernel/RevisionBasicUITest.php new file mode 100644 index 000000000..f282ac415 --- /dev/null +++ b/sites/all/modules/contrib/dev/entity/tests/src/Kernel/RevisionBasicUITest.php @@ -0,0 +1,164 @@ +installEntitySchema('user'); + $this->installEntitySchema('entity_test_enhanced'); + $this->installSchema('system', 'router'); + $this->installConfig(['system']); + + $bundle = EnhancedEntityBundle::create([ + 'id' => 'default', + 'label' => 'Default', + ]); + $bundle->save(); + + \Drupal::service('router.builder')->rebuild(); + } + + /** + * Tests the revision history controller. + */ + public function testRevisionHistory() { + $entity = EnhancedEntity::create([ + 'name' => 'rev 1', + 'type' => 'default', + ]); + $entity->save(); + + $revision = clone $entity; + $revision->name->value = 'rev 2'; + $revision->setNewRevision(TRUE); + $revision->isDefaultRevision(FALSE); + $revision->save(); + + /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */ + $http_kernel = \Drupal::service('http_kernel'); + $request = Request::create($revision->url('version-history')); + $response = $http_kernel->handle($request); + $this->assertEquals(403, $response->getStatusCode()); + + $role = Role::create(['id' => 'test_role']); + $role->grantPermission('view all entity_test_enhanced revisions'); + $role->grantPermission('administer entity_test_enhanced'); + $role->save(); + + $user = User::create([ + 'name' => 'Test user', + ]); + $user->addRole($role->id()); + \Drupal::service('account_switcher')->switchTo($user); + + $request = Request::create($revision->url('version-history')); + $response = $http_kernel->handle($request); + $this->assertEquals(200, $response->getStatusCode()); + + // This ensures that the default revision is still the first revision. + $this->assertTrue(strpos($response->getContent(), 'entity_test_enhanced/1/revisions/2/view') !== FALSE); + $this->assertTrue(strpos($response->getContent(), 'entity_test_enhanced/1') !== FALSE); + + // Publish a new revision. + $revision = clone $entity; + $revision->name->value = 'rev 3'; + $revision->setNewRevision(TRUE); + $revision->isDefaultRevision(TRUE); + $revision->save(); + + $request = Request::create($revision->url('version-history')); + $response = $http_kernel->handle($request); + $this->assertEquals(200, $response->getStatusCode()); + + // The first revision row should now include a revert link. + $this->assertTrue(strpos($response->getContent(), 'entity_test_enhanced/1/revisions/1/revert') !== FALSE); + } + + public function testRevisionView() { + $entity = EnhancedEntity::create([ + 'name' => 'rev 1', + 'type' => 'default', + ]); + $entity->save(); + + $revision = clone $entity; + $revision->name->value = 'rev 2'; + $revision->setNewRevision(TRUE); + $revision->isDefaultRevision(FALSE); + $revision->save(); + + /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */ + $http_kernel = \Drupal::service('http_kernel'); + $request = Request::create($revision->url('revision')); + $response = $http_kernel->handle($request); + $this->assertEquals(403, $response->getStatusCode()); + + $role = Role::create(['id' => 'test_role']); + $role->grantPermission('view all entity_test_enhanced revisions'); + $role->grantPermission('administer entity_test_enhanced'); + $role->save(); + + $user = User::create([ + 'name' => 'Test user', + ]); + $user->addRole($role->id()); + \Drupal::service('account_switcher')->switchTo($user); + + $request = Request::create($revision->url('revision')); + $response = $http_kernel->handle($request); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertNotContains('rev 1', $response->getContent()); + $this->assertContains('rev 2', $response->getContent()); + } + + public function testRevisionRevert() { + $entity = EnhancedEntity::create([ + 'name' => 'rev 1', + 'type' => 'entity_test_enhance', + ]); + $entity->save(); + $entity->name->value = 'rev 2'; + $entity->setNewRevision(TRUE); + $entity->isDefaultRevision(TRUE); + $entity->save(); + + $role = Role::create(['id' => 'test_role']); + $role->grantPermission('administer entity_test_enhanced'); + $role->grantPermission('revert all entity_test_enhanced revisions'); + $role->save(); + + $user = User::create([ + 'name' => 'Test user', + ]); + $user->addRole($role->id()); + \Drupal::service('account_switcher')->switchTo($user); + + /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */ + $http_kernel = \Drupal::service('http_kernel'); + $request = Request::create($entity->url('revision-revert-form')); + $response = $http_kernel->handle($request); + $this->assertEquals(200, $response->getStatusCode()); + } + +} diff --git a/sites/all/modules/contrib/mail/maillog/LICENSE.txt b/sites/all/modules/contrib/mail/maillog/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General +Public License instead of this License. diff --git a/sites/all/modules/contrib/mail/maillog/README.txt b/sites/all/modules/contrib/mail/maillog/README.txt new file mode 100644 index 000000000..afe807a56 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/README.txt @@ -0,0 +1,4 @@ + +This module defines drupal_mail_wrapper(), which takes over the handling of mail sending. This wrapper creates a node of type 'Logged Mail', a node type created by the module, sends the e-mail, and displays the e-mail and its details. All 3 tasks can be enabled/disabled from 'admin/settings/maillog'. + +The invoking of the handler depends on a system variable 'smtp_library'. Unfortunately, some modules may change the value of this variable, rendering the module dysfunctional. Simply disabling and re-enabling the module should fix this problem. Check 'admin/reports/status' to see the status of the SMTP library. diff --git a/sites/all/modules/contrib/mail/maillog/config/install/maillog.settings.yml b/sites/all/modules/contrib/mail/maillog/config/install/maillog.settings.yml new file mode 100644 index 000000000..460534a91 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/config/install/maillog.settings.yml @@ -0,0 +1,3 @@ +send: true +log: true +verbose: true diff --git a/sites/all/modules/contrib/mail/maillog/config/install/views.view.maillog_overview.yml b/sites/all/modules/contrib/mail/maillog/config/install/views.view.maillog_overview.yml new file mode 100644 index 000000000..8b7df5361 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/config/install/views.view.maillog_overview.yml @@ -0,0 +1,441 @@ +langcode: en +status: true +dependencies: + config: + - system.menu.admin + enforced: + module: + - maillog + module: + - user +id: maillog_overview +label: 'Maillog overview' +module: views +description: '' +tag: '' +base_table: maillog +base_field: idmaillog +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 1 + display_options: + access: + type: perm + options: + perm: 'view maillog' + cache: + type: none + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: full + options: + items_per_page: 10 + offset: 0 + id: 0 + total_pages: null + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 20, 40, 60' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + tags: + previous: '‹ previous' + next: 'next ›' + first: '« first' + last: 'last »' + quantity: 9 + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + override: true + sticky: false + caption: '' + summary: '' + description: '' + columns: + idmaillog: idmaillog + sent_date: sent_date + subject: subject + header_from: header_from + header_to: header_to + info: + idmaillog: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + sent_date: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + subject: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + header_from: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + header_to: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + default: '-1' + empty_table: true + row: + type: fields + fields: + idmaillog: + id: idmaillog + table: maillog + field: idmaillog + relationship: none + group_type: group + admin_label: '' + label: '#' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: null + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: 'There are currently no mails to report.' + hide_empty: false + empty_zero: false + hide_alter_empty: true + plugin_id: standard + sent_date: + id: sent_date + table: maillog + field: sent_date + relationship: none + group_type: group + admin_label: '' + label: Date + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: null + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + date_format: short + custom_date_format: '' + timezone: '' + plugin_id: date + subject: + id: subject + table: maillog + field: subject + relationship: none + group_type: group + admin_label: '' + label: Subject + exclude: false + alter: + alter_text: false + text: '' + make_link: true + path: '/maillog/details/{{idmaillog}}' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: null + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + plugin_id: standard + header_from: + id: header_from + table: maillog + field: header_from + relationship: none + group_type: group + admin_label: '' + label: From + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: null + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + plugin_id: standard + header_to: + id: header_to + table: maillog + field: header_to + relationship: none + group_type: group + admin_label: '' + label: To + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: null + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + plugin_id: standard + filters: { } + sorts: + sent_date: + id: sent_date + table: maillog + field: sent_date + relationship: none + group_type: group + admin_label: '' + order: DESC + exposed: false + expose: + label: '' + granularity: second + plugin_id: date + title: 'Maillog overview' + header: { } + footer: { } + empty: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: '' + empty: true + tokenize: false + content: 'There are no mail logs in the database.' + plugin_id: text_custom + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + contexts: + - 'languages:language_interface' + - url.query_args + - user.permissions + cacheable: false + max-age: 0 + tags: { } + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: 1 + display_options: + path: admin/reports/maillog + menu: + type: normal + title: Maillog + description: '' + parent: system.admin_reports + weight: 0 + context: '0' + menu_name: admin + display_extenders: { } + cache_metadata: + contexts: + - 'languages:language_interface' + - url.query_args + - user.permissions + cacheable: false + max-age: 0 + tags: { } diff --git a/sites/all/modules/contrib/mail/maillog/config/schema/maillog.schema.yml b/sites/all/modules/contrib/mail/maillog/config/schema/maillog.schema.yml new file mode 100644 index 000000000..ac265aed1 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/config/schema/maillog.schema.yml @@ -0,0 +1,12 @@ +maillog.settings: + type: config_object + mapping: + send: + type: boolean + label: Send emails + log: + type: boolean + label: Log emails + verbose: + type: boolean + label: Display mails diff --git a/sites/all/modules/contrib/mail/maillog/maillog.info.yml b/sites/all/modules/contrib/mail/maillog/maillog.info.yml new file mode 100644 index 000000000..a93f4b0eb --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/maillog.info.yml @@ -0,0 +1,16 @@ +name: 'Maillog / Mail Developer' +description: 'Utility to log all Mails for debugging purposes. It is possible to suppress mail delivery for e.g. dev or staging systems.' +package: 'Mail' +# core: 8.x +type: module + +dependencies: +- views + +configure: maillog.settings + +# Information added by Drupal.org packaging script on 2016-08-05 +version: '8.x-1.x-dev' +core: '8.x' +project: 'maillog' +datestamp: 1470431946 diff --git a/sites/all/modules/contrib/mail/maillog/maillog.install b/sites/all/modules/contrib/mail/maillog/maillog.install new file mode 100644 index 000000000..775cac975 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/maillog.install @@ -0,0 +1,93 @@ +getEditable('system.mail'); + $config->set('interface.default', 'maillog'); + $config->save(); +} + +/** + * Implements hook_uninstall(). + */ +function maillog_uninstall() { + $config = \Drupal::configFactory()->getEditable('system.mail'); + + // Restore the mail configuration to php_mail if it currently uses maillog. + if ($config->get('interface.default') == 'maillog') { + $config->set('interface.default', 'php_mail'); + $config->save(); + } +} + +/** + * Implements hook_schema(). + */ +function maillog_schema() { + $schema['maillog'] = array( + 'description' => "Stores outgoing e-mail details for nodes of type 'maillog'.", + 'fields' => array( + 'idmaillog' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => "The mail_log {node}.nid", + ), + 'header_message_id' => array( + 'type' => 'varchar', + 'length' => 255, + 'default' => '', + 'description' => "The 'message-id' field of the e-mail.", + ), + 'header_from' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => "The 'From' field of the e-mail.", + ), + 'header_to' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => "The 'To' field of the e-mail.", + ), + 'header_reply_to' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => "The 'Reply-To' field of the e-mail.", + ), + 'header_all' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => "The 'Header' field of the e-mail.", + ), + 'subject' => array( + 'description' => "The 'Subject' fieldof the e-mail.", + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'body' => array( + 'description' => 'The body of this version.', + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + 'sent_date' => array( + 'description' => 'The Unix timestamp when the mail was sent.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('idmaillog'), + ); + + return $schema; +} diff --git a/sites/all/modules/contrib/mail/maillog/maillog.links.menu.yml b/sites/all/modules/contrib/mail/maillog/maillog.links.menu.yml new file mode 100644 index 000000000..4b4b8b7e0 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/maillog.links.menu.yml @@ -0,0 +1,5 @@ +maillog.settings: + title: 'Maillog Settings' + parent: system.admin_config_development + description: 'Configure the settings of Maillog module.' + route_name: maillog.settings diff --git a/sites/all/modules/contrib/mail/maillog/maillog.module b/sites/all/modules/contrib/mail/maillog/maillog.module new file mode 100644 index 000000000..54c6fb8f5 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/maillog.module @@ -0,0 +1,28 @@ +' . t('About') . '

      '; + $output .= '

      ' . t('Maillog module provides an opportunity to save the mail log for debugging purposes.') . '

      '; + $output .= '

      ' . t('Uses') . '

      '; + $output .= '
      '; + $output .= '
      ' . t('Saves the mail log') . '
      '; + $output .= '
      ' . t('View integration') . '
      '; + $output .= '
      ' . t('Displays verbose output of the mail for users with permission "view maillog"') . '
      '; + $output .= '
      ' . t('Suppress email delivery') . '
      '; + $output .= '
      '; + return $output; + } +} diff --git a/sites/all/modules/contrib/mail/maillog/maillog.permissions.yml b/sites/all/modules/contrib/mail/maillog/maillog.permissions.yml new file mode 100644 index 000000000..2dda83fe6 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/maillog.permissions.yml @@ -0,0 +1,10 @@ +view maillog: + title: 'View Maillog' + description: 'Allow users to view messages when being sent and all previously sent and logged mails.' + restrict access: true +delete maillog: + title: 'Delete entries from the log' + description: 'Allow users to delete logged mails.' +administer maillog: + title: 'Administer Maillog' + description: 'Allow users to change Maillog settings' diff --git a/sites/all/modules/contrib/mail/maillog/maillog.routing.yml b/sites/all/modules/contrib/mail/maillog/maillog.routing.yml new file mode 100644 index 000000000..c797f540f --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/maillog.routing.yml @@ -0,0 +1,30 @@ +maillog.settings: + path: '/admin/config/development/maillog' + defaults: + _form: '\Drupal\maillog\Form\MaillogSettingsForm' + _title: 'Maillog Settings' + requirements: + _permission: 'administer maillog' + +maillog.delete: + path: '/maillog/delete/{maillog_id}' + defaults: + _controller: '\Drupal\maillog\Controller\MaillogController::delete' + requirements: + _permission: 'delete maillog' + _csrf_token: 'TRUE' + +maillog.clear_log: + path: '/maillog/delete/all' + defaults: + _form: '\Drupal\maillog\Form\MaillogClearConfirmForm' + _title: 'Clear log' + requirements: + _permission: 'administer maillog' + +maillog.details: + path: '/maillog/details/{maillog_id}' + defaults: + _controller: '\Drupal\maillog\Controller\MaillogController::details' + requirements: + _permission: 'view maillog' diff --git a/sites/all/modules/contrib/mail/maillog/maillog.views.inc b/sites/all/modules/contrib/mail/maillog/maillog.views.inc new file mode 100644 index 000000000..70154ae55 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/maillog.views.inc @@ -0,0 +1,192 @@ + 'idmaillog', + 'title' => t('Maillog'), + 'help' => t("This table contains the logged e-mails."), + 'weight' => -10, + ); + + $data['maillog']['idmaillog'] = array( + 'title' => t('Maillog_ID'), + 'help' => t('The primary key of the maillog table.'), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['maillog']['header_message_id'] = array( + 'title' => t('Message_ID'), + 'help' => t("The 'Message_ID' e-mail address."), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['maillog']['header_from'] = array( + 'title' => t('From'), + 'help' => t("The 'From' field of the e-mail address."), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['maillog']['header_to'] = array( + 'title' => t('To'), + 'help' => t("The 'To' field of the e-mail address."), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['maillog']['header_reply_to'] = array( + 'title' => t('Reply To'), + 'help' => t("The 'Reply-To' field of the e-mail address."), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['maillog']['header_all'] = array( + 'title' => t('Header'), + 'help' => t("The 'Header' field of the e-mail."), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['maillog']['body'] = array( + 'title' => t('Body'), + 'help' => t("The 'Body' field of the e-mail."), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['maillog']['subject'] = array( + 'title' => t('Subject'), + 'help' => t("The 'Subject' field of the e-mail."), + 'field' => array( + 'id' => 'standard', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'standard', + ), + 'filter' => array( + 'id' => 'string', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['maillog']['sent_date'] = array( + 'title' => t('Date'), + 'help' => t("The 'Date' field of the e-mail."), + 'field' => array( + 'id' => 'date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'id' => 'date', + ), + 'filter' => array( + 'id' => 'date', + ), + 'argument' => array( + 'id' => 'string', + ), + ); + + $data['maillog']['delete_maillog'] = array( + 'field' => array( + 'title' => t('Delete link'), + 'help' => t('Provide a simple link to delete an eMail entry from the maillog table.'), + 'id' => 'maillog_field_delete', + 'real field' => 'idmaillog', + ), + ); + return $data; +} diff --git a/sites/all/modules/contrib/mail/maillog/src/Controller/MaillogController.php b/sites/all/modules/contrib/mail/maillog/src/Controller/MaillogController.php new file mode 100644 index 000000000..6735996bc --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/src/Controller/MaillogController.php @@ -0,0 +1,132 @@ +database = $database; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('database')); + } + + /** + * Page callback - Get the Maillog Entry. + * + * @param int $maillog_id + * The Maillog ID + * + * @return array + * The output fields + */ + public function details($maillog_id) { + $maillog_entry = $this->getMaillogEntry(intval($maillog_id)); + + if (!$maillog_entry) { + throw new NotFoundHttpException(); + } + + $output = array(); + + $output['#title'] = $maillog_entry['subject']; + + $output['header_from'] = array( + '#title' => t('From'), + '#type' => 'item', + '#markup' => SafeMarkup::checkPlain($maillog_entry['header_from']), + ); + $output['header_to'] = array( + '#title' => t('To'), + '#type' => 'item', + '#markup' => SafeMarkup::checkPlain($maillog_entry['header_to']), + ); + $output['header_reply_to'] = array( + '#title' => t('Reply to'), + '#type' => 'item', + '#markup' => SafeMarkup::checkPlain($maillog_entry['header_reply_to']), + ); + $output['header_all'] = array( + '#title' => t('All'), + '#type' => 'item', + '#markup' => '
      ',
      +    );
      +
      +    foreach ($maillog_entry['header_all'] as $header_all_name => $header_all_value) {
      +      $output['header_all']['#markup'] .= SafeMarkup::checkPlain($header_all_name) . ': ' . SafeMarkup::checkPlain($header_all_value) . '
      '; + } + + $output['header_all']['#markup'] .= '
      '; + + $output['body'] = array( + '#title' => t('Body'), + '#type' => 'item', + '#markup' => '
      ' . SafeMarkup::checkPlain($maillog_entry['body']) . '
      ', + ); + + return $output; + } + + /** + * Page Callback - Delete a specific maillog entry. + * + * @param int $maillog_id + * The maillog ID. + */ + public function delete($maillog_id) { + $idmaillog = intval($maillog_id); + $this->database->query("DELETE FROM {maillog} WHERE idmaillog = :id", array(':id' => $idmaillog)); + drupal_set_message(t('Mail with ID @idmaillog has been deleted!', array('@idmaillog' => $idmaillog))); + + return $this->redirect('view.maillog_overview.page_1'); + } + + /** + * Loads the Maillog Entry. + * + * @param int $maillog_id + * The maillog ID. + * + * @return array + * Maillog entry as Array + */ + protected function getMaillogEntry($maillog_id) { + $result = $this->database->query("SELECT idmaillog, header_from, header_to, header_reply_to, header_all, subject, body FROM {maillog} WHERE idmaillog=:id", array( + ':id' => $maillog_id, + )); + + if ($maillog = $result->fetchAssoc()) { + // Unserialize values. + $maillog['header_all'] = unserialize($maillog['header_all']); + } + return $maillog; + } +} diff --git a/sites/all/modules/contrib/mail/maillog/src/Form/MaillogClearConfirmForm.php b/sites/all/modules/contrib/mail/maillog/src/Form/MaillogClearConfirmForm.php new file mode 100644 index 000000000..352a51b06 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/src/Form/MaillogClearConfirmForm.php @@ -0,0 +1,64 @@ +t('All maillog database entries will be deleted. This action cannot be undone.'); + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->t('Are you sure you want to clear all the maillog entries?'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('maillog.settings'); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Clear'); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + Database::getConnection('default')->truncate('maillog')->execute(); + drupal_set_message($this->t("All maillog entries have been deleted.")); + $form_state->setRedirect('maillog.settings'); + } + +} diff --git a/sites/all/modules/contrib/mail/maillog/src/Form/MaillogSettingsForm.php b/sites/all/modules/contrib/mail/maillog/src/Form/MaillogSettingsForm.php new file mode 100644 index 000000000..25c78be35 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/src/Form/MaillogSettingsForm.php @@ -0,0 +1,108 @@ +config('maillog.settings'); + + $form = array(); + + $form['clear_maillog'] = array( + '#type' => 'fieldset', + '#title' => $this->t('Clear Maillog'), + ); + + $form['clear_maillog']['clear'] = array( + '#type' => 'submit', + '#value' => $this->t('Clear all maillog entries'), + '#submit' => ['::clearLog'], + ); + + $form['maillog_send'] = array( + '#type' => 'checkbox', + '#title' => t("Allow the e-mails to be sent."), + '#default_value' => $config->get('send'), + ); + + $form['maillog_log'] = array( + '#type' => 'checkbox', + '#title' => t("Create table entries in maillog table for each e-mail."), + '#default_value' => $config->get('log'), + ); + + $form['maillog_verbose'] = array( + '#type' => 'checkbox', + '#title' => t("Display the e-mails on page."), + '#default_value' => $config->get('verbose'), + '#description' => $this->t('If enabled, anonymous users with permissions will see any verbose output mail.'), + ); + + /*if (\Drupal::moduleHandler()->moduleExists('mimemail')) { + $engines = mimemail_get_engines(); + // maillog will be unset, because ist would cause an recursion + unset($engines['maillog']); + $form['maillog_engine'] = array( + '#type' => 'select', + '#title' => t("Select the mailengine which should be used."), + '#default_value' => $config->get('maillog_engine'), + '#options' => $engines, + ); + }*/ + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $this->config('maillog.settings') + ->set('send', $form_state->getValue('maillog_send')) + ->set('log', $form_state->getValue('maillog_log')) + ->set('verbose', $form_state->getValue('maillog_verbose'))->save(); + + parent::submitForm($form, $form_state); + + if ($this->config('maillog.settings')->get('verbose') == TRUE) { + drupal_set_message(t('Any user having the permission "view maillog" will see output of any mail that is sent.'), 'warning'); + } + } + + /** + * Clear all the maillog entries. + */ + public function clearLog(array $form, FormStateInterface $form_state) { + $form_state->setRedirect('maillog.clear_log'); + } + +} diff --git a/sites/all/modules/contrib/mail/maillog/src/Plugin/Mail/Maillog.php b/sites/all/modules/contrib/mail/maillog/src/Plugin/Mail/Maillog.php new file mode 100755 index 000000000..55c0e8933 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/src/Plugin/Mail/Maillog.php @@ -0,0 +1,104 @@ +format($message); + } + + /** + * {@inheritdoc} + */ + public function mail(array $message) { + $config = \Drupal::configFactory()->get('maillog.settings'); + // Log the e-mail + if ($config->get('log')) { + $record = new \stdClass; + + // In case the subject/from/to is already encoded, decode with + // Unicode::mimeHeaderDecode(). + $record->header_message_id = isset($message['headers']['Message-ID']) ? $message['headers']['Message-ID'] : NULL; + $record->subject = $message['subject']; + $record->subject = Unicode::substr(Unicode::mimeHeaderDecode($record->subject), 0, 255); + $record->body = $message['body']; + $record->header_from = isset($message['from']) ? $message['from'] : NULL; + $record->header_from = Unicode::mimeHeaderDecode($record->header_from); + + $header_to = array(); + if (isset($message['to'])) { + if (is_array($message['to'])) { + foreach ($message['to'] as $value) { + $header_to[] = Unicode::mimeHeaderDecode($value); + } + } + else { + $header_to[] = Unicode::mimeHeaderDecode($message['to']); + } + } + $record->header_to = implode(', ', $header_to); + + $record->header_reply_to = isset($message['headers']['Reply-To']) ? $message['headers']['Reply-To'] : ''; + $record->header_all = serialize($message['headers']); + $record->sent_date = REQUEST_TIME; + + Database::getConnection()->insert('maillog') + ->fields((array) $record) + ->execute(); + } + + // Display the e-mail if the verbose is enabled. + if ($config->get('verbose') && \Drupal::currentUser()->hasPermission('view maillog')) { + + // Print the message. + $header_output = print_r($message['headers'], TRUE); + $output = t('A mail has been sent:
      [Subject] => @subject
      [From] => @from
      [To] => @to
      [Reply-To] => @reply
        [Header] => @header 
      [Body] => @body
      ', [ + '@subject' => $message['subject'], + '@from' => $message['from'], + '@to' => $message['to'], + '@reply' => isset($message['reply_to']) ? $message['reply_to'] : NULL, + '@header' => $header_output, + '@body' => $message['body'] + ]); + drupal_set_message($output, 'status', TRUE); + } + + if ($config->get('send')) { + $default = new PhpMail(); + $result = $default->mail($message); + } + elseif (\Drupal::currentUser()->hasPermission('administer maillog')) { + $message = t('Sending of e-mail messages is disabled by Maillog module. Go @here to enable.', ['@here' => \Drupal::l('here', Url::fromRoute('maillog.settings'))]); + + drupal_set_message($message, 'warning', TRUE); + } + else { + \Drupal::logger('maillog')->notice('Attempted to send an email, but sending emails is disabled.'); + } + return isset($result) ? $result : TRUE; + } +} diff --git a/sites/all/modules/contrib/mail/maillog/src/Plugin/views/field/MaillogFieldDelete.php b/sites/all/modules/contrib/mail/maillog/src/Plugin/views/field/MaillogFieldDelete.php new file mode 100644 index 000000000..1b00adc9c --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/src/Plugin/views/field/MaillogFieldDelete.php @@ -0,0 +1,38 @@ +hasPermission('delete maillog')) { + return; + } + + $id = $this->getValue($values); + + $text = !empty($this->options['text']) ? $this->options['text'] : t('delete'); + + return \Drupal::l($text, 'maillog.delete', array('maillog_id' => $id), array('query' => drupal_get_destination())); + } +} + diff --git a/sites/all/modules/contrib/mail/maillog/src/Tests/Mail/MailUiTest.php b/sites/all/modules/contrib/mail/maillog/src/Tests/Mail/MailUiTest.php new file mode 100644 index 000000000..07bb49c1d --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/src/Tests/Mail/MailUiTest.php @@ -0,0 +1,149 @@ +config('system.mail')->set('interface.default', 'maillog')->save(); + // The system.site.mail setting goes into the From header of outgoing mails. + $this->config('system.site')->set('mail', 'simpletest@example.com')->save(); + + // Disable e-mail sending. + $this->config('maillog.settings') + ->set('send', FALSE) + ->save(); + } + + /** + * Tests logging mail with maillog module. + */ + public function testLogging() { + $mail = \Drupal::service('plugin.manager.mail')->mail('maillog', 'ui_test', 'test@example.com', \Drupal::languageManager()->getCurrentLanguage(), [], 'me@example.com', FALSE); + $mail['subject'] = 'This is a test subject.'; + $mail['body'] = 'This message is a test email body.'; + + // Send the prepared email. + $sender = new Maillog(); + $sender->mail($mail); + + // Create a user with valid permissions and go to the maillog overview page. + $this->drupalLogin($this->drupalCreateUser(['view maillog', 'administer maillog'])); + $this->drupalGet('admin/reports/maillog'); + + // Assert some values and click the subject link. + $this->assertText('simpletest@example.com'); + $this->assertText('test@example.com'); + $this->clickLink('This is a test subject.'); + $this->assertText('This message is a test email body.'); + + // Test clear log. + $this->drupalPostForm('admin/config/development/maillog', [], 'Clear all maillog entries'); + $this->drupalPostForm(NULL, [], 'Clear'); + $this->drupalGet('admin/reports/maillog'); + $this->assertNoText('simpletest@example.com'); + $this->assertText(t('There are no mail logs in the database.')); + } + + /** + * Checks the drupal_set_message() for disabled mail sending. + */ + protected function testNotice() { + + // Create a user with valid permissions and a recipient for a message. + $recipient = $this->drupalCreateUser(); + $this->drupalLogin($this->drupalCreateUser(['access user contact forms', 'access user profiles', 'administer maillog'])); + + // Send the recipient a message and check the expected notice. + $this->drupalPostForm('user/' . $recipient->id() . '/contact', [ + 'subject[0][value]' => 'Test Message', + 'message[0][value]' => 'This is a test.', + ], t('Send message')); + $this->clickLink('here'); + $this->assertResponse(200); + $this->assertTitle('Maillog Settings | Drupal'); + } + + /** + * Tests verbose output after send an email. + */ + public function testVerboseOutput() { + + // Create a user with valid permissions. + $user = $this->drupalCreateUser(); + $this->drupalLogin($this->drupalCreateUser([ + 'access user contact forms', + 'access user profiles', + 'view maillog' + ])); + + // Send the message. + $this->drupalPostForm('user/' . $user->id() . '/contact', [ + 'subject[0][value]' => 'Test Message', + 'message[0][value]' => 'This is a test.', + ], t('Send message')); + + // Assert the verbose output. + $this->assertText('A mail has been sent:'); + $this->assertRaw('[To] => ' . $user->getUsername() . '@example.com'); + $this->assertRaw('[Header] => Array'); + $this->assertRaw('[X-Mailer] => Drupal'); + $this->assertRaw('[Content-Type] => text/plain; charset=UTF-8; format=flowed; delsp=yes'); + $this->assertRaw('[Body] => Hello ' . $user->getUsername()); + + // Set verbose to false. + $this->config('maillog.settings')->set('verbose', FALSE)->save(); + $this->drupalPostForm('user/' . $user->id() . '/contact', [ + 'subject[0][value]' => 'Test Message', + 'message[0][value]' => 'This is a test.', + ], t('Send message')); + + // Assert there is no output. + $this->assertNoText('A mail has been sent:'); + $this->assertNoRaw('[To] => ' . $user->getUsername() . '@example.com'); + $this->assertNoRaw('[Header] => Array'); + + // Tests that users without permission cannot see verbose output. + $this->config('maillog.settings')->set('verbose', TRUE)->save(); + $this->drupalLogin($this->drupalCreateUser([ + 'access user contact forms', + 'access user profiles', + ])); + + $this->drupalPostForm('user/' . $user->id() . '/contact', [ + 'subject[0][value]' => 'Test Message', + 'message[0][value]' => 'This is a test.', + ], t('Send message')); + + // Assert there is no output. + $this->assertNoText('A mail has been sent:'); + $this->assertNoRaw('[To] => ' . $user->getUsername() . '@example.com'); + $this->assertNoRaw('[Header] => Array'); + } + +} diff --git a/sites/all/modules/contrib/mail/maillog/tests/modules/maillog_test/maillog_test.info.yml b/sites/all/modules/contrib/mail/maillog/tests/modules/maillog_test/maillog_test.info.yml new file mode 100644 index 000000000..c78813a81 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/tests/modules/maillog_test/maillog_test.info.yml @@ -0,0 +1,12 @@ +name: 'Maillog test' +description: 'Contains helper logic for the Maillog tests.' +package: 'Mail' +# core: 8.x +type: module +hidden: TRUE + +# Information added by Drupal.org packaging script on 2016-08-05 +version: '8.x-1.x-dev' +core: '8.x' +project: 'maillog' +datestamp: 1470431946 diff --git a/sites/all/modules/contrib/mail/maillog/tests/modules/maillog_test/maillog_test.module b/sites/all/modules/contrib/mail/maillog/tests/modules/maillog_test/maillog_test.module new file mode 100644 index 000000000..3da505c97 --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/tests/modules/maillog_test/maillog_test.module @@ -0,0 +1,34 @@ +alert("XSS test")'; + $message['body'][] = t('Test email body.'); + } + + // This has an XSS test for the body field. + elseif ($key == 'maillog_body_xss_test') { + $message['subject'] = t('Test email subject'); + $message['body'][] = ''; + } +} diff --git a/sites/all/modules/contrib/mail/maillog/tests/src/Kernel/MailTest.php b/sites/all/modules/contrib/mail/maillog/tests/src/Kernel/MailTest.php new file mode 100644 index 000000000..313707d9e --- /dev/null +++ b/sites/all/modules/contrib/mail/maillog/tests/src/Kernel/MailTest.php @@ -0,0 +1,98 @@ +installSchema('maillog', 'maillog'); + $this->installConfig(['system', 'maillog']); + // The system.site.mail setting goes into the From header of outgoing mails. + $this->config('system.site')->set('mail', 'simpletest@example.com')->save(); + + // Use the maillog mail plugin. + $GLOBALS['config']['system.mail']['interface']['default'] = 'maillog'; + + // Disables E-Mail Sending, only tracking. + $this->config('maillog.settings') + ->set('send', FALSE) + ->save(); + } + + /** + * Tests logging mail with maillog module. + */ + public function testLogging() { + $language = \Drupal::languageManager()->getCurrentLanguage(); + + // Send an email. + $from_email = 'simpletest@example.com'; + $reply_email = 'someone_else@example.com'; + \Drupal::service('plugin.manager.mail')->mail('simpletest', 'from_test', 'from_test@example.com', $language, array(), $reply_email); + + // Compare the maillog db entry with the sent mail. + $logged_email = $this->getLatestMaillogEntry(); + $this->assertTrue(is_array($logged_email), 'Email is captured.'); + $this->assertEqual($from_email, $logged_email['header_from'], 'Email is sent correctly.'); + $this->assertEqual($reply_email, $logged_email['header_all']['Reply-to'], 'Message is sent with correct reply address.'); + } + + /** + * Gets the latest Maillog entry. + * + * @return array + * Maillog entry + */ + protected function getLatestMaillogEntry() { + $query = 'SELECT idmaillog, header_from, header_to, header_reply_to, header_all, subject, body FROM {maillog} ORDER BY idmaillog DESC'; + $result = \Drupal::database()->queryRange($query, 0, 1); + + if ($maillog = $result->fetchAssoc()) { + // Unserialize values. + $maillog['header_all'] = unserialize($maillog['header_all']); + } + return $maillog; + } + + /** + * Confirm what happens with long subject lines. + */ + public function testLongSubject() { + // Send an email. + $from_email = 'from_test@example.com'; + $to_email = 'to_test@example.com'; + \Drupal::service('plugin.manager.mail')->mail('maillog_test', 'maillog_long_subject_test', $to_email, 'en', [], $from_email); + + // Compare the maillog db entry with the sent mail. + $logged_email = $this->getLatestMaillogEntry(); + $this->assertTrue(!empty($logged_email), 'The test email was captured.'); + + // The original subject line, as copied from maillog_test_mail(). + $subject = str_repeat(t('Test email subject@space', ['@space' => ' ']), 20); + + // Duplicate the string trimming. + $subject_trimmed = Unicode::substr($subject, 0, 255); + self::assertEquals($subject_trimmed, $logged_email['subject'], 'Email subject was trimmed correctly.'); + self::assertNotEquals($subject, $logged_email['subject'], 'Email subject is not untrimmed.'); + } + +} diff --git a/sites/all/modules/contrib/theme/extlink/LICENSE.txt b/sites/all/modules/contrib/theme/extlink/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General +Public License instead of this License. diff --git a/sites/all/modules/contrib/theme/extlink/README.txt b/sites/all/modules/contrib/theme/extlink/README.txt new file mode 100644 index 000000000..8b44719e6 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/README.txt @@ -0,0 +1,37 @@ +The External Links module is a very simple approach to adding icons to links +to external websites or e-mail addresses. It is a JavaScript-based +implementation, so the icons are only shown to users that have JavaScript +enabled. + +External Links was written by Nathan Haug. +Built by Robots: http://www.lullabot.com + +Install +------- +Simply install External Links like you would install any other module. + +1) Copy the extlink folder in the modules folder in your Drupal docroot. + +2) Enable the module using Manage -> Extend (/admin/modules). + +3) No additional configuration is necessary though you may fine-tune settings at + Manage -> Configuration -> External Links (/admin/config/user-interface/extlink). + +A note about the CSS +-------------------- +This module adds a CSS file that is only a few lines in length. You may choose +to move this CSS to your theme to prevent the file from needing to be loaded +separately. To do this: + +1) Open the .info.yml file for your theme and add those lines of code to prevent + the extlink.css file from loading: + + stylesheets-remove: + - extlink.css + +2) Open the extlink.css file within the extlink directory and copy all the code + from the file into your theme's style.css file. +3) Copy the extlink.png and mailto.png files to your theme's directory. + +Note that you DO NOT need to make an extlink.css file. Specifying the file in the +.info.yml file is enough to tell Drupal not to load the original file. diff --git a/sites/all/modules/contrib/theme/extlink/config/extlink.settings.yml b/sites/all/modules/contrib/theme/extlink/config/extlink.settings.yml new file mode 100644 index 000000000..1a650813c --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/config/extlink.settings.yml @@ -0,0 +1,8 @@ +extlink_target: 0 +extlink_subdomains: 1 +extlink_alert: 0 +extlink_alert_text: 'This link will take you to an external web site. We are not responsible for their content.' +extlink_exclude: '' +extlink_include: '' +extlink_class: 'ext' +extlink_mailto_class: 'mailto' \ No newline at end of file diff --git a/sites/all/modules/contrib/theme/extlink/config/schema/extlink.schema.yml b/sites/all/modules/contrib/theme/extlink/config/schema/extlink.schema.yml new file mode 100644 index 000000000..08fe220cb --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/config/schema/extlink.schema.yml @@ -0,0 +1,40 @@ +# Schema for the configuration files of the exlink module. + +extlink.settings: + type: mapping + label: 'External Links Settings' + mapping: + extlink_target: + type: boolean + label: 'Open external links in a new window' + extlink_subdomains: + type: boolean + label: 'Consider subdomains internal' + extlink_alert: + type: boolean + label: 'Enable Pop-up warnings' + extlink_alert_text: + type: string + label: 'Message to display when Pop-up warning is shown' + extlink_exclude: + type: string + label: 'Exclude links matching the pattern' + extlink_include: + type: string + label: 'Include links matching the pattern' + extlink_class: + type: string + label: 'Add icon to external links' + extlink_img_class: + type: boolean + label: 'Treat images wrapped in an anchor tag as external links' + extlink_css_exclude: + type: string + label: 'Comma-separated list of CSS selectors to exclude' + extlink_css_explicit: + type: string + label: 'Comma-separated list of CSS selectors to include' + extlink_mailto_class: + type: string + label: 'Add icon to mailto links' + diff --git a/sites/all/modules/contrib/theme/extlink/extlink.css b/sites/all/modules/contrib/theme/extlink/extlink.css new file mode 100644 index 000000000..f912d8b8f --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/extlink.css @@ -0,0 +1,22 @@ +span.ext { + background: url(extlink_s.png) 2px center no-repeat; + width: 10px; + height: 10px; + padding-right: 12px; + text-decoration: none; +} +span.mailto { + background: url(extlink_s.png) -20px center no-repeat; + width: 10px; + height: 10px; + padding-right: 12px; + text-decoration: none; +} + +/* Hide the extra spans when printing. */ +@media print { + span.ext, span.mailto { + display: none; + padding: 0; + } +} diff --git a/sites/all/modules/contrib/theme/extlink/extlink.info.yml b/sites/all/modules/contrib/theme/extlink/extlink.info.yml new file mode 100644 index 000000000..e70b4f818 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/extlink.info.yml @@ -0,0 +1,13 @@ +name: External Links +type: module +description: Modify behavior and appearance of external links. +package: User interface +# version: 8.x +# core: 8.x +configure: extlink_admin.settings + +# Information added by Drupal.org packaging script on 2016-08-31 +version: '8.x-1.0-alpha2+8-dev' +core: '8.x' +project: 'extlink' +datestamp: 1472650450 diff --git a/sites/all/modules/contrib/theme/extlink/extlink.install b/sites/all/modules/contrib/theme/extlink/extlink.install new file mode 100644 index 000000000..92aa2bcc1 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/extlink.install @@ -0,0 +1,21 @@ +configure the External Links module for your site.', + ['@extlink_admin' => $url->toString()]), 'status'); + } +} diff --git a/sites/all/modules/contrib/theme/extlink/extlink.js b/sites/all/modules/contrib/theme/extlink/extlink.js new file mode 100644 index 000000000..5af9fccf0 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/extlink.js @@ -0,0 +1,171 @@ +(function ($, Drupal, drupalSettings) { + + "use strict"; + + Drupal.extlink = Drupal.extlink || {}; + + Drupal.extlink.attach = function (context, drupalSettings) { + if (!drupalSettings.data.hasOwnProperty('extlink')) { + return; + } + + // Strip the host name down, removing ports, subdomains, or www. + var pattern = /^(([^\/:]+?\.)*)([^\.:]{1,})((\.[a-z0-9]{1,253})*)(:[0-9]{1,5})?$/; + var host = window.location.host.replace(pattern, '$3$4'); + var subdomain = window.location.host.replace(pattern, '$1'); + + // Determine what subdomains are considered internal. + var subdomains; + if (drupalSettings.data.extlink.extSubdomains) { + subdomains = "([^/]*\\.)?"; + } + else if (subdomain === 'www.' || subdomain === '') { + subdomains = "(www\\.)?"; + } + else { + subdomains = subdomain.replace(".", "\\."); + } + + // Build regular expressions that define an internal link. + var internal_link = new RegExp("^https?://" + subdomains + host, "i"); + + // Extra internal link matching. + var extInclude = false; + if (drupalSettings.data.extlink.extInclude) { + extInclude = new RegExp(drupalSettings.data.extlink.extInclude.replace(/\\/, '\\'), "i"); + } + + // Extra external link matching. + var extExclude = false; + if (drupalSettings.data.extlink.extExclude) { + extExclude = new RegExp(drupalSettings.data.extlink.extExclude.replace(/\\/, '\\'), "i"); + } + + // Extra external link CSS selector exclusion. + var extCssExclude = false; + if (drupalSettings.data.extlink.extCssExclude) { + extCssExclude = drupalSettings.data.extlink.extCssExclude; + } + + // Extra external link CSS selector explicit. + var extCssExplicit = false; + if (drupalSettings.data.extlink.extCssExplicit) { + extCssExplicit = drupalSettings.data.extlink.extCssExplicit; + } + + // Find all links which are NOT internal and begin with http as opposed + // to ftp://, javascript:, etc. other kinds of links. + // When operating on the 'this' variable, the host has been appended to + // all links by the browser, even local ones. + // In jQuery 1.1 and higher, we'd use a filter method here, but it is not + // available in jQuery 1.0 (Drupal 5 default). + var external_links = []; + var mailto_links = []; + $("a:not(." + drupalSettings.data.extlink.extClass + ", ." + drupalSettings.data.extlink.mailtoClass + "), area:not(." + drupalSettings.data.extlink.extClass + ", ." + drupalSettings.data.extlink.mailtoClass + ")", context).each(function (el) { + try { + if (typeof this.href == 'string') { + var url = this.href.toLowerCase(); + } + // Handle SVG links (xlink:href). + else if (typeof this.href == 'object') { + var url = this.href.baseVal; + } + if (url.indexOf('http') === 0 + && ((!url.match(internal_link) && !(extExclude && url.match(extExclude))) || (extInclude && url.match(extInclude))) + && !(extCssExclude && $(this).parents(extCssExclude).length > 0) + && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { + external_links.push(this); + } + // Do not include area tags with begin with mailto: (this prohibits + // icons from being added to image-maps). + else if (this.tagName !== 'AREA' + && url.indexOf('mailto:') === 0 + && !(extCssExclude && $(this).parents(extCssExclude).length > 0) + && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { + mailto_links.push(this); + } + } + // IE7 throws errors often when dealing with irregular links, such as: + // Empty tags. + // example User:pass syntax. + catch (error) { + return false; + } + }); + + if (drupalSettings.data.extlink.extClass) { + Drupal.extlink.applyClassAndSpan(external_links, drupalSettings.data.extlink.extClass); + } + + if (drupalSettings.data.extlink.mailtoClass) { + Drupal.extlink.applyClassAndSpan(mailto_links, drupalSettings.data.extlink.mailtoClass); + } + + if (drupalSettings.data.extlink.extTarget) { + // Apply the target attribute to all links. + if(drupalSettings.data.extlink.extTarget){ + $(external_links).attr('target', '_blank'); + } + } + + Drupal.extlink = Drupal.extlink || {}; + + // Set up default click function for the external links popup. This should be + // overridden by modules wanting to alter the popup. + Drupal.extlink.popupClickHandler = Drupal.extlink.popupClickHandler || function () { + if (drupalSettings.data.extlink.extAlert) { + return confirm(drupalSettings.data.extlink.extAlertText); + } + }; + + $(external_links).click(function (e) { + return Drupal.extlink.popupClickHandler(e, this); + }); + }; + + /** + * Apply a class and a trailing to all links not containing images. + * + * @param links + * An array of DOM elements representing the links. + * @param class_name + * The class to apply to the links. + */ + Drupal.extlink.applyClassAndSpan = function (links, class_name) { + var $links_to_process; + if (drupalSettings.data.extlink.extImgClass) { + $links_to_process = $(links); + } + else { + var links_with_images = $(links).find('img').parents('a'); + $links_to_process = $(links).not(links_with_images); + } + $links_to_process.addClass(class_name); + var i; + var length = $links_to_process.length; + for (i = 0; i < length; i++) { + var $link = $($links_to_process[i]); + if ($link.css('display') === 'inline' || $link.css('display') === 'inline-block') { + if (class_name === drupalSettings.data.extlink.mailtoClass) { + $link.append(' ' + drupalSettings.data.extlink.mailtoLabel + ''); + } + else { + $link.append(' ' + drupalSettings.data.extlink.extLabel + ''); + } + } + } + }; + + Drupal.behaviors.extlink = Drupal.behaviors.extlink || {}; + Drupal.behaviors.extlink.attach = function (context, drupalSettings) { + // Backwards compatibility, for the benefit of modules overriding extlink + // functionality by defining an "extlinkAttach" global function. + if (typeof extlinkAttach === 'function') { + extlinkAttach(context); + } + else { + Drupal.extlink.attach(context, drupalSettings); + } + }; + +})(jQuery, Drupal, drupalSettings); diff --git a/sites/all/modules/contrib/theme/extlink/extlink.libraries.yml b/sites/all/modules/contrib/theme/extlink/extlink.libraries.yml new file mode 100644 index 000000000..dae673e6c --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/extlink.libraries.yml @@ -0,0 +1,12 @@ +drupal.extlink: + version: VERSION + js: + extlink.min.js: {} + css: + theme: + extlink.css: { every_page: 1 } + dependencies: + - core/jquery + - core/drupal + - core/drupalSettings + - core/jquery.once diff --git a/sites/all/modules/contrib/theme/extlink/extlink.links.menu.yml b/sites/all/modules/contrib/theme/extlink/extlink.links.menu.yml new file mode 100644 index 000000000..4392012c2 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/extlink.links.menu.yml @@ -0,0 +1,5 @@ +extlink_admin.settings: + title: External Links + description: 'External Links administration page.' + route_name: extlink_admin.settings + parent: system.admin_config_ui diff --git a/sites/all/modules/contrib/theme/extlink/extlink.min.js b/sites/all/modules/contrib/theme/extlink/extlink.min.js new file mode 100644 index 000000000..c4d7aeaf9 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/extlink.min.js @@ -0,0 +1 @@ +!function(t,a,e){"use strict";a.extlink=a.extlink||{},a.extlink.attach=function(e,n){if(n.data.hasOwnProperty("extlink")){var l,i=/^(([^\/:]+?\.)*)([^\.:]{4,})((\.[a-z]{1,4})*)(:[0-9]{1,5})?$/,s=window.location.host.replace(i,"$3$4"),x=window.location.host.replace(i,"$1");l=n.data.extlink.extSubdomains?"([^/]*\\.)?":"www."===x||""===x?"(www\\.)?":x.replace(".","\\.");var p=new RegExp("^https?://"+l+s,"i"),d=!1;n.data.extlink.extInclude&&(d=new RegExp(n.data.extlink.extInclude.replace(/\\/,"\\"),"i"));var r=!1;n.data.extlink.extExclude&&(r=new RegExp(n.data.extlink.extExclude.replace(/\\/,"\\"),"i"));var k=!1;n.data.extlink.extCssExclude&&(k=n.data.extlink.extCssExclude);var c=!1;n.data.extlink.extCssExplicit&&(c=n.data.extlink.extCssExplicit);var o=[],h=[];t("a:not(."+n.data.extlink.extClass+", ."+n.data.extlink.mailtoClass+"), area:not(."+n.data.extlink.extClass+", ."+n.data.extlink.mailtoClass+")",e).each(function(a){try{var e=this.href.toLowerCase();0!==e.indexOf("http")||(e.match(p)||r&&e.match(r))&&(!d||!e.match(d))||k&&t(this).parents(k).length>0||c&&t(this).parents(c).length<1?"AREA"===this.tagName||0!==e.indexOf("mailto:")||k&&t(this).parents(k).length>0||c&&t(this).parents(c).length<1||h.push(this):o.push(this)}catch(n){return!1}}),n.data.extlink.extClass&&a.extlink.applyClassAndSpan(o,n.data.extlink.extClass),n.data.extlink.mailtoClass&&a.extlink.applyClassAndSpan(h,n.data.extlink.mailtoClass),n.data.extlink.extTarget&&n.data.extlink.extTarget&&t(o).attr("target","_blank"),a.extlink=a.extlink||{},a.extlink.popupClickHandler=a.extlink.popupClickHandler||function(){return n.data.extlink.extAlert?confirm(n.data.extlink.extAlertText):void 0},t(o).click(function(t){return a.extlink.popupClickHandler(t)})}},a.extlink.applyClassAndSpan=function(a,n){var l;if(e.data.extlink.extImgClass)l=t(a);else{var i=t(a).find("img").parents("a");l=t(a).not(i)}l.addClass(n);var s,x=l.length;for(s=0;x>s;s++){var p=t(l[s]);("inline"===p.css("display")||"inline-block"===p.css("display"))&&(n===e.data.extlink.mailtoClass?p.append(' '+e.data.extlink.mailtoLabel+""):p.append(' '+e.data.extlink.extLabel+""))}},a.behaviors.extlink=a.behaviors.extlink||{},a.behaviors.extlink.attach=function(t,e){"function"==typeof extlinkAttach?extlinkAttach(t):a.extlink.attach(t,e)}}(jQuery,Drupal,drupalSettings); diff --git a/sites/all/modules/contrib/theme/extlink/extlink.module b/sites/all/modules/contrib/theme/extlink/extlink.module new file mode 100644 index 000000000..92d744467 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/extlink.module @@ -0,0 +1,32 @@ + $config->get('extlink_target', 0), + 'extClass' => $config->get('extlink_class', 'ext'), + 'extLabel' => Html::escape($config->get('extlink_label', t('(link is external)'))), + 'extImgClass' => $config->get('extlink_img_class', 0), + 'extSubdomains' => $config->get('extlink_subdomains', 1), + 'extExclude' => $config->get('extlink_exclude', ''), + 'extInclude' => $config->get('extlink_include', ''), + 'extCssExclude' => $config->get('extlink_css_exclude', ''), + 'extCssExplicit' => $config->get('extlink_css_explicit', ''), + 'extAlert' => $config->get('extlink_alert', 0), + 'extAlertText' => $config->get('extlink_alert_text', t('This link will take you to an external web site. We are not responsible for their content.')), + 'mailtoClass' => $config->get('extlink_mailto_class', 'mailto'), + 'mailtoLabel' => Html::escape($config->get('extlink_mailto_label', t('(link sends e-mail)'))), + ); +} diff --git a/sites/all/modules/contrib/theme/extlink/extlink.routing.yml b/sites/all/modules/contrib/theme/extlink/extlink.routing.yml new file mode 100644 index 000000000..63bd78df8 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/extlink.routing.yml @@ -0,0 +1,7 @@ +extlink_admin.settings: + path: '/admin/config/user-interface/extlink' + defaults: + _form: '\Drupal\extlink\Form\ExtlinkAdminSettingsForm' + _title: 'External Links' + requirements: + _permission: 'administer site configuration' \ No newline at end of file diff --git a/sites/all/modules/contrib/theme/extlink/extlink_s.png b/sites/all/modules/contrib/theme/extlink/extlink_s.png new file mode 100644 index 0000000000000000000000000000000000000000..778fb586c4789382e5b7b8a4192540dbf6668509 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3P8-o!2%@fU8d**sRU0K#}JM4b0?kVWl-R7zFE&) zmZWYIVt0%~EAGM){Vg6Q-KKndr>oo)XAmxFuvzLay*7q%oAo?}_K92@qC}Zn_V7(K zz0{P(cH;I6y!Bqk8OynIwte{C%H};|?#YWrCtZPdF?hQAxvX 'checkbox', + '#title' => $this->t('Place an icon next to external links.'), + '#return_value' => 'ext', + '#default_value' => $config->get('extlink_class'), + '#description' => $this->t('Places an   icon next to external links.'), + ); + + $form['extlink_mailto_class'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Place an icon next to mailto links'), + '#return_value' => 'mailto', + '#default_value' => $config->get('extlink_mailto_class'), + '#description' => $this->t('Places an   icon next to mailto links.'), + ); + + $form['extlink_img_class'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Place an icon next to image links'), + '#return_value' => TRUE, + '#default_value' => $config->get('extlink_img_class', FALSE), + '#description' => $this->t('If checked, images wrapped in an anchor tag will be treated as external links.'), + ); + + $form['extlink_subdomains'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Exclude links with the same primary domain.'), + '#default_value' => $config->get('extlink_subdomains'), + '#description' => $this->t("For example, a link from 'www.example.com' to the subdomain of 'my.example.com' would be excluded."), + ); + + $form['extlink_target'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Open external links in a new window'), + '#return_value' => '_blank', + '#default_value' => $config->get('extlink_target'), + ); + + $form['extlink_alert'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Display a pop-up warning when any external link is clicked.'), + '#return_value' => '_blank', + '#default_value' => $config->get('extlink_alert'), + ); + + $form['extlink_alert_text'] = array( + '#type' => 'textarea', + '#title' => $this->t('Text to display in the pop-up warning box.'), + '#rows' => 3, + '#default_value' => $config->get('extlink_alert_text'), + '#description' => $this->t('Text to display in the pop-up external link warning box.'), + '#wysiwyg' => FALSE, + '#states' => array( + // Only show this field when user opts to display a pop-up warning. + 'visible' => array( + ':input[name="extlink_alert"]' => array('checked' => TRUE), + ), + ), + ); + + $patterns = array( + '#theme' => 'item_list', + '#items' => array( + array('#markup' => '(example\.com) ' . $this->t('Matches example.com.')), + array('#markup' => '(example\.com)|(example\.net) ' . $this->t('Multiple patterns can be strung together by using a pipe. Matches example.com OR example.net.')), + array('#markup' => '(links/goto/[0-9]+/[0-9]+) ' . $this->t('Matches links that go through the Links module redirect.')), + ), + ); + + $wildcards = array( + '#theme' => 'item_list', + '#items' => array( + array('#markup' => '. ' . $this->t('Matches any character.')), + array('#markup' => '? ' . $this->t('The previous character or set is optional.')), + array('#markup' => '\d ' . $this->t('Matches any digit (0-9).')), + array('#markup' => '[a-z] ' . $this->t('Brackets may be used to match a custom set of characters. This matches any alphabetic letter.')), + ), + ); + + $form['patterns'] = array( + '#type' => 'details', + '#title' => $this->t('Pattern matching'), + '#description' => + '

      ' . $this->t('External links uses patterns (regular expressions) to match the "href" property of links.') . '

      ' . + $this->t('Here are some common patterns.') . + $renderer->render($patterns) . + $this->t('Common special characters:') . + $renderer->render($wildcards) . + '

      ' . $this->t('All special characters (@characters) must also be escaped with backslashes. Patterns are not case-sensitive. Any pattern supported by JavaScript may be used.', array('@characters' => '^ $ . ? ( ) | * +')) . '

      ', + '#open' => FALSE, + ); + + $form['patterns']['extlink_exclude'] = array( + '#type' => 'textfield', + '#title' => $this->t('Exclude links matching the pattern'), + '#maxlength' => NULL, + '#default_value' => $config->get('extlink_exclude'), + '#description' => $this->t('Enter a regular expression for links that you wish to exclude from being considered external.'), + ); + + $form['patterns']['extlink_include'] = array( + '#type' => 'textfield', + '#title' => $this->t('Include links matching the pattern'), + '#maxlength' => NULL, + '#default_value' => $config->get('extlink_include'), + '#description' => $this->t('Enter a regular expression for internal links that you wish to be considered external.'), + ); + + $form['css_matching'] = array( + '#tree' => FALSE, + '#type' => 'fieldset', + '#title' => $this->t('CSS Matching'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#description' => + '

      ' . $this->t('Use CSS selectors to exclude entirely or only look inside explicitly specified classes and IDs for external links. These will be passed straight to jQuery for matching.') . '

      ', + ); + + $form['css_matching']['extlink_css_exclude'] = array( + '#type' => 'textarea', + '#title' => $this->t('Exclude links inside these CSS selectors'), + '#maxlength' => NULL, + '#default_value' => $config->get('extlink_css_exclude', ''), + '#description' => $this->t('Enter a comma-separated list of CSS selectors (ie "#block-block-2 .content, ul.menu")'), + ); + + $form['css_matching']['extlink_css_explicit'] = array( + '#type' => 'textarea', + '#title' => $this->t('Only look for links inside these CSS selectors'), + '#maxlength' => NULL, + '#default_value' => $config->get('extlink_css_explicit', ''), + '#description' => $this->t('Enter a comma-separated list of CSS selectors (ie "#block-block-2 .content, ul.menu")'), + ); + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + parent::validateForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $values = $form_state->getValues(); + + \Drupal::configFactory()->getEditable('extlink.settings') + ->set('extlink_include', $values['extlink_include']) + ->set('extlink_exclude', $values['extlink_exclude']) + ->set('extlink_alert_text', $values['extlink_alert_text']) + ->set('extlink_alert', $values['extlink_alert']) + ->set('extlink_target', $values['extlink_target']) + ->set('extlink_subdomains', $values['extlink_subdomains']) + ->set('extlink_mailto_class', $values['extlink_mailto_class']) + ->set('extlink_img_class', $values['extlink_img_class']) + ->set('extlink_class', $values['extlink_class']) + ->set('extlink_css_exclude', $values['extlink_css_exclude']) + ->set('extlink_css_explicit', $values['extlink_css_explicit']) + ->save(); + + parent::SubmitForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function getEditableConfigNames() { + return ['extlink.settings']; + } + +} diff --git a/sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkAdminTest.php b/sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkAdminTest.php new file mode 100644 index 000000000..9d4feb246 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkAdminTest.php @@ -0,0 +1,26 @@ +drupalLogin($this->normal_user); + $this->drupalGet(self::EXTLINK_ADMIN_PATH); + $this->assertText(t('Access denied'), 'Normal users should not be able to access the External Links admin pages', 'External Links'); + + $this->drupalLogin($this->admin_user); + $this->drupalGet(self::EXTLINK_ADMIN_PATH); + $this->assertNoText(t('Access denied'), 'Admin users should be able to access the External Links admin pages', 'External Links'); + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkTest.php b/sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkTest.php new file mode 100644 index 000000000..581b191cb --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkTest.php @@ -0,0 +1,18 @@ +drupalGet(''); + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkTestBase.php b/sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkTestBase.php new file mode 100644 index 000000000..962d1ef47 --- /dev/null +++ b/sites/all/modules/contrib/theme/extlink/src/Tests/ExtlinkTestBase.php @@ -0,0 +1,67 @@ +normal_user = $this->drupalCreateUser($permissions); + + // Create an admin user. + $permissions[] = 'administer site configuration'; + $permissions[] = 'administer permissions'; + $this->admin_user = $this->drupalCreateUser($permissions); + } + + protected function getNodeFormValues() { + $edit = array( + 'title' => 'node_title ' . $this->randomName(32), + 'body[' . LANGUAGE_NONE . '][0][value]' => 'node_body ' . $this->randomName(256) . ' Google!', + ); + return $edit; + } + + /** + * Test if External Link is present + */ + protected function assertExternalLinkPresence() { + $elements = $this->xpath('//span[@class="ext"]'); + if (count($elements) > 0) + $this->pass('There should be an External Link on the form.', 'External Links'); + else + $this->fail('There should be an External Link on the form.', 'External Links'); + } +} \ No newline at end of file diff --git a/sites/all/modules/contrib/users/profile/.travis-phpcs.sh b/sites/all/modules/contrib/users/profile/.travis-phpcs.sh new file mode 100644 index 000000000..8803b3b4f --- /dev/null +++ b/sites/all/modules/contrib/users/profile/.travis-phpcs.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e $DRUPAL_TI_DEBUG + +# Ensure the right Drupal version is installed. +# Note: This function is re-entrant. +drupal_ti_ensure_drupal + +phpcs --report=full --standard=Drupal "$DRUPAL_TI_DRUPAL_DIR/$DRUPAL_TI_MODULES_PATH/$DRUPAL_TI_MODULE_NAME" || true diff --git a/sites/all/modules/contrib/users/profile/.travis.yml b/sites/all/modules/contrib/users/profile/.travis.yml new file mode 100644 index 000000000..d4c22ddb5 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/.travis.yml @@ -0,0 +1,119 @@ +# @file +# .travis.yml - Drupal for Travis CI Integration +# +# Template provided by https://github.com/LionsAd/drupal_ti. +# +# Based for simpletest upon: +# https://github.com/sonnym/travis-ci-drupal-module-example + +language: php +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +php: + - 5.5 + - 5.6 + - 7 + - hhvm + +matrix: + fast_finish: true + allow_failures: + - php: 7 + - php: hhvm +branches: + only: + - "8.x-1.x" + +env: + global: + # add composer's global bin directory to the path + # see: https://github.com/drush-ops/drush#install---composer + - PATH="$PATH:$HOME/.composer/vendor/bin" + + # Configuration variables. + - DRUPAL_TI_MODULE_NAME="profile" + - DRUPAL_TI_SIMPLETEST_GROUP="profile" + + # Define runners and environment vars to include before and after the + # main runners / environment vars. + #- DRUPAL_TI_SCRIPT_DIR_BEFORE="./drupal_ti/before" + #- DRUPAL_TI_SCRIPT_DIR_AFTER="./drupal_ti/after" + + # The environment to use, supported are: drupal-7, drupal-8 + - DRUPAL_TI_ENVIRONMENT="drupal-8" + + # Drupal specific variables. + - DRUPAL_TI_DB="drupal_travis_db" + - DRUPAL_TI_DB_URL="mysql://root:@127.0.0.1/drupal_travis_db" + # Note: Do not add a trailing slash here. + - DRUPAL_TI_WEBSERVER_URL="http://127.0.0.1" + - DRUPAL_TI_WEBSERVER_PORT="8080" + + # Simpletest specific commandline arguments, the DRUPAL_TI_SIMPLETEST_GROUP is appended at the end. + - DRUPAL_TI_SIMPLETEST_ARGS="--verbose --color --concurrency 4 --url $DRUPAL_TI_WEBSERVER_URL:$DRUPAL_TI_WEBSERVER_PORT" + + # === Behat specific variables. + # This is relative to $TRAVIS_BUILD_DIR + - DRUPAL_TI_BEHAT_DIR="./tests/behat" + # These arguments are passed to the bin/behat command. + - DRUPAL_TI_BEHAT_ARGS="" + # Specify the filename of the behat.yml with the $DRUPAL_TI_DRUPAL_DIR variables. + - DRUPAL_TI_BEHAT_YML="behat.yml.dist" + # This is used to setup Xvfb. + - DRUPAL_TI_BEHAT_SCREENSIZE_COLOR="1280x1024x16" + # The version of seleniumthat should be used. + - DRUPAL_TI_BEHAT_SELENIUM_VERSION="2.44" + + # PHPUnit specific commandline arguments. + - DRUPAL_TI_PHPUNIT_ARGS="" + + # Code coverage via coveralls.io + - DRUPAL_TI_COVERAGE="satooshi/php-coveralls:0.6.*" + # This needs to match your .coveralls.yml file. + - DRUPAL_TI_COVERAGE_FILE="build/logs/clover.xml" + + # Debug options + #- DRUPAL_TI_DEBUG="-x -v" + # Set to "all" to output all files, set to e.g. "xvfb selenium" or "selenium", + # etc. to only output those channels. + #- DRUPAL_TI_DEBUG_FILE_OUTPUT="selenium xvfb webserver" + + matrix: + # [[[ SELECT ANY OR MORE OPTIONS ]]] + #- DRUPAL_TI_RUNNERS="phpunit" + - DRUPAL_TI_RUNNERS="simpletest" + #- DRUPAL_TI_RUNNERS="behat" + #- DRUPAL_TI_RUNNERS="phpunit simpletest behat" + # Uncomment once unit tests added. + #- DRUPAL_TI_RUNNERS="phpunit-core simpletest" + +mysql: + database: drupal_travis_db + username: root + encoding: utf8 + +before_install: + - composer self-update + - composer global require "lionsad/drupal_ti:1.*" + - drupal-ti before_install + # Codesniffer and Coder + - composer global require "squizlabs/php_codesniffer:2.0.*@dev" + - composer global require drupal/coder:8.2.0-beta1 + - ln -s ~/.composer/vendor/drupal/coder/coder_sniffer/Drupal ~/.composer/vendor/squizlabs/php_codesniffer/CodeSniffer/Standards/ + +install: + - drupal-ti install + +before_script: + - drupal-ti before_script + +script: + - drupal-ti --include .travis-phpcs.sh + - export SIMPLETEST_DB=mysql://root:@127.0.0.1/drupal_travis_db; drupal-ti script + +after_script: + - drupal-ti after_script diff --git a/sites/all/modules/contrib/users/profile/LICENSE.txt b/sites/all/modules/contrib/users/profile/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General +Public License instead of this License. diff --git a/sites/all/modules/contrib/users/profile/README.md b/sites/all/modules/contrib/users/profile/README.md new file mode 100644 index 000000000..fb77c5bf4 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/README.md @@ -0,0 +1,23 @@ +Profile +------- + +The *Profile* module provides configurable user profiles. + +## Requirements + +* [Entity API](https://www.drupal.org/project/entity) + +## Comparison to user account fields + +Why use profiles instead of user account fields? + +* With profile, user account settings and user profiles are conceptually different things, e.g. with the "Profile" module enabled users get two separate menu links "My account" and "My profile". +* Profile allows for creating multiple profile types, which may be assigned to roles via permissions (e.g. a general profile + a customer profile) +* Profile supports private profile fields, which are only shown to the user owning the profile and to administrators. + +## Features + +* Multiple profile types may be created via the UI (e.g. a general profile + a customer profile), whereas the module provides separated permissions for those. +* Optionally, profile forms are shown during user account registration. +* Fields may be configured to be private - thus visible only to the profile owner and administrators. +* Profile types are displayed on the user view page, and can be configured through "Manage Display" on account settings. \ No newline at end of file diff --git a/sites/all/modules/contrib/users/profile/composer.json b/sites/all/modules/contrib/users/profile/composer.json new file mode 100644 index 000000000..3b0970111 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/composer.json @@ -0,0 +1,10 @@ +{ + "name": "drupal/profile", + "type": "drupal-module", + "description": "Provides configurable user profiles.", + "homepage": "http://drupal.org/project/profile", + "license": "GPL-2.0+", + "require": { + "drupal/core": "~8.1" + } +} diff --git a/sites/all/modules/contrib/users/profile/config/install/system.action.profile_delete_action.yml b/sites/all/modules/contrib/users/profile/config/install/system.action.profile_delete_action.yml new file mode 100644 index 000000000..8805221e8 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/config/install/system.action.profile_delete_action.yml @@ -0,0 +1,9 @@ +langcode: en +status: true +dependencies: + module: + - profile +id: profile_delete_action +label: 'Delete selected profile' +type: profile +plugin: profile_delete_action diff --git a/sites/all/modules/contrib/users/profile/config/install/system.action.profile_publish_action.yml b/sites/all/modules/contrib/users/profile/config/install/system.action.profile_publish_action.yml new file mode 100644 index 000000000..36e5eda64 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/config/install/system.action.profile_publish_action.yml @@ -0,0 +1,9 @@ +langcode: en +status: true +dependencies: + module: + - profile +id: profile_publish_action +label: 'Publish selected profile' +type: profile +plugin: profile_publish_action diff --git a/sites/all/modules/contrib/users/profile/config/install/system.action.profile_unpublish_action.yml b/sites/all/modules/contrib/users/profile/config/install/system.action.profile_unpublish_action.yml new file mode 100644 index 000000000..ac0e0f330 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/config/install/system.action.profile_unpublish_action.yml @@ -0,0 +1,9 @@ +langcode: en +status: true +dependencies: + module: + - profile +id: profile_unpublish_action +label: 'Unpublish selected profile' +type: profile +plugin: profile_unpublish_action diff --git a/sites/all/modules/contrib/users/profile/config/install/views.view.profiles.yml b/sites/all/modules/contrib/users/profile/config/install/views.view.profiles.yml new file mode 100644 index 000000000..9f6953648 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/config/install/views.view.profiles.yml @@ -0,0 +1,379 @@ +langcode: en +status: true +dependencies: + module: + - entity + - profile +id: profiles +label: Profiles +module: views +description: '' +tag: '' +base_table: profile +base_field: profile_id +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: none + options: { } + cache: + type: tag + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: none + options: + offset: 0 + style: + type: grid + options: + grouping: { } + columns: 4 + automatic_width: true + alignment: horizontal + col_class_default: true + col_class_custom: '' + row_class_default: true + row_class_custom: '' + row: + type: fields + options: + inline: { } + separator: '' + hide_empty: false + default_field_elements: true + fields: + rendered_entity: + id: rendered_entity + table: profile + field: rendered_entity + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + view_mode: default + entity_type: profile + plugin_id: rendered_entity + operations: + id: operations + table: profile + field: operations + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: true + entity_type: profile + plugin_id: entity_operations + filters: { } + sorts: { } + header: { } + footer: { } + empty: { } + relationships: { } + arguments: + uid: + id: uid + table: profile + field: uid + relationship: none + group_type: group + admin_label: '' + default_action: default + exception: + value: all + title_enable: false + title: All + title_enable: false + title: '' + default_argument_type: user + default_argument_options: + user: false + default_argument_skip_url: false + summary_options: + base_path: '' + count: true + items_per_page: 25 + override: false + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: false + validate: + type: none + fail: 'not found' + validate_options: { } + break_phrase: false + not: false + entity_type: profile + entity_field: uid + plugin_id: numeric + type: + id: type + table: profile + field: type + relationship: none + group_type: group + admin_label: '' + default_action: ignore + exception: + value: all + title_enable: false + title: All + title_enable: false + title: '' + default_argument_type: fixed + default_argument_options: + argument: '' + default_argument_skip_url: false + summary_options: + base_path: '' + count: true + items_per_page: 25 + override: false + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: true + validate: + type: 'entity:profile_type' + fail: 'not found' + validate_options: { } + glossary: false + limit: 0 + case: none + path_case: none + transform_dash: false + break_phrase: false + entity_type: profile + entity_field: type + plugin_id: string + status: + id: status + table: profile + field: status + relationship: none + group_type: group + admin_label: '' + default_action: ignore + exception: + value: all + title_enable: false + title: All + title_enable: false + title: '' + default_argument_type: fixed + default_argument_options: + argument: '' + default_argument_skip_url: false + summary_options: + base_path: '' + count: true + items_per_page: 25 + override: false + summary: + sort_order: asc + number_of_records: 0 + format: default_summary + specify_validation: false + validate: + type: none + fail: 'not found' + validate_options: { } + break_phrase: false + not: false + entity_type: profile + entity_field: status + plugin_id: numeric + display_extenders: { } + title: Profiles + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_interface' + - url + profile_type_listing: + display_plugin: embed + id: profile_type_listing + display_title: 'Profile type listing' + position: 1 + display_options: + display_extenders: { } + display_description: '' + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_interface' + - url + user_view: + display_plugin: embed + id: user_view + display_title: 'User view' + position: 2 + display_options: + display_extenders: { } + fields: + rendered_entity: + id: rendered_entity + table: profile + field: rendered_entity + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + view_mode: default + entity_type: profile + plugin_id: rendered_entity + defaults: + fields: false + display_description: '' + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_interface' + - url diff --git a/sites/all/modules/contrib/users/profile/config/schema/profile.schema.yml b/sites/all/modules/contrib/users/profile/config/schema/profile.schema.yml new file mode 100644 index 000000000..000fc746a --- /dev/null +++ b/sites/all/modules/contrib/users/profile/config/schema/profile.schema.yml @@ -0,0 +1,53 @@ +profile.type.*: + type: config_entity + label: 'Profile type settings' + mapping: + id: + type: string + label: 'Machine-readable name' + uuid: + type: string + label: 'UUID' + label: + type: label + label: 'Label' + registration: + type: boolean + label: 'Registration' + multiple: + type: boolean + label: 'Allow multiple profiles' + roles: + label: 'Allowed roles' + type: sequence + sequence: + type: string + weight: + type: integer + label: 'Weight' + status: + type: boolean + label: 'Status' + langcode: + type: string + label: 'Default language' + +field.field.*.*.*.third_party.profile: + type: mapping + label: 'Profile private' + mapping: + profile_private: + type: boolean + label: 'Whether this field is private or not.' + +action.configuration.profile_delete_action: + type: action_configuration_default + label: 'Delete profile configuration' + +action.configuration.profile_publish_action: + type: action_configuration_default + label: 'Publish selected profile configuration' + +action.configuration.profile_unpublish_action: + type: action_configuration_default + label: 'Unpublish selected profile configuration' diff --git a/sites/all/modules/contrib/users/profile/css/profile.base.css b/sites/all/modules/contrib/users/profile/css/profile.base.css new file mode 100644 index 000000000..dde5e01a4 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/css/profile.base.css @@ -0,0 +1,11 @@ +.profile-title { + font-weight: bold; +} + +.profile-item { + margin-bottom: 20px; +} + +.profile-item-action-links { + margin-bottom: 10px; +} diff --git a/sites/all/modules/contrib/users/profile/profile.api.php b/sites/all/modules/contrib/users/profile/profile.api.php new file mode 100644 index 000000000..d299016c1 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.api.php @@ -0,0 +1,51 @@ +getType() == 'secret' && !\Drupal::currentUser()->hasPermission('custom permission')) { + return FALSE; + } + // For profiles other than the default profile grant access. + if ($profile->getType() != 'main' && \Drupal::currentUser()->hasPermission('custom permission')) { + return TRUE; + } + // In other cases do not alter access. + return NULL; +} + +/** + * @} + */ diff --git a/sites/all/modules/contrib/users/profile/profile.info.yml b/sites/all/modules/contrib/users/profile/profile.info.yml new file mode 100644 index 000000000..27e628ab4 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.info.yml @@ -0,0 +1,17 @@ +name: Profile +type: module +description: 'Provides configurable user profiles.' +# core: 8.x +configure: entity.profile_type.collection +dependencies: + - user + - entity + - field + - views + - system (>=8.1.0) + +# Information added by Drupal.org packaging script on 2016-08-31 +version: '8.x-1.0-alpha5+4-dev' +core: '8.x' +project: 'profile' +datestamp: 1472611474 diff --git a/sites/all/modules/contrib/users/profile/profile.libraries.yml b/sites/all/modules/contrib/users/profile/profile.libraries.yml new file mode 100644 index 000000000..8b1a352b4 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.libraries.yml @@ -0,0 +1,5 @@ +drupal.profile-items: + version: VERSION + css: + component: + css/profile.base.css: {} diff --git a/sites/all/modules/contrib/users/profile/profile.links.action.yml b/sites/all/modules/contrib/users/profile/profile.links.action.yml new file mode 100644 index 000000000..0ed924ea8 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.links.action.yml @@ -0,0 +1,5 @@ +profile_type_add: + route_name: entity.profile_type.add_form + title: 'Add profile type' + appears_on: + - entity.profile_type.collection diff --git a/sites/all/modules/contrib/users/profile/profile.links.menu.yml b/sites/all/modules/contrib/users/profile/profile.links.menu.yml new file mode 100644 index 000000000..533fbd53e --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.links.menu.yml @@ -0,0 +1,5 @@ +entity.profile.collection: + title: 'User profiles' + description: 'Manage profile types, including fields, and profiles for users.' + route_name: entity.profile.collection + parent: user.admin_index diff --git a/sites/all/modules/contrib/users/profile/profile.links.task.yml b/sites/all/modules/contrib/users/profile/profile.links.task.yml new file mode 100644 index 000000000..0adc857d2 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.links.task.yml @@ -0,0 +1,14 @@ +# Tabs on the profile admin page +entity.profile.collection_tab: + route_name: entity.profile.collection + base_route: entity.profile.collection + title: 'Profiles' + +entity.profile_type.collection_tab: + route_name: entity.profile_type.collection + base_route: entity.profile.collection + title: 'Profile types' + +# Add tabs for each profile type +entity.profile.user_profile_form: + deriver: Drupal\profile\Plugin\Derivative\ProfileLocalTask diff --git a/sites/all/modules/contrib/users/profile/profile.module b/sites/all/modules/contrib/users/profile/profile.module new file mode 100644 index 000000000..bc5718924 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.module @@ -0,0 +1,345 @@ +' . t('About') . ''; + $output .= '

      ' . t('The Profile module provides a fieldable entity, that allows administrators to define different sets of fields for user profiles, which are then displayed in the My Account section. This permits users of a site to share more information about themselves, and can help community-based sites organize users around specific information.', ['!user' => Url::fromRoute('user.page')]) . '

      '; + $output .= '
      '; + $output .= '
      ' . t('Types of profiles') . '
      '; + $output .= '
      ' . t('Profile types provide a way of grouping similar data for user profiles e.g. Personal information, Work etc. A default "Personal information type is provided. You may create more types and manage fields for each type from the Profile types admin page. When creating a new profile type, you will be able to specify whether a user may create multiple profiles or make the profile form available when registering a new user.', ['!profile-types' => Url::fromRoute('entity.profile_type.collection')]) . '
      '; + $output .= '
      ' . t('Creating profiles') . '
      '; + $output .= '
      ' . t('A user will see tabs they have access to, when editing their main user account e.g. "Add personal information profile". The visibility of a tab depends on whether they can create multiple profiles or if they haven\'t created a profile of the type that doesn\'t allow multiple instances.') . '
      '; + $output .= '
      '; + return $output; + } +} + +/** + * Implements hook_theme(). + */ +function profile_theme() { + return [ + 'profile' => [ + 'render element' => 'elements', + ], + ]; +} + +/** + * Prepares variables for profile templates. + * + * Default template: profile.html.twig. + * + * @param array $variables + * An associative array containing: + * - elements: An associative array containing rendered fields. + * - attributes: HTML attributes for the containing element. + */ +function template_preprocess_profile(array &$variables) { + /** @var Drupal\profile\Entity\ProfileInterface $profile */ + $profile = $variables['elements']['#profile']; + + $variables['profile'] = $profile; + $variables['url'] = $profile->toUrl(); + // Helpful $content variable for templates. + $variables['content'] = []; + foreach (\Drupal\Core\Render\Element::children($variables['elements']) as $key) { + $variables['content'][$key] = $variables['elements'][$key]; + } +} + +/** + * Implements hook_user_view(). + */ +function profile_user_view(array &$build, UserInterface $account, EntityViewDisplayInterface $display, $view_mode) { + // Iterate through each bundle and see if it's component exists. + foreach (ProfileType::loadMultiple() as $bundle) { + $component_key = 'profile_' . $bundle->id(); + if ($display->getComponent($component_key)) { + + // Embed the view of active profiles for profile type. + $build[$component_key] = [ + '#type' => 'view', + '#name' => 'profiles', + '#display_id' => 'user_view', + '#arguments' => [$account->id(), $bundle->id(), 1], + '#embed' => TRUE, + '#title' => $bundle->label(), + '#pre_render' => [ + ['\Drupal\views\Element\View', 'preRenderViewElement'], + 'profile_views_add_title_pre_render', + ], + ]; + } + } +} + +/** + * Implements hook_entity_extra_field_info(). + */ +function profile_entity_extra_field_info() { + $extra = []; + + // Add each profile type as an extra field for display. Enabled by default. + foreach (ProfileType::loadMultiple() as $bundle) { + $extra['user']['user']['display']['profile_' . $bundle->id()] = array( + 'label' => $bundle->label(), + 'description' => t('Display @type profiles', ['@type' => $bundle->label()]), + 'weight' => 10, + 'visible' => TRUE, + ); + } + return $extra; +} + +/** + * Implements hook_user_delete(). + */ +function profile_user_delete(EntityInterface $entity) { + $list = \Drupal::entityTypeManager() + ->getStorage('profile') + ->loadByProperties([ + 'uid' => $entity->id(), + ]); + + foreach ($list as $profile) { + $profile->delete(); + } +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function profile_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { + $field = $form_state->getFormObject()->getEntity(); + if ($field->getTargetEntityTypeId() != 'profile') { + return; + } + + $form['field']['profile']['profile_private'] = [ + '#type' => 'checkbox', + '#title' => t('This is a private field.'), + '#default_value' => $field->getThirdPartySetting('profile', 'profile_private', FALSE), + ]; + $form['actions']['submit']['#submit'][] = 'profile_form_field_config_edit_form_submit'; +} + +/** + * Form submission handler for profile_form_field_config_edit_form_alter. + * + * @param array $form + * The form array. + * @param FormStateInterface $form_state + * The form state. + */ +function profile_form_field_config_edit_form_submit(array $form, FormStateInterface $form_state) { + $field = $form_state->getFormObject()->getEntity(); + $form_fields = &$form_state->getValues(); + + // If the private option is checked, update settings. + if ($form_fields['profile_private']) { + $field->setThirdPartySetting('profile', 'profile_private', TRUE); + $field->save(); + } + else { + $field->unsetThirdPartySetting('profile', 'profile_private'); + $field->save(); + } +} + +/** + * Implements hook_form_FORM_ID_alter(). + * + * Add available profile forms to the user registration form. + */ +function profile_form_user_register_form_alter(&$form, FormStateInterface $form_state) { + $attached_profile_form = FALSE; + $weight = 90; + + /** @var ProfileType[] $profile_types */ + $profile_types = ProfileType::loadMultiple(); + foreach ($profile_types as $profile_type) { + $instances = array_filter(\Drupal::service('entity_field.manager')->getFieldDefinitions('profile', $profile_type->id()), function ($field_definition) { + return $field_definition instanceof FieldConfigInterface; + }); + + if ($profile_type->getRegistration() === TRUE && count($instances)) { + $property = ['profiles', $profile_type->id()]; + $profile = $form_state->get($property); + if (empty($profile)) { + $profile = Profile::create([ + 'type' => $profile_type->id(), + 'langcode' => $profile_type->language() ? + $profile_type->language() : \Drupal::languageManager()->getDefaultLanguage()->getId(), + ]); + + // Attach profile entity form. + $form_state->set($property, $profile); + } + $form_state->set('form_display_' . $profile_type->id(), EntityFormDisplay::collectRenderDisplay($profile, 'default')); + $form['entity_' . $profile_type->id()] = [ + '#type' => 'details', + '#title' => $profile_type->label(), + '#tree' => TRUE, + '#parents' => ['entity_' . $profile_type->id()], + '#weight' => ++$weight, + '#open' => TRUE, + ]; + $form_state + ->get('form_display_' . $profile_type->id()) + ->buildForm($profile, $form['entity_' . $profile_type->id()], $form_state); + + $attached_profile_form = TRUE; + } + } + + if ($attached_profile_form) { + $form['actions']['submit']['#validate'][] = 'profile_form_user_register_form_validate'; + $form['actions']['submit']['#submit'][] = 'profile_form_user_register_form_submit'; + } +} + +/** + * Extra form validation handler for the user registration form. + */ +function profile_form_user_register_form_validate(array &$form, FormStateInterface $form_state) { + $profiles = $form_state->get('profiles'); + if (!empty($profiles)) { + foreach ($profiles as $bundle => $entity) { + /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ + $form_display = $form_state->get('form_display_' . $bundle); + $form_display->extractFormValues($entity, $form['entity_' . $bundle], $form_state); + $form_display->validateFormValues($entity, $form['entity_' . $bundle], $form_state); + } + } + // Entity was validated in entityFormValidate(). This will prevent validation + // exception from being thrown. + $form_state->getFormObject()->getEntity()->validate(); +} + +/** + * Extra form submission handler for the user registration form. + */ +function profile_form_user_register_form_submit(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\Core\Session\AccountInterface $account */ + $account = $form_state->getFormObject()->getEntity(); + $profiles = $form_state->get('profiles'); + if (!empty($profiles)) { + foreach ($profiles as $bundle => $entity) { + $entity->setOwnerId($account->id()); + $entity->setActive(TRUE); + $entity->save(); + } + } +} + +/** + * Pre render callback for profile embedded views to ensure a title is set. + * @param $element + * + * @return mixed + */ +function profile_views_add_title_pre_render($element) { + /** @var \Drupal\views\ViewExecutable $view */ + if (isset($element['#title'])) { + $view = $element['view_build']['#view']; + $view->setTitle($element['#title']); + } + + return $element; +} + +/** + * Implements hook_preprocess_HOOK(). + */ +function profile_preprocess_views_view(&$variables) { + // We have to manually add back the title since it was removed by Views. + // @see template_preprocess_views_view() + /** @var \Drupal\views\ViewExecutable $view */ + $view = $variables['view']; + if ($view->storage->id() == 'profiles' && !empty($view->result)) { + // Test access to the profile. + /** @var \Drupal\profile\Entity\profile $entity */ + $entity = reset($view->result)->_entity; + if ($entity->access('view')) { + $variables['title'] = $view->getTitle(); + } + } +} + +/** + * Implements hook_views_data_alter(). + * + * Adds a relationship from the user table to its' profile entity. + */ +function profile_views_data_alter(&$data) { + $data['users_field_data']['profile']['relationship'] = [ + 'title' => t('Profile'), + 'label' => t('Profile'), + 'group' => 'User', + 'help' => t('Reference to the profile of a user.'), + 'id' => 'standard', + 'base' => 'profile', + 'base field' => 'uid', + 'field' => 'uid', + ]; +} + +/** + * Implements hook_theme_suggestions_HOOK(). + */ +function profile_theme_suggestions_profile(array $variables) { + $original = $variables['theme_hook_original']; + $entity = $variables['elements']['#profile']; + $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_'); + + $suggestions = []; + $suggestions[] = $original; + $suggestions[] = $original . '__' . $sanitized_view_mode; + $suggestions[] = $original . '__' . $entity->bundle(); + $suggestions[] = $original . '__' . $entity->bundle() . '__' . $sanitized_view_mode; + $suggestions[] = $original . '__' . $entity->id(); + $suggestions[] = $original . '__' . $entity->id() . '__' . $sanitized_view_mode; + + return $suggestions; +} diff --git a/sites/all/modules/contrib/users/profile/profile.permissions.yml b/sites/all/modules/contrib/users/profile/profile.permissions.yml new file mode 100644 index 000000000..2f9ed375f --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.permissions.yml @@ -0,0 +1,13 @@ +administer profile types: + title: 'Administer profile types' + restrict access: true +administer profiles: + title: 'Administer profiles' + restrict access: true +bypass profile access: + title: 'Bypass profile access control' + description: 'View, edit and delete all user profiles, including private field values, regardless of permission restrictions.' + restrict access: true + +permission_callbacks: + - \Drupal\profile\ProfilePermissions::profileTypePermissions diff --git a/sites/all/modules/contrib/users/profile/profile.routing.yml b/sites/all/modules/contrib/users/profile/profile.routing.yml new file mode 100644 index 000000000..f655f59b5 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.routing.yml @@ -0,0 +1,60 @@ +# edit_form, delete_form routes are generated by DefaultHtmlRouteProvider. +entity.profile.collection: + path: '/admin/config/people/profiles' + defaults: + _entity_list: 'profile' + _title: 'Profiles' + requirements: + _permission: 'administer profiles' + +entity.profile_type.collection: + path: '/admin/config/people/profiles/types' + defaults: + _entity_list: 'profile_type' + _title: 'Profile types' + requirements: + _permission: 'administer profile types' + +entity.profile_type.add_form: + path: '/admin/config/people/profiles/types/add' + defaults: + _entity_form: profile_type.add + _title: 'Add' + requirements: + _permission: 'administer profile types' + +entity.profile.add_form: + path: '/user/{user}/profile/{profile_type}' + defaults: + _controller: '\Drupal\profile\Controller\ProfileController::addProfile' + _title: 'Add new profile' + operation: 'add' + requirements: + _profile_access_check: 'profile:{profile_type}' + +entity.profile.canonical: + path: '/profile/{profile}' + defaults: + _controller: '\Drupal\profile\Controller\ProfileViewController::view' + _title_callback: '\Drupal\profile\Controller\ProfileViewController::title' + requirements: + _entity_access: 'profile.view' + +entity.profile.multiple_delete_confirm: + path: '/admin/content/profile/delete' + defaults: + _form: '\Drupal\profile\Form\DeleteMultiple' + requirements: + _permission: 'administer profiles' + +entity.profile.set_default: + path: '/profile/{profile}/set-default' + defaults: + _controller: '\Drupal\profile\Controller\ProfileController::setDefault' + options: + parameters: + profile: + type: entity:profile + requirements: + _entity_access: 'profile.edit' + _csrf_token: 'TRUE' diff --git a/sites/all/modules/contrib/users/profile/profile.services.yml b/sites/all/modules/contrib/users/profile/profile.services.yml new file mode 100644 index 000000000..97b7ed71b --- /dev/null +++ b/sites/all/modules/contrib/users/profile/profile.services.yml @@ -0,0 +1,7 @@ +services: + + access_check.profile_type.profile_access: + class: Drupal\profile\Access\ProfileAccessCheck + arguments: ['@entity.manager'] + tags: + - { name: access_check, applies_to: _profile_access_check } diff --git a/sites/all/modules/contrib/users/profile/src/Access/ProfileAccessCheck.php b/sites/all/modules/contrib/users/profile/src/Access/ProfileAccessCheck.php new file mode 100644 index 000000000..90e121063 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Access/ProfileAccessCheck.php @@ -0,0 +1,72 @@ +entityTypeManager = $entity_type_manager; + } + + /** + * Checks access to the profile add page for the profile type. + * + * @param \Symfony\Component\Routing\Route $route + * The route to check against. + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * @param \Drupal\profile\Entity\ProfileTypeInterface $profile_type + * The profile type entity. + * + * @return bool|\Drupal\Core\Access\AccessResultInterface + * The access result. + */ + public function access(Route $route, AccountInterface $account, ProfileTypeInterface $profile_type = NULL) { + $access_control_handler = $this->entityTypeManager->getAccessControlHandler('profile'); + + if ($account->hasPermission('administer profile types')) { + return AccessResult::allowed()->cachePerPermissions(); + } + $operation = $route->getRequirement('_profile_access_check'); + if ($operation == 'add') { + return $access_control_handler->access($profile_type, $operation, $account, TRUE); + } + + if ($profile_type) { + return $access_control_handler->createAccess($profile_type->id(), $account, [], TRUE); + } + // If checking whether a profile of any type may be created. + foreach ($this->entityTypeManager->getStorage('profile_type')->loadMultiple() as $profile_type) { + if (($access = $access_control_handler->createAccess($profile_type->id(), $account, [], TRUE)) && $access->isAllowed()) { + return $access; + } + } + + // No opinion. + return AccessResult::neutral(); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Controller/ProfileController.php b/sites/all/modules/contrib/users/profile/src/Controller/ProfileController.php new file mode 100644 index 000000000..915fc3a32 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Controller/ProfileController.php @@ -0,0 +1,177 @@ +entityTypeManager()->getStorage('profile')->create([ + 'uid' => $user->id(), + 'type' => $profile_type->id(), + ]); + + return $this->entityFormBuilder()->getForm($profile, 'add', ['uid' => $user->id(), 'created' => REQUEST_TIME]); + } + + /** + * Provides the profile edit form. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. + * @param \Drupal\user\UserInterface $user + * The user account. + * @param \Drupal\profile\Entity\ProfileInterface $profile + * The profile entity to edit. + * + * @return array + * The profile edit form. + */ + public function editProfile(RouteMatchInterface $route_match, UserInterface $user, ProfileInterface $profile) { + return $this->entityFormBuilder()->getForm($profile, 'edit'); + } + + /** + * Provides profile delete form. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. + * @param \Drupal\user\UserInterface $user + * The user account. + * @param \Drupal\profile\Entity\ProfileTypeInterface $profile_type + * The profile type entity for the profile. + * @param int $id + * The id of the profile to delete. + * + * @return array + * Returns form array. + */ + public function deleteProfile(RouteMatchInterface $route_match, UserInterface $user, ProfileTypeInterface $profile_type, $id) { + return $this->entityFormBuilder()->getForm(Profile::load($id), 'delete'); + } + + /** + * The _title_callback for the entity.profile.add_form route. + * + * @param \Drupal\profile\Entity\ProfileTypeInterface $profile_type + * The current profile type. + * + * @return string + * The page title. + */ + public function addPageTitle(ProfileTypeInterface $profile_type) { + // @todo: edit profile uses this form too? + return $this->t('Create @label', ['@label' => $profile_type->label()]); + } + + /** + * Provides profile create form. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. + * @param \Drupal\user\UserInterface $user + * The user account. + * @param \Drupal\profile\Entity\ProfileTypeInterface $profile_type + * The profile type entity for the profile. + * + * @return array + * Returns form array. + */ + public function userProfileForm(RouteMatchInterface $route_match, UserInterface $user, ProfileTypeInterface $profile_type) { + /** @var \Drupal\profile\Entity\ProfileType $profile_type */ + + /** @var \Drupal\profile\Entity\ProfileInterface|bool $active_profile */ + $active_profile = $this->entityTypeManager()->getStorage('profile') + ->loadByUser($user, $profile_type->id()); + + // If the profile type does not support multiple, only display an add form + // if there are no entities, or an edit for the current. + if (!$profile_type->getMultiple()) { + + // If there is an active profile, provide edit form. + if ($active_profile) { + return $this->editProfile($route_match, $user, $active_profile); + } + + // Else show the add form. + return $this->addProfile($route_match, $user, $profile_type); + } + // Display active, and link to create a profile. + else { + $build = []; + + // If there is no active profile, display add form. + if (!$active_profile) { + return $this->addProfile($route_match, $user, $profile_type); + } + + $build['add_profile'] = Link::createFromRoute( + $this->t('Add new @type', ['@type' => $profile_type->label()]), + "entity.profile.type.{$profile_type->id()}.user_profile_form.add", + ['user' => \Drupal::currentUser()->id(), 'profile_type' => $profile_type->id()]) + ->toRenderable(); + + // Render the active profiles. + $build['active_profiles'] = [ + '#type' => 'view', + '#name' => 'profiles', + '#display_id' => 'profile_type_listing', + '#arguments' => [$user->id(), $profile_type->id(), 1], + '#embed' => TRUE, + '#title' => $this->t('Active @type', ['@type' => $profile_type->label()]), + '#pre_render' => [ + ['\Drupal\views\Element\View', 'preRenderViewElement'], + 'profile_views_add_title_pre_render', + ], + ]; + + return $build; + } + } + + /** + * Mark profile as default. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $routeMatch + * The route match. + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + * A redirect back to the currency listing. + */ + public function setDefault(RouteMatchInterface $routeMatch) { + $profile = $routeMatch->getParameter('profile'); + $profile->setDefault(TRUE); + $profile->save(); + + drupal_set_message($this->t('The %label profile has been marked as default.', ['%label' => $profile->label()])); + + $url = $profile->urlInfo('collection'); + return $this->redirect($url->getRouteName(), $url->getRouteParameters(), $url->getOptions()); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Controller/ProfileViewController.php b/sites/all/modules/contrib/users/profile/src/Controller/ProfileViewController.php new file mode 100644 index 000000000..b99985dcf --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Controller/ProfileViewController.php @@ -0,0 +1,61 @@ + \Drupal::entityTypeManager() + ->getViewBuilder($profile->getEntityTypeId()) + ->view($profile, $view_mode, $langcode), + ]; + $build['#title'] = $profile->label(); + + foreach ($profile->uriRelationships() as $rel) { + // Set the profile path as the canonical URL to prevent duplicate content. + $build['#attached']['html_head_link'][] = [ + [ + 'rel' => $rel, + 'href' => $profile->toUrl($rel)->toString(), + ], + TRUE, + ]; + + if ($rel == 'canonical') { + // Set the non-aliased canonical path as a default shortlink. + $build['#attached']['html_head_link'][] = [ + [ + 'rel' => 'shortlink', + 'href' => $profile->toUrl($rel, ['alias' => TRUE])->toString(), + ], + TRUE, + ]; + } + } + return $build; + } + + /** + * The _title_callback for the page that renders a profile entity. + * + * @param \Drupal\Core\Entity\EntityInterface $profile + * The current profile. + * + * @return string + * The page title. + */ + public function title(EntityInterface $profile) { + return $this->entityManager->getTranslationFromContext($profile)->label(); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Entity/Profile.php b/sites/all/modules/contrib/users/profile/src/Entity/Profile.php new file mode 100644 index 000000000..6902ec43a --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Entity/Profile.php @@ -0,0 +1,275 @@ +setLabel(t('Owner')) + ->setDescription(t('The user that owns this profile.')) + ->setRevisionable(TRUE) + ->setSetting('target_type', 'user') + ->setSetting('handler', 'default'); + + $fields['status'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Active status')) + ->setDescription(t('A boolean indicating whether the profile is active.')) + ->setDefaultValue(TRUE) + ->setRevisionable(TRUE); + + $fields['is_default'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Default')) + ->setDescription(t('A boolean indicating whether the profile is the default one.')) + ->setRevisionable(TRUE); + + $fields['created'] = BaseFieldDefinition::create('created') + ->setLabel(t('Created')) + ->setDescription(t('The time that the profile was created.')) + ->setRevisionable(TRUE); + + $fields['changed'] = BaseFieldDefinition::create('changed') + ->setLabel(t('Changed')) + ->setDescription(t('The time that the profile was last edited.')) + ->setRevisionable(TRUE); + + return $fields; + } + + /** + * Overrides Entity::id(). + */ + public function id() { + return $this->get('profile_id')->value; + } + + /** + * {@inheritdoc} + */ + public function label() { + $profile_type = ProfileType::load($this->bundle()); + return + t('@type profile of @username (uid: @uid)', + [ + '@type' => $profile_type->label(), + '@username' => $this->getOwner()->getDisplayName(), + '@uid' => $this->getOwnerId(), + ]); + } + + /** + * {@inheritdoc} + */ + public function getType() { + return $this->bundle(); + } + + /** + * {@inheritdoc} + */ + public function setType($type) { + $this->set('type', $this->bundle()); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getOwnerId() { + return $this->get('uid')->target_id; + } + + /** + * {@inheritdoc} + */ + public function setOwnerId($uid) { + $this->set('uid', $uid); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getOwner() { + return $this->get('uid')->entity; + } + + /** + * {@inheritdoc} + */ + public function setOwner(UserInterface $account) { + $this->set('uid', $account->id()); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getCreatedTime() { + return $this->get('created')->value; + } + + /** + * {@inheritdoc} + */ + public function setCreatedTime($timestamp) { + $this->set('created', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRevisionCreationTime() { + return $this->get('revision_timestamp')->value; + } + + /** + * {@inheritdoc} + */ + public function setRevisionCreationTime($timestamp) { + $this->set('revision_timestamp', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRevisionAuthor() { + return $this->get('revision_uid')->entity; + } + + /** + * {@inheritdoc} + */ + public function setRevisionAuthorId($uid) { + $this->set('revision_uid', $uid); + return $this; + } + + /** + * {@inheritdoc} + */ + public function isActive() { + return (bool) $this->get('status')->value; + } + + /** + * {@inheritdoc} + */ + public function setActive($active) { + $this->set('status', $active ? PROFILE_ACTIVE : PROFILE_NOT_ACTIVE); + return $this; + } + + /** + * {@inheritdoc} + */ + public function isDefault() { + return (bool) $this->get('is_default')->value; + } + + /** + * {@inheritdoc} + */ + public function setDefault($is_default) { + $this->set('is_default', $is_default ? PROFILE_DEFAULT : PROFILE_NOT_DEFAULT); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getCacheTagsToInvalidate() { + $tags = parent::getCacheTagsToInvalidate(); + return Cache::mergeTags($tags, [ + 'user:' . $this->getOwnerId(), + 'user_view', + ]); + } + + /** + * {@inheritdoc} + */ + public function postSave(EntityStorageInterface $storage, $update = TRUE) { + /** @var \Drupal\profile\ProfileStorage $storage */ + parent::postSave($storage, $update); + + // Check if this profile is, or became the default. + if ($this->isDefault()) { + /** @var \Drupal\profile\Entity\ProfileInterface[] $profiles */ + $profiles = $storage->loadMultipleByUser($this->getOwner(), $this->getType()); + + // Ensure that all other profiles are set to not default. + foreach ($profiles as $profile) { + if ($profile->id() != $this->id() && $profile->isDefault()) { + $profile->setDefault(FALSE); + $profile->save(); + } + } + } + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Entity/ProfileInterface.php b/sites/all/modules/contrib/users/profile/src/Entity/ProfileInterface.php new file mode 100644 index 000000000..0c948bb10 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Entity/ProfileInterface.php @@ -0,0 +1,126 @@ +registration; + } + + /** + * {@inheritdoc} + */ + public function setRegistration($registration) { + $this->registration = $registration; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getMultiple() { + return $this->multiple; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($multiple) { + $this->multiple = $multiple; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getRoles() { + return $this->roles; + } + + /** + * {@inheritdoc} + */ + public function setRoles($roles) { + $this->roles = $roles; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getWeight() { + return $this->weight; + } + + /** + * {@inheritdoc} + */ + public function setWeight($weight) { + $this->weight = $weight; + return $this; + } + + /** + * {@inheritdoc} + */ + public function postSave(EntityStorageInterface $storage, $update = TRUE) { + parent::postSave($storage, $update); + + // @todo Setting ->setRebuildNeeded isn't enough. Investigate. + \Drupal::service('router.builder')->rebuild(); + } + + +} diff --git a/sites/all/modules/contrib/users/profile/src/Entity/ProfileTypeInterface.php b/sites/all/modules/contrib/users/profile/src/Entity/ProfileTypeInterface.php new file mode 100644 index 000000000..d831ddc50 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Entity/ProfileTypeInterface.php @@ -0,0 +1,73 @@ +privateTempStoreFactory = $temp_store_factory; + $this->storage = $manager->getStorage('profile'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('user.private_tempstore'), + $container->get('entity.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'profile_multiple_delete_confirm'; + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return \Drupal::translation()->formatPlural(count($this->profiles), 'Are you sure you want to delete this profile?', 'Are you sure you want to delete these profiles?'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('entity.profile.collection'); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return t('Delete'); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $this->profiles = $this->privateTempStoreFactory->get('profile_multiple_delete_confirm')->get(\Drupal::currentUser()->id()); + if (empty($this->profiles)) { + return new RedirectResponse(\Drupal::url('entity.profile.collection', [], ['absolute' => TRUE])); + } + + $form['profiles'] = [ + '#theme' => 'item_list', + '#items' => array_map(function ($profile) { + return $profile->label(); + }, $this->profiles), + ]; + $form = parent::buildForm($form, $form_state); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + if ($form_state->getValue('confirm') && !empty($this->profiles)) { + $this->storage->delete($this->profiles); + $this->privateTempStoreFactory->get('profile_multiple_delete_confirm')->delete(\Drupal::currentUser()->id()); + $count = count($this->profiles); + $this->logger('content')->notice('Deleted @count profiles.', ['@count' => $count]); + drupal_set_message(\Drupal::translation()->formatPlural($count, 'Deleted 1 profile.', 'Deleted @count profiles.')); + } + $form_state->setRedirect('entity.profile.collection'); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Form/ProfileDeleteForm.php b/sites/all/modules/contrib/users/profile/src/Form/ProfileDeleteForm.php new file mode 100644 index 000000000..e6ef1ff19 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Form/ProfileDeleteForm.php @@ -0,0 +1,29 @@ + $this->entity->getOwnerId(), + ]); + } + + /** + * {@inheritdoc} + */ + protected function getRedirectUrl() { + return $this->getCancelUrl(); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Form/ProfileForm.php b/sites/all/modules/contrib/users/profile/src/Form/ProfileForm.php new file mode 100644 index 000000000..ae2ef0768 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Form/ProfileForm.php @@ -0,0 +1,89 @@ +isNew()) { + $entity->setCreatedTime(REQUEST_TIME); + } + return $entity; + } + + /** + * {@inheritdoc} + */ + protected function actions(array $form, FormStateInterface $form_state) { + $element = parent::actions($form, $form_state); + /** @var \Drupal\profile\Entity\ProfileInterface $profile */ + $profile = $this->entity; + + // Add an "Activate" button. + $element['set_default'] = $element['submit']; + $element['set_default']['#value'] = t('Save and make default'); + $element['set_default']['#weight'] = 10; + $element['set_default']['#access'] = !$profile->isDefault(); + array_unshift($element['set_default']['#submit'], [$this, 'setDefault']); + + return $element; + } + + /** + * Form submission handler for the 'set_default' action. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * A reference to a keyed array containing the current state of the form. + */ + public function setDefault(array $form, FormStateInterface $form_state) { + $form_state->setValue('is_default', TRUE); + } + + /** + * Form submission handler for the 'deactivate' action. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * A reference to a keyed array containing the current state of the form. + */ + public function deactivate(array $form, FormStateInterface $form_state) { + $form_state->setValue('status', FALSE); + $form_state->setValue('is_default', TRUE); + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + $profile_type = ProfileType::load($this->entity->bundle()); + + switch ($this->entity->save()) { + case SAVED_NEW: + drupal_set_message($this->t('%label profile has been created.', ['%label' => $profile_type->label()])); + break; + + case SAVED_UPDATED: + drupal_set_message($this->t('%label profile has been updated.', ['%label' => $profile_type->label()])); + break; + } + + $form_state->setRedirect('entity.user.canonical', [ + 'user' => $this->entity->getOwnerId(), + ]); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Form/ProfileTypeDeleteForm.php b/sites/all/modules/contrib/users/profile/src/Form/ProfileTypeDeleteForm.php new file mode 100644 index 000000000..5d1def15c --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Form/ProfileTypeDeleteForm.php @@ -0,0 +1,60 @@ +queryFactory = $query_factory; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity.query') + ); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $num_profiles = $this->queryFactory->get('profile') + ->condition('type', $this->entity->id()) + ->count() + ->execute(); + if ($num_profiles) { + $caption = '

      ' . \Drupal::translation() + ->formatPlural($num_profiles, '%type is used by 1 profile on your site. You can not remove this profile type until you have removed all of the %type profiles.', '%type is used by @count profiles on your site. You may not remove %type until you have removed all of the %type profiles.', ['%type' => $this->entity->label()]) . '

      '; + $form['#title'] = $this->entity->label(); + $form['description'] = ['#markup' => $caption]; + return $form; + } + + return parent::buildForm($form, $form_state); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Form/ProfileTypeForm.php b/sites/all/modules/contrib/users/profile/src/Form/ProfileTypeForm.php new file mode 100644 index 000000000..840f16645 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Form/ProfileTypeForm.php @@ -0,0 +1,126 @@ +entity; + + if ($this->operation == 'add') { + $form['#title'] = $this->t('Add profile type'); + } + else { + $form['#title'] = $this->t('Edit %label profile type', ['%label' => $type->label()]); + } + + $form['label'] = [ + '#title' => t('Label'), + '#type' => 'textfield', + '#default_value' => $type->label(), + '#description' => t('The human-readable name of this profile type.'), + '#required' => TRUE, + '#size' => 30, + ]; + $form['id'] = [ + '#type' => 'machine_name', + '#default_value' => $type->id(), + '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH, + '#machine_name' => [ + 'exists' => '\Drupal\profile\Entity\ProfileType::load', + 'source' => ['label'], + ], + ]; + $form['registration'] = [ + '#type' => 'checkbox', + '#title' => t('Include in user registration form'), + '#default_value' => $type->getRegistration(), + ]; + $form['multiple'] = [ + '#type' => 'checkbox', + '#title' => t('Allow multiple profiles'), + '#default_value' => $type->getMultiple(), + ]; + + $form['roles'] = [ + '#type' => 'checkboxes', + '#title' => t('Allowed roles'), + '#description' => $this->t('Limit the users that can have this profile by role.
      None will indicate that all users can have this profile type.'), + '#options' => [], + '#default_value' => $type->getRoles(), + ]; + foreach (Role::loadMultiple() as $role) { + /** @var \Drupal\user\Entity\Role $role */ + // We aren't interested in anon role. + if ($role->id() !== Role::ANONYMOUS_ID) { + $form['roles']['#options'][$role->id()] = $role->label(); + } + } + + return $this->protectBundleIdElement($form); + } + + /** + * {@inheritdoc} + */ + protected function actions(array $form, FormStateInterface $form_state) { + $actions = parent::actions($form, $form_state); + if (\Drupal::moduleHandler()->moduleExists('field_ui') && + $this->getEntity()->isNew() + ) { + $actions['save_continue'] = $actions['submit']; + $actions['save_continue']['#value'] = t('Save and manage fields'); + $actions['save_continue']['#submit'][] = [$this, 'redirectToFieldUI']; + } + return $actions; + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + $type = $this->entity; + $status = $type->save(); + + if ($status == SAVED_UPDATED) { + drupal_set_message(t('%label profile type has been updated.', ['%label' => $type->label()])); + } + else { + drupal_set_message(t('%label profile type has been created.', ['%label' => $type->label()])); + } + $form_state->setRedirect('entity.profile_type.collection'); + } + + /** + * Form submission handler to redirect to Manage fields page of Field UI. + */ + public function redirectToFieldUI(array $form, FormStateInterface $form_state) { + if ($form_state->getTriggeringElement()['#parents'][0] === 'save_continue' && $route_info = FieldUI::getOverviewRouteInfo('profile', $this->entity->id())) { + $form_state->setRedirectUrl($route_info); + } + } + + /** + * {@inheritdoc} + */ + public function delete(array $form, FormStateInterface $form_state) { + $form_state->setRedirect('entity.profile_type.delete_form', [ + 'profile_type' => $this->entity->id(), + ]); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Plugin/Action/DeleteProfile.php b/sites/all/modules/contrib/users/profile/src/Plugin/Action/DeleteProfile.php new file mode 100644 index 000000000..6b2ec57bb --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Plugin/Action/DeleteProfile.php @@ -0,0 +1,77 @@ +privateTempStore = $private_temp_store->get('profile_multiple_delete_confirm'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static($configuration, $plugin_id, $plugin_definition, $container->get('user.private_tempstore')); + } + + /** + * {@inheritdoc} + */ + public function executeMultiple(array $entities) { + $this->privateTempStore->set(\Drupal::currentUser()->id(), $entities); + } + + /** + * {@inheritdoc} + */ + public function execute($object = NULL) { + $this->executeMultiple([$object]); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\profile\Entity\ProfileInterface $object */ + return $object->access('delete', $account, $return_as_object); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Plugin/Action/PublishProfile.php b/sites/all/modules/contrib/users/profile/src/Plugin/Action/PublishProfile.php new file mode 100644 index 000000000..2a62437ca --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Plugin/Action/PublishProfile.php @@ -0,0 +1,39 @@ +setActive(TRUE); + $entity->save(); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\profile\Entity\ProfileInterface $object */ + $result = $object->access('update', $account, TRUE) + ->andIf($object->status->access('edit', $account, TRUE)); + + return $return_as_object ? $result : $result->isAllowed(); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Plugin/Action/UnpublishProfile.php b/sites/all/modules/contrib/users/profile/src/Plugin/Action/UnpublishProfile.php new file mode 100644 index 000000000..dea0dde9a --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Plugin/Action/UnpublishProfile.php @@ -0,0 +1,39 @@ +setActive(FALSE); + $entity->save(); + } + + /** + * {@inheritdoc} + */ + public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { + /** @var \Drupal\profile\Entity\ProfileInterface $object */ + $access = $object->access('update', $account, TRUE) + ->andIf($object->status->access('edit', $account, TRUE)); + + return $return_as_object ? $access : $access->isAllowed(); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Plugin/Derivative/ProfileLocalTask.php b/sites/all/modules/contrib/users/profile/src/Plugin/Derivative/ProfileLocalTask.php new file mode 100644 index 000000000..a74006f72 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Plugin/Derivative/ProfileLocalTask.php @@ -0,0 +1,66 @@ +entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_definition) { + return new static( + $base_plugin_definition, + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + $this->derivatives = []; + + // Starting weight for ordering the local tasks. + $weight = 10; + + foreach ($this->entityTypeManager->getStorage('profile_type')->loadMultiple() as $profile_type_id => $profile_type) { + $this->derivatives["profile.type.$profile_type_id"] = [ + 'title' => $profile_type->label(), + 'route_name' => "entity.profile.type.$profile_type_id.user_profile_form", + 'base_route' => 'entity.user.canonical', + 'route_parameters' => ['profile_type' => $profile_type_id], + 'weight' => ++$weight, + ] + $base_plugin_definition; + } + + return $this->derivatives; + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Plugin/EntityReferenceSelection/ProfileSelection.php b/sites/all/modules/contrib/users/profile/src/Plugin/EntityReferenceSelection/ProfileSelection.php new file mode 100644 index 000000000..55c5b64ef --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Plugin/EntityReferenceSelection/ProfileSelection.php @@ -0,0 +1,29 @@ +t('Profile types'); + return $form; + } +} diff --git a/sites/all/modules/contrib/users/profile/src/Plugin/views/field/ProfileBulkForm.php b/sites/all/modules/contrib/users/profile/src/Plugin/views/field/ProfileBulkForm.php new file mode 100644 index 000000000..97294584d --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Plugin/views/field/ProfileBulkForm.php @@ -0,0 +1,21 @@ +prepareUser($account); + + if ($account->hasPermission('bypass profile access')) { + $result = AccessResult::allowed()->cachePerPermissions(); + return $return_as_object ? $result : $result->isAllowed(); + } + + $result = parent::createAccess($entity_bundle, $account, $context, TRUE)->cachePerPermissions(); + return $return_as_object ? $result : $result->isAllowed(); + } + + /** + * {@inheritdoc} + * + * When the $operation is 'add' then the $entity is of type 'profile_type', + * otherwise $entity is of type 'profile'. + */ + protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { + $account = $this->prepareUser($account); + + $user_page = \Drupal::request()->attributes->get('user'); + + // Some times, operation edit is called update. + // Use edit in any case. + if ($operation == 'update') { + $operation = 'edit'; + } + + // Check that if profile type has require roles, the user the profile is + // being added to has any of the required roles. + if ($entity->getEntityTypeId() == 'profile') { + $profile_roles = ProfileType::load($entity->bundle())->getRoles(); + // Retrieve all user roles including locked roles. + $user_roles = $entity->getOwner()->getRoles(); + if (!empty(array_filter($profile_roles)) && !array_intersect($user_roles, $profile_roles)) { + return AccessResult::forbidden(); + } + } + elseif ($entity->getEntityTypeId() == 'profile_type') { + $profile_roles = $entity->getRoles(); + // Retrieve all user roles including locked roles. + $user_roles = User::load($user_page->id())->getRoles(); + if (!empty(array_filter($profile_roles)) && !array_intersect($user_roles, $profile_roles)) { + return AccessResult::forbidden(); + } + } + + if ($account->hasPermission('bypass profile access')) { + return AccessResult::allowed()->cachePerPermissions(); + } + elseif ( + ( + $operation == 'add' + && ( + ( + $user_page->id() == $account->id() + && $account->hasPermission($operation . ' own ' . $entity->id() . ' profile') + ) + || $account->hasPermission($operation . ' any ' . $entity->id() . ' profile') + ) + ) || ( + $operation != 'add' + && ( + ( + $entity->getOwnerId() == $account->id() + && $account->hasPermission($operation . ' own ' . $entity->getType() . ' profile') + ) + || $account->hasPermission($operation . ' any ' . $entity->getType() . ' profile') + ) + ) + ){ + return AccessResult::allowed()->cachePerPermissions(); + } + else { + // No opinion. + return AccessResult::neutral()->cachePerPermissions(); + } + } + + /** + * {@inheritdoc} + */ + protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { + return AccessResult::allowedIfHasPermissions($account, [ + 'add any ' . $entity_bundle . ' profile', + 'add own ' . $entity_bundle . ' profile', + ], 'OR'); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/ProfileHtmlRouteProvider.php b/sites/all/modules/contrib/users/profile/src/ProfileHtmlRouteProvider.php new file mode 100644 index 000000000..8f1960dea --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/ProfileHtmlRouteProvider.php @@ -0,0 +1,59 @@ + '\Drupal\profile\Controller\ProfileController::userProfileForm', + '_title_callback' => '\Drupal\profile\Controller\ProfileController::addPageTitle' + ], + ['_profile_access_check' => 'add'], + [ + 'parameters' => [ + 'user' => ['type' => 'entity:user'], + 'profile_type' => ['type' => 'entity:profile_type'], + ], + ]) + ); + $collection->add("entity.profile.type.{$profile_type->id()}.user_profile_form", $route); + + // If the profile type supports multiple, we need an additional route for + // adding new profiles. + if ($profile_type->getMultiple()) { + $route = (new Route( + "/user/{user}/{profile_type}/add", + ['_controller' => '\Drupal\profile\Controller\ProfileController::addProfile'], + ['_profile_access_check' => 'add'], + [ + 'parameters' => [ + 'user' => ['type' => 'entity:user'], + 'profile_type' => ['type' => 'entity:profile_type'], + ], + ]) + ); + $collection->add("entity.profile.type.{$profile_type->id()}.user_profile_form.add", $route); + } + } + + return $collection; + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/ProfileListBuilder.php b/sites/all/modules/contrib/users/profile/src/ProfileListBuilder.php new file mode 100644 index 000000000..aef2e5d64 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/ProfileListBuilder.php @@ -0,0 +1,140 @@ +dateFormatter = $date_formatter; + $this->renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $entity_type, + $container->get('entity.manager')->getStorage($entity_type->id()), + $container->get('date.formatter'), + $container->get('renderer') + ); + } + + /** + * {@inheritdoc} + */ + public function buildHeader() { + $header = [ + 'label' => $this->t('Label'), + 'type' => [ + 'data' => $this->t('Type'), + 'class' => [RESPONSIVE_PRIORITY_MEDIUM], + ], + 'owner' => [ + 'data' => $this->t('Owner'), + 'class' => [RESPONSIVE_PRIORITY_LOW], + ], + 'status' => $this->t('Status'), + 'is_default' => $this->t('Default'), + 'changed' => [ + 'data' => $this->t('Updated'), + 'class' => [RESPONSIVE_PRIORITY_LOW], + ], + ]; + if (\Drupal::languageManager()->isMultilingual()) { + $header['language_name'] = [ + 'data' => $this->t('Language'), + 'class' => [RESPONSIVE_PRIORITY_LOW], + ]; + } + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + /** @var \Drupal\profile\Entity\ProfileInterface $entity */ + $langcode = $entity->language()->getId(); + $uri = $entity->toUrl(); + $options = $uri->getOptions(); + $options += ($langcode != LanguageInterface::LANGCODE_NOT_SPECIFIED && isset($languages[$langcode]) ? ['language' => $languages[$langcode]] : []); + $uri->setOptions($options); + $row['label'] = $entity->toLink(); + $row['type'] = $entity->getType(); + $row['owner']['data'] = [ + '#theme' => 'username', + '#account' => $entity->getOwner(), + ]; + $row['status'] = $entity->isActive() ? $this->t('active') : $this->t('not active'); + $row['is_default'] = $entity->isDefault() ? $this->t('default') : $this->t('not default'); + $row['changed'] = $this->dateFormatter->format($entity->getChangedTime(), 'short'); + $language_manager = \Drupal::languageManager(); + if ($language_manager->isMultilingual()) { + $row['language_name'] = $language_manager->getLanguageName($langcode); + } + + return $row + parent::buildRow($entity); + } + + /** + * {@inheritdoc} + */ + public function getOperations(EntityInterface $entity) { + $operations = parent::getOperations($entity); + + if (!$entity->isDefault()) { + $operations['set_default'] = [ + 'title' => $this->t('Mark as default'), + 'url' => $entity->toUrl('set-default'), + 'parameter' => $entity, + ]; + } + + return $operations; + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/ProfilePermissions.php b/sites/all/modules/contrib/users/profile/src/ProfilePermissions.php new file mode 100644 index 000000000..22d87258d --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/ProfilePermissions.php @@ -0,0 +1,72 @@ +buildPermissions($type); + } + + return $perms; + } + + /** + * Builds a standard list of permissions for a given profile type. + * + * @param \Drupal\profile\Entity\ProfileType $profile_type + * The machine name of the profile type. + * + * @return array + * An array of permission names and descriptions. + */ + protected function buildPermissions(ProfileType $profile_type) { + $type_id = $profile_type->id(); + $type_params = ['%type' => $profile_type->label()]; + + return [ + "add own $type_id profile" => [ + 'title' => $this->t('%type: Add own profile', $type_params), + ], + "add any $type_id profile" => [ + 'title' => $this->t('%type: Add any profile', $type_params), + ], + "view own $type_id profile" => [ + 'title' => $this->t('%type: View own profile', $type_params), + ], + "view any $type_id profile" => [ + 'title' => $this->t('%type: View any profile', $type_params), + ], + "edit own $type_id profile" => [ + 'title' => $this->t('%type: Edit own profile', $type_params), + ], + "edit any $type_id profile" => [ + 'title' => $this->t('%type: Edit any profile', $type_params), + ], + "delete own $type_id profile" => [ + 'title' => $this->t('%type: Delete own profile', $type_params), + ], + "delete any $type_id profile" => [ + 'title' => $this->t('%type: Delete any profile', $type_params), + ], + ]; + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/ProfileStorage.php b/sites/all/modules/contrib/users/profile/src/ProfileStorage.php new file mode 100644 index 000000000..b98694bc2 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/ProfileStorage.php @@ -0,0 +1,51 @@ +loadByProperties([ + 'uid' => $account->id(), + 'type' => $profile_type, + 'status' => $active, + ]); + + return reset($result); + } + + /** + * {@inheritdoc} + */ + public function loadMultipleByUser(AccountInterface $account, $profile_type, $active = PROFILE_ACTIVE) { + return $this->loadByProperties([ + 'uid' => $account->id(), + 'type' => $profile_type, + 'status' => $active, + ]); + } + + /** + * {@inheritdoc} + */ + public function loadDefaultByUser(AccountInterface $account, $profile_type) { + $result = $this->loadByProperties([ + 'uid' => $account->id(), + 'type' => $profile_type, + 'status' => PROFILE_ACTIVE, + 'is_default' => TRUE, + ]); + + return reset($result); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/ProfileStorageInterface.php b/sites/all/modules/contrib/users/profile/src/ProfileStorageInterface.php new file mode 100644 index 000000000..18bea72b9 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/ProfileStorageInterface.php @@ -0,0 +1,56 @@ +randomMachineName(); + $label = !empty($label) ? $label : $this->randomMachineName(); + + $type = ProfileType::create([ + 'id' => $id, + 'label' => $label, + 'registration' => $registration, + 'roles' => $roles, + ]); + $type->save(); + + return $type; + } + + /** + * Create a user, and optionally a profile. + * + * @param \Drupal\profile\Entity\ProfileTypeInterface $profile_type + * A profile type for the created profile entity. + * @param \Drupal\user\UserInterface $user + * A user to create a profile. + * + * @return \Drupal\profile\Entity\ProfileInterface + * A profile for a user. + */ + protected function createProfile(ProfileTypeInterface $profile_type, UserInterface $user) { + $profile = Profile::create([ + 'type' => $profile_type->id(), + 'uid' => $user->id(), + ]); + $profile->save(); + return $profile; + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/ProfileTypeListBuilder.php b/sites/all/modules/contrib/users/profile/src/ProfileTypeListBuilder.php new file mode 100644 index 000000000..836a5d2f4 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/ProfileTypeListBuilder.php @@ -0,0 +1,63 @@ +toLink(NULL, 'edit-form'); + $row['registration'] = $entity->getRegistration() ? t('Yes') : t('No'); + $row['multiple'] = $entity->getMultiple() ? t('Yes') : t('No'); + return $row + parent::buildRow($entity); + } + + /** + * {@inheritdoc} + */ + public function getOperations(EntityInterface $entity) { + $operations = parent::getOperations($entity); + // Place the edit operation after the operations added by field_ui.module + // which have the weights 15, 20, 25. + if (isset($operations['edit'])) { + $operations['edit'] = [ + 'title' => t('Edit'), + 'weight' => 30, + 'url' => $entity->toUrl('edit-form'), + ]; + } + if (isset($operations['delete'])) { + $operations['delete'] = [ + 'title' => t('Delete'), + 'weight' => 35, + 'url' => $entity->toUrl('delete-form'), + ]; + } + // Sort the operations to normalize link order. + uasort($operations, [ + 'Drupal\Component\Utility\SortArray', + 'sortByWeightElement', + ]); + + return $operations; + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/ProfileViewBuilder.php b/sites/all/modules/contrib/users/profile/src/ProfileViewBuilder.php new file mode 100644 index 000000000..d5916f580 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/ProfileViewBuilder.php @@ -0,0 +1,22 @@ + t('Profile operations bulk form'), + 'help' => t('Add a form element that lets you run operations on multiple profiles.'), + 'field' => [ + 'id' => 'profile_bulk_form', + ], + ]; + + return $data; + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Tests/ProfileDefaultTest.php b/sites/all/modules/contrib/users/profile/src/Tests/ProfileDefaultTest.php new file mode 100644 index 000000000..04b9d2412 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Tests/ProfileDefaultTest.php @@ -0,0 +1,411 @@ +adminUser = $this->drupalCreateUser([ + 'access user profiles', + 'administer profiles', + 'administer profile types', + 'bypass profile access', + 'access administration pages', + ]); + + $this->user1 = User::create([ + 'name' => $this->randomMachineName(), + 'mail' => $this->randomMachineName() . '@example.com', + ]); + $this->user1->save(); + $this->user2 = User::create([ + 'name' => $this->randomMachineName(), + 'mail' => $this->randomMachineName() . '@example.com', + ]); + $this->user2->save(); + } + + /** + * Tests profiles are active by default. + */ + public function testProfileActive() { + $profile_type = $this->createProfileType('test_defaults', 'test_defaults'); + + // Create new profiles. + $profile1 = Profile::create($expected = [ + 'type' => $profile_type->id(), + 'uid' => $this->user1->id(), + ]); + $profile1->save(); + + $this->assertTrue($profile1->isActive()); + + $profile1->setActive(PROFILE_NOT_ACTIVE); + $profile1->save(); + + $this->assertFalse($profile1->isActive()); + } + + /** + * Tests default profile functionality. + */ + public function testDefaultProfile() { + $profile_type = $this->createProfileType('test_defaults', 'test_defaults'); + + // Create new profiles. + $profile1 = Profile::create($expected = [ + 'type' => $profile_type->id(), + 'uid' => $this->user1->id(), + ]); + $profile1->save(); + $profile2 = Profile::create($expected = [ + 'type' => $profile_type->id(), + 'uid' => $this->user1->id(), + ]); + $profile2->setDefault(TRUE); + $profile2->save(); + + $this->assertFalse($profile1->isDefault()); + $this->assertTrue($profile2->isDefault()); + + $profile1->setDefault(TRUE)->save(); + + $this->assertFalse(Profile::load($profile2->id())->isDefault()); + $this->assertTrue(Profile::load($profile1->id())->isDefault()); + } + + /** + * Tests loading default from storage handler. + */ + public function testLoadDefaultProfile() { + $profile_type = $this->createProfileType('test_defaults', 'test_defaults'); + + // Create new profiles. + $profile1 = Profile::create($expected = [ + 'type' => $profile_type->id(), + 'uid' => $this->user1->id(), + ]); + $profile1->setActive(TRUE); + $profile1->save(); + $profile2 = Profile::create($expected = [ + 'type' => $profile_type->id(), + 'uid' => $this->user1->id(), + ]); + $profile2->setActive(TRUE); + $profile2->setDefault(TRUE); + $profile2->save(); + + /** @var \Drupal\profile\ProfileStorageInterface $storage */ + $storage = \Drupal::entityTypeManager()->getStorage('profile'); + + $default_profile = $storage->loadDefaultByUser($this->user1, $profile_type->id()); + $this->assertEqual($profile2->id(), $default_profile->id()); + } + + /** + * Tests mark as default action. + */ + public function testDefaultAction() { + $types_data = [ + 'profile_type_0' => [ + 'label' => $this->randomMachineName(), + 'multiple' => TRUE, + ], + 'profile_type_1' => [ + 'label' => $this->randomMachineName(), + 'multiple' => TRUE, + ], + ]; + + /** @var ProfileType[] $types */ + $types = []; + foreach ($types_data as $id => $values) { + $types[$id] = $this->createProfileType($id, $values['label']); + } + + $restricted_user = $this->drupalCreateUser([ + 'administer profiles', + 'edit own ' . $types['profile_type_0']->id() . ' profile', + 'edit own ' . $types['profile_type_1']->id() . ' profile', + ]); + + $admin_user = $this->drupalCreateUser([ + 'administer profiles', + 'edit any ' . $types['profile_type_0']->id() . ' profile', + 'edit any ' . $types['profile_type_1']->id() . ' profile', + ]); + + // Create new profiles. + $profile_profile_type_0_restricted_user = Profile::create($expected = [ + 'type' => $types['profile_type_0']->id(), + 'uid' => $restricted_user->id(), + ]); + $profile_profile_type_0_restricted_user->setActive(TRUE); + $profile_profile_type_0_restricted_user->save(); + $profile_profile_type_0_user1_1 = Profile::create($expected = [ + 'type' => $types['profile_type_0']->id(), + 'uid' => $this->user1->id(), + ]); + $profile_profile_type_0_user1_1->setActive(TRUE); + $profile_profile_type_0_user1_1->save(); + $profile_profile_type_0_user1_2 = Profile::create($expected = [ + 'type' => $types['profile_type_0']->id(), + 'uid' => $this->user1->id(), + ]); + $profile_profile_type_0_user1_2->setActive(TRUE); + $profile_profile_type_0_user1_2->save(); + $profile_profile_type_1_user1 = Profile::create($expected = [ + 'type' => $types['profile_type_1']->id(), + 'uid' => $this->user1->id(), + ]); + $profile_profile_type_1_user1->setActive(TRUE); + $profile_profile_type_1_user1->save(); + $profile_profile_type_1_user2 = Profile::create($expected = [ + 'type' => $types['profile_type_1']->id(), + 'uid' => $this->user2->id(), + ]); + $profile_profile_type_1_user2->setActive(TRUE); + $profile_profile_type_1_user2->save(); + $profile_profile_type_1_user1_inactive = Profile::create($expected = [ + 'type' => $types['profile_type_1']->id(), + 'uid' => $this->user1->id(), + ]); + $profile_profile_type_1_user1_inactive->setActive(FALSE); + $profile_profile_type_1_user1_inactive->save(); + $profile_profile_type_1_user1_active = Profile::create($expected = [ + 'type' => $types['profile_type_1']->id(), + 'uid' => $this->user1->id(), + ]); + $profile_profile_type_1_user1_active->isActive(TRUE); + $profile_profile_type_1_user1_active->save(); + + // Make sure that $restricted_user is allowed to set default his own profile + // and not others'. + $this->drupalLogin($restricted_user); + + $this->drupalGet('admin/config/people/profiles'); + + $this->clickLink('Mark as default', 0); + $this->assertTrue(Profile::load($profile_profile_type_0_restricted_user->id())->isDefault()); + $this->clickLink('Mark as default', 1); + $this->assertResponse(403); + $this->drupalLogout(); + + $this->drupalLogin($admin_user); + + $this->drupalGet('admin/config/people/profiles'); + + // Mark $profile_profile_type_0_user1_1 as default + // $profile_profile_type_0_user1_2 should stay not default. + $this->clickLink('Mark as default', 0); + $this->assertTrue(Profile::load($profile_profile_type_0_user1_1->id())->isDefault()); + $this->assertFalse($profile_profile_type_0_user1_2->isDefault()); + + // Mark $profile_profile_type_0_user1_2 as default + // $profile_profile_type_0_user1_1 should become not default. + $profile_profile_type_0_user1_2->setDefault(TRUE); + $profile_profile_type_0_user1_2->save(); + $this->assertTrue($profile_profile_type_0_user1_2->isDefault()); + $this->assertFalse($profile_profile_type_0_user1_1->isDefault()); + + // Mark $profile_profile_type_1_user1 as default + // $profile_profile_type_1_user2 should stay not default. + $this->clickLink('Mark as default', 1); + $this->assertTrue(Profile::load($profile_profile_type_1_user1->id())->isDefault()); + $this->assertFalse($profile_profile_type_1_user2->isDefault()); + + // Mark $profile_profile_type_1_user2 as default + // $profile_profile_type_1_user1 should stay default. + $profile_profile_type_1_user2->setDefault(TRUE); + $profile_profile_type_1_user2->save(); + $this->assertTrue($profile_profile_type_1_user2->isDefault()); + $this->assertTrue(Profile::load($profile_profile_type_1_user1->id())->isDefault()); + + // Mark $profile_profile_type_1_user1_inactive as default + // $profile_profile_type_1_user1_active should stay not default. + $this->clickLink('Mark as default', 2); + $this->assertTrue(Profile::load($profile_profile_type_1_user1_inactive->id())->isDefault()); + $this->assertFalse($profile_profile_type_1_user1_active->isDefault()); + + // Mark $profile_profile_type_1_user1_active as default + // $profile_profile_type_1_user1_inactive should stay default. + $profile_profile_type_1_user1_active->setDefault(TRUE); + $profile_profile_type_1_user1_active->save(); + $this->assertTrue($profile_profile_type_1_user1_active->isDefault()); + $this->assertTrue(Profile::load($profile_profile_type_1_user1_inactive->id())->isDefault()); + } + + /** + * Tests whether profile default on edit is working. + */ + public function testProfileEdit() { + $types_data = [ + 'profile_type_0' => [ + 'label' => $this->randomMachineName(), + 'multiple' => TRUE, + ], + ]; + + /** @var \Drupal\profile\Entity\ProfileTypeInterface[] $types */ + $types = []; + foreach ($types_data as $id => $values) { + $types[$id] = $this->createProfileType($id, $values['label']); + } + + $admin_user = $this->drupalCreateUser([ + 'administer profiles', + 'administer users', + 'edit any ' . $types['profile_type_0']->id() . ' profile', + ]); + + // Create new profiles. + $profile1 = Profile::create($expected = [ + 'type' => $types['profile_type_0']->id(), + 'uid' => $this->user1->id(), + ]); + $profile1->save(); + $profile2 = Profile::create($expected = [ + 'type' => $types['profile_type_0']->id(), + 'uid' => $this->user1->id(), + ]); + $profile2->setDefault(TRUE); + $profile2->save(); + + $this->assertFalse($profile1->isDefault()); + $this->assertTrue($profile2->isDefault()); + + $this->drupalLogin($admin_user); + + $this->drupalPostForm("profile/{$profile1->id()}/edit", [], 'Save and make default'); + + \Drupal::entityTypeManager()->getStorage('profile')->resetCache([$profile1->id(), $profile2->id()]); + $this->assertTrue(Profile::load($profile1->id())->isDefault()); + $this->assertFalse(Profile::load($profile2->id())->isDefault()); + } + + /** + * Tests administrative-only profiles. + */ + public function testAdminOnlyProfiles() { + $id = $this->type->id(); + $field_name = $this->field->getName(); + + // Create a test user account. + $web_user = $this->drupalCreateUser(['access user profiles']); + $uid = $web_user->id(); + $value = $this->randomMachineName(); + + // Administratively enter profile field values for the new account. + $this->drupalLogin($this->adminUser); + + $edit = [ + "{$this->field->getName()}[0][value]" => $value, + ]; + $this->drupalPostForm("user/$uid/$id", $edit, 'Save and make default'); + + /** @var \Drupal\profile\Entity\ProfileInterface $profile */ + $profile = \Drupal::entityTypeManager() + ->getStorage('profile') + ->loadByUser($web_user, $this->type->id()); + $profile_id = $profile->id(); + + $this->assertEqual($profile->getType(), $this->type->id()); + + /* +// Verify that the administrator can see the profile. +$this->drupalGet("user/$uid"); +$this->assertText($this->type->label()); +$this->assertText($value); +$this->drupalLogout(); + // Verify that the user can not access, create or edit the profile. + $this->drupalLogin($web_user); + $this->drupalGet("user/$uid"); + $this->assertNoText($this->type->label()); + $this->assertNoText($value); + $this->drupalGet("user/$uid/edit/profile/$id/$profile_id"); + $this->assertResponse(403); + + // Check edit link isn't displayed. + $this->assertNoLinkByHref("user/$uid/edit/profile/$id/$profile_id"); + // Check delete link isn't displayed. + $this->assertNoLinkByHref("user/$uid/delete/profile/$id/$profile_id"); + + + // Allow users to edit own profiles. + user_role_grant_permissions(AccountInterface::AUTHENTICATED_ROLE, ["edit own $id profile"]); + + // Verify that the user is able to edit the own profile. + $value = $this->randomMachineName(); + $edit = [ + "{$field_name}[0][value]" => $value, + ]; + $this->drupalPostForm("user/$uid/edit/profile/$id/$profile_id", $edit, t('Save')); + $this->assertText(new FormattableMarkup('profile has been updated.', [])); + + + // Verify that the own profile is still not visible on the account page. + $this->drupalGet("user/$uid"); + $this->assertNoText($this->type->label()); + $this->assertNoText($value); + + // Allow users to view own profiles. + user_role_grant_permissions(AccountInterface::AUTHENTICATED_ROLE, ["view own $id profile"]); + + // Verify that the own profile is visible on the account page. + $this->drupalGet("user/$uid"); + $this->assertText($this->type->label()); + $this->assertText($value); + + // Allow users to delete own profiles. + user_role_grant_permissions(AccountInterface::AUTHENTICATED_ROLE, ["delete own $id profile"]); + + // Verify that the user can delete the own profile. + $this->drupalGet("user/$uid/edit/profile/$id/$profile_id"); + $this->clickLink(t('Delete')); + $this->drupalPostForm(NULL, [], t('Delete')); + $this->assertRaw(new FormattableMarkup('@label profile deleted.', ['@label' => $this->type->label()])); + $this->assertUrl("user/$uid"); + + // Verify that the profile is gone. + $this->drupalGet("user/$uid"); + $this->assertNoText($this->type->label()); + $this->assertNoText($value); + $this->drupalGet("user/$uid/edit/profile/$id"); + $this->assertNoText($value); +*/ + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Tests/ProfileFieldAccessTest.php b/sites/all/modules/contrib/users/profile/src/Tests/ProfileFieldAccessTest.php new file mode 100644 index 000000000..6c7a11bdb --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Tests/ProfileFieldAccessTest.php @@ -0,0 +1,91 @@ +adminUser = $this->drupalCreateUser([ + 'access user profiles', + 'administer profile types', + 'administer profile fields', + 'administer profile display', + 'bypass profile access', + ]); + + $user_permissions = [ + 'access user profiles', + "add own {$this->type->id()} profile", + "edit own {$this->type->id()} profile", + "view own {$this->type->id()} profile", + ]; + + $this->webUser = $this->drupalCreateUser($user_permissions); + $this->otherUser = $this->drupalCreateUser($user_permissions); + } + + /** + * Tests private profile field access. + */ + public function testPrivateField() { + $this->drupalLogin($this->adminUser); + + // Create a private profile field. + $edit = [ + 'new_storage_type' => 'string', + 'label' => 'Secret', + 'field_name' => 'secret', + ]; + $this->drupalPostForm("admin/config/people/profiles/types/manage/{$this->type->id()}/fields/add-field", $edit, t('Save and continue')); + $this->drupalPostForm(NULL, [], t('Save field settings')); + $edit = [ + 'profile_private' => 1, + ]; + $this->drupalPostForm(NULL, $edit, t('Save settings')); + + // Fill in a field value. + $this->drupalLogin($this->webUser); + $uid = $this->webUser->id(); + $secret = $this->randomMachineName(); + $edit = [ + 'field_secret[0][value]' => $secret, + ]; + $this->drupalPostForm("user/$uid/{$this->type->id()}", $edit, t('Save')); + + // User cache page need to be cleared to see new profile. + Cache::invalidateTags([ + 'user:' . $uid, + 'user_view', + ]); + + // Verify that the private field value appears for the profile owner. + $this->drupalGet("user/$uid"); + $this->assertText($secret); + + // Verify that the private field value appears for the administrator. + $this->drupalLogin($this->adminUser); + $this->drupalGet("user/$uid"); + $this->assertText($secret); + + // Verify that the private field value does not appear for other users. + $this->drupalLogin($this->otherUser); + $this->drupalGet("user/$uid"); + $this->assertNoText($secret); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Tests/ProfileRoleAccessTest.php b/sites/all/modules/contrib/users/profile/src/Tests/ProfileRoleAccessTest.php new file mode 100644 index 000000000..15b1a79ff --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Tests/ProfileRoleAccessTest.php @@ -0,0 +1,165 @@ +role1 = $this->drupalCreateRole([]); + $this->role2 = $this->drupalCreateRole([]); + $this->type2 = $this->createProfileType(NULL, NULL, FALSE, [$this->role2]); + $this->type3 = $this->createProfileType(NULL, NULL, FALSE, [$this->role1, $this->role2]); + } + + /** + * Tests add profile form access for a profile type that does not require + * users to have a role. + */ + public function testProfileWithNoRoles() { + // Create user with add own profile permissions. + $web_user1 = $this->drupalCreateUser(["add own {$this->type->id()} profile"]); + $this->drupalLogin($web_user1); + + // Test user without role can access add profile form. + // Expected: User can access form. + $this->drupalGet("user/{$web_user1->id()}/{$this->type->id()}"); + $this->assertResponse(200); + } + + public function testLockedRoles() { + $locked_role_type = $this->createProfileType(NULL, NULL, FALSE, [AccountInterface::AUTHENTICATED_ROLE]); + + // Create user with add own profile permissions. + $web_user1 = $this->drupalCreateUser(["add own {$locked_role_type->id()} profile"]); + $this->drupalLogin($web_user1); + + // Test user without role can access add profile form. + // Expected: User can access form. + $this->drupalGet("user/{$web_user1->id()}/{$locked_role_type->id()}"); + $this->assertResponse(200); + } + + /** + * Tests add profile form access for a profile type that requires users to + * have a single role. + */ + public function testProfileWithSingleRole() { + // Create user with add own profile permissions. + $web_user1 = $this->drupalCreateUser(["add own {$this->type2->id()} profile"]); + $this->drupalLogin($web_user1); + + // Test user without role can access add profile form. + // Expected: User cannot access form. + $this->drupalGet("user/{$web_user1->id()}/{$this->type2->id()}"); + $this->assertResponse(403); + + // Test user with wrong role can access add profile form. + // Expected: User cannot access form. + $web_user1->addRole($this->role1); + $web_user1->save(); + + $this->drupalGet("user/{$web_user1->id()}/{$this->type2->id()}"); + $this->assertResponse(403); + + // Test user with correct role can access add profile form. + // Expected: User can access form. + $web_user1->removeRole($this->role1); + $web_user1->addRole($this->role2); + $web_user1->save(); + + $this->drupalGet("user/{$web_user1->id()}/{$this->type2->id()}"); + $this->assertResponse(200); + } + + /** + * Tests add profile form access for a profile type that requires users to + * have one of multiple roles. + */ + public function testProfileWithAllRoles() { + // Create user with add own profile permissions. + $web_user1 = $this->drupalCreateUser(["add own {$this->type3->id()} profile"]); + $this->drupalLogin($web_user1); + + // Test user without role can access add profile form. + // Expected: User cannot access form. + $this->drupalGet("user/{$web_user1->id()}/{$this->type3->id()}"); + $this->assertResponse(403); + + // Test user with role 1 can access add profile form. + // Expected: User can access form. + $web_user1->addRole($this->role1); + $web_user1->save(); + + $this->drupalGet("user/{$web_user1->id()}/{$this->type3->id()}"); + $this->assertResponse(200); + + // Test user with both roles can access add profile form. + // Expected: User can access form. + $web_user1->addRole($this->role2); + $web_user1->save(); + + $this->drupalGet("user/{$web_user1->id()}/{$this->type3->id()}"); + $this->assertResponse(200); + + // Test user with role 2 can access add profile form. + // Expected: User can access form. + $web_user1->removeRole($this->role1); + $web_user1->save(); + + $this->drupalGet("user/{$web_user1->id()}/{$this->type3->id()}"); + $this->assertResponse(200); + + // Test user without role can access add profile form. + // Expected: User cannot access form. + $web_user1->removeRole($this->role2); + $web_user1->save(); + + $this->drupalGet("user/{$web_user1->id()}/{$this->type3->id()}"); + $this->assertResponse(403); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Tests/ProfileTabTest.php b/sites/all/modules/contrib/users/profile/src/Tests/ProfileTabTest.php new file mode 100644 index 000000000..637a17981 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Tests/ProfileTabTest.php @@ -0,0 +1,135 @@ +adminUser = $this->drupalCreateUser([ + 'access user profiles', + 'administer profiles', + 'administer profile types', + 'bypass profile access', + 'access administration pages', + ]); + } + + /** + * Tests tabs in profile UI. + */ + public function testProfileTabs() { + $types_data = [ + 'profile_type_0' => ['label' => $this->randomMachineName()], + 'profile_type_1' => ['label' => $this->randomMachineName()], + ]; + + /** @var ProfileType[] $types */ + $types = []; + foreach ($types_data as $id => $values) { + $types[$id] = ProfileType::create(['id' => $id] + $values); + $types[$id]->save(); + } + $this->container->get('router.builder')->rebuild(); + + $this->user1 = User::create([ + 'name' => $this->randomMachineName(), + 'mail' => $this->randomMachineName() . '@example.com', + ]); + $this->user1->save(); + $this->user2 = User::create([ + 'name' => $this->randomMachineName(), + 'mail' => $this->randomMachineName() . '@example.com', + ]); + $this->user2->save(); + + // Create new profiles. + $profile1 = Profile::create($expected = [ + 'type' => $types['profile_type_0']->id(), + 'uid' => $this->user1->id(), + ]); + $profile1->save(); + $profile2 = Profile::create($expected = [ + 'type' => $types['profile_type_1']->id(), + 'uid' => $this->user2->id(), + ]); + $profile2->save(); + + $this->drupalLogin($this->adminUser); + + $this->drupalGet('admin/config'); + $this->clickLink('User profiles'); + $this->assertResponse(200); + $this->assertUrl('admin/config/people/profiles'); + + $this->assertLink($profile1->label()); + $this->assertLinkByHref($profile2->toUrl('canonical')->toString()); + + $tasks = [ + ['entity.profile.collection', []], + ['entity.profile_type.collection', []], + ]; + + $this->assertLocalTasks($tasks, 0); + } + + /** + * Asserts local tasks in the page output. + * + * @param array $routes + * A list of expected local tasks, prepared as an array of route names and + * their associated route parameters, to assert on the page (in the given + * order). + * @param int $level + * (optional) The local tasks level to assert; 0 for primary, 1 for + * secondary. Defaults to 0. + */ + protected function assertLocalTasks(array $routes, $level = 0) { + $elements = $this->xpath('//*[contains(@class, :class)]//a', array( + ':class' => $level == 0 ? 'tabs primary' : 'tabs secondary', + )); + $this->assertTrue(count($elements), 'Local tasks found.'); + foreach ($routes as $index => $route_info) { + list($route_name, $route_parameters) = $route_info; + $expected = Url::fromRoute($route_name, $route_parameters)->toString(); + $method = ($elements[$index]['href'] == $expected ? 'pass' : 'fail'); + $this->{$method}(new FormattableMarkup('Task @number href @value equals @expected.', [ + '@number' => $index + 1, + '@value' => (string) $elements[$index]['href'], + '@expected' => $expected, + ])); + } + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Tests/ProfileTestBase.php b/sites/all/modules/contrib/users/profile/src/Tests/ProfileTestBase.php new file mode 100644 index 000000000..3a91f994d --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Tests/ProfileTestBase.php @@ -0,0 +1,139 @@ +drupalPlaceBlock('local_tasks_block'); + $this->drupalPlaceBlock('local_actions_block'); + $this->drupalPlaceBlock('page_title_block'); + + $this->type = $this->createProfileType('test', 'Test profile', TRUE); + + $id = $this->type->id(); + + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'profile_fullname', + 'entity_type' => 'profile', + 'type' => 'text', + ]); + $field_storage->save(); + + $this->field = FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => $this->type->id(), + 'label' => 'Full name', + ]); + $this->field->save(); + + // Configure the default display. + $this->display = EntityViewDisplay::load("profile.{$this->type->id()}.default"); + if (!$this->display) { + $this->display = EntityViewDisplay::create([ + 'targetEntityType' => 'profile', + 'bundle' => $this->type->id(), + 'mode' => 'default', + 'status' => TRUE, + ]); + $this->display->save(); + } + $this->display + ->setComponent($this->field->getName(), ['type' => 'string']) + ->save(); + + // Configure rhe default form. + $this->form = EntityFormDisplay::load("profile.{$this->type->id()}.default"); + if (!$this->form) { + $this->form = EntityFormDisplay::create([ + 'targetEntityType' => 'profile', + 'bundle' => $this->type->id(), + 'mode' => 'default', + 'status' => TRUE, + ]); + $this->form->save(); + } + $this->form + ->setComponent($this->field->getName(), [ + 'type' => 'string_textfield', + ])->save(); + + $this->checkPermissions([ + 'administer profile types', + "view own $id profile", + "view any $id profile", + "add own $id profile", + "add any $id profile", + "edit own $id profile", + "edit any $id profile", + "delete own $id profile", + "delete any $id profile", + ]); + + user_role_grant_permissions(AccountInterface::AUTHENTICATED_ROLE, ['access user profiles']); + $this->adminUser = $this->drupalCreateUser([ + 'administer profile types', + 'administer profiles', + "view any $id profile", + "add any $id profile", + "edit any $id profile", + "delete any $id profile", + ]); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Tests/ProfileTypeCRUDTest.php b/sites/all/modules/contrib/users/profile/src/Tests/ProfileTypeCRUDTest.php new file mode 100644 index 000000000..3e40b4d18 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Tests/ProfileTypeCRUDTest.php @@ -0,0 +1,103 @@ +adminUser = $this->drupalCreateUser([ + 'access user profiles', + 'administer profile types', + 'administer profile fields', + 'administer profile display', + 'bypass profile access', + ]); + } + + /** + * Verify that routes are created for the profile type. + */ + public function testRoutes() { + $this->drupalLogin($this->adminUser); + $type = $this->createProfileType($this->randomMachineName()); + \Drupal::service('router.builder')->rebuildIfNeeded(); + $this->drupalGet("user/{$this->adminUser->id()}/{$type->id()}"); + $this->assertResponse(200); + } + + /** + * Tests CRUD operations for profile types through the UI. + */ + public function testCRUDUI() { + $this->drupalLogin($this->adminUser); + + // Create a new profile type. + $this->drupalGet('admin/config/people/profiles/types'); + $this->assertResponse(200); + $this->clickLink(t('Add profile type')); + + $this->assertUrl('admin/config/people/profiles/types/add'); + $id = Unicode::strtolower($this->randomMachineName()); + $label = $this->randomString(); + $edit = [ + 'id' => $id, + 'label' => $label, + ]; + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertUrl('admin/config/people/profiles/types'); + $this->assertRaw(new FormattableMarkup('%label profile type has been created.', ['%label' => $label])); + $this->assertLinkByHref("admin/config/people/profiles/types/manage/$id"); + $this->assertLinkByHref("admin/config/people/profiles/types/manage/$id/fields"); + $this->assertLinkByHref("admin/config/people/profiles/types/manage/$id/display"); + $this->assertLinkByHref("admin/config/people/profiles/types/manage/$id/delete"); + + // Edit the new profile type. + $this->drupalGet("admin/config/people/profiles/types/manage/$id"); + $this->assertRaw(new FormattableMarkup('Edit %label profile type', ['%label' => $label])); + $edit = [ + 'registration' => 1, + ]; + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertUrl('admin/config/people/profiles/types'); + $this->assertRaw(new FormattableMarkup('%label profile type has been updated.', ['%label' => $label])); + + \Drupal::service('entity_type.bundle.info')->clearCachedBundles(); + + // Add a field to the profile type. + $this->drupalGet("admin/config/people/profiles/types/manage/$id/fields/add-field"); + $field_name = Unicode::strtolower($this->randomMachineName()); + $field_label = $this->randomString(); + $edit = [ + 'new_storage_type' => 'string', + 'label' => $field_label, + 'field_name' => $field_name, + ]; + $this->drupalPostForm(NULL, $edit, t('Save and continue')); + $this->drupalPostForm(NULL, [], t('Save field settings')); + $this->drupalPostForm(NULL, [], t('Save settings')); + $this->assertRaw(new FormattableMarkup('Saved %label configuration.', ['%label' => $field_label])); + + // Verify that the field is still associated with it. + $this->drupalGet("admin/config/people/profiles/types/manage/$id/fields"); + // @todo D8 core: This assertion fails for an unknown reason. Database + // contains the right values, so field_attach_rename_bundle() works + // correctly. The pre-existing field does not appear on the Manage + // fields page of the renamed bundle. Not even flushing all caches + // helps. Can be reproduced manually. + // $this->assertText(check_plain($field_label)); + } + +} diff --git a/sites/all/modules/contrib/users/profile/src/Tests/ProfileTypeMultipleTest.php b/sites/all/modules/contrib/users/profile/src/Tests/ProfileTypeMultipleTest.php new file mode 100644 index 000000000..c21a1d335 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/src/Tests/ProfileTypeMultipleTest.php @@ -0,0 +1,95 @@ +drupalLogin($this->adminUser); + + $edit = [ + 'multiple' => 1, + ]; + $this->drupalPostForm("admin/config/people/profiles/types/manage/{$this->type->id()}", $edit, t('Save')); + $this->assertRaw(new FormattableMarkup('%type profile type has been updated.', [ + '%type' => $this->type->label(), + ])); + + $web_user1 = $this->drupalCreateUser( + [ + "view own {$this->type->id()} profile", + "add own {$this->type->id()} profile", + "edit own {$this->type->id()} profile", + ] + ); + $this->drupalLogin($web_user1); + $value = $this->randomMachineName(); + + $edit = [ + "{$this->field->getName()}[0][value]" => $value, + ]; + $this->drupalPostForm("user/{$web_user1->id()}/{$this->type->id()}", $edit, t('Save')); + $this->assertRaw(new FormattableMarkup('%type profile has been created.', [ + '%type' => $this->type->label(), + ])); + + $this->drupalGet("user/{$web_user1->id()}/{$this->type->id()}"); + $this->assertLinkByHref("user/{$web_user1->id()}/{$this->type->id()}/add"); + $this->assertText($value); + + $value2 = $this->randomMachineName(); + $edit = [ + "{$this->field->getName()}[0][value]" => $value2, + ]; + $this->drupalPostForm("user/{$web_user1->id()}/{$this->type->id()}/add", $edit, t('Save')); + $this->assertRaw(new FormattableMarkup('%type profile has been created.', [ + '%type' => $this->type->label(), + ])); + + Cache::invalidateTags(['profile_view']); + + $this->drupalGet("user/{$web_user1->id()}/{$this->type->id()}"); + $this->assertText($value2); + } + + /** + * Tests the non-multiple profile type create and edit flow. + */ + public function testProfileNotMultipleFlow() { + $web_user1 = $this->createUser([ + "add own {$this->type->id()} profile", + "edit own {$this->type->id()} profile", + ]); + $this->drupalLogin($web_user1); + + // Create the profile. + $edit = [ + "{$this->field->getName()}[0][value]" => $this->randomString(), + ]; + $this->drupalPostForm("user/{$web_user1->id()}/{$this->type->id()}", $edit, 'Save and make default'); + $this->assertRaw(new FormattableMarkup('%type profile has been created.', [ + '%type' => $this->type->label(), + ])); + + // Update the profile. + $edit = [ + "{$this->field->getName()}[0][value]" => $this->randomString(), + ]; + $this->drupalPostForm("user/{$web_user1->id()}/{$this->type->id()}", $edit, t('Save')); + $this->assertRaw(new FormattableMarkup('%type profile has been updated.', [ + '%type' => $this->type->label(), + ])); + } + +} diff --git a/sites/all/modules/contrib/users/profile/templates/profile.html.twig b/sites/all/modules/contrib/users/profile/templates/profile.html.twig new file mode 100644 index 000000000..91651ee0f --- /dev/null +++ b/sites/all/modules/contrib/users/profile/templates/profile.html.twig @@ -0,0 +1,24 @@ +{# +/** + * @file + * + * Default theme implementation for profiles. + * + * Available variables: + * - content: Items for the content of the profile. + * Use 'content' to print them all, or print a subset such as + * 'content.title'. Use the following code to exclude the + * printing of a given child element: + * @code + * {{ content|without('title') }} + * @endcode + * - attributes: HTML attributes for the wrapper. + * - profile: The profile object. + * - url: The profile URL. + * + * @ingroup themeable + */ +#} + + {{ content }} +
      diff --git a/sites/all/modules/contrib/users/profile/tests/profile_test/profile_test.info.yml b/sites/all/modules/contrib/users/profile/tests/profile_test/profile_test.info.yml new file mode 100644 index 000000000..4ecc56353 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/tests/profile_test/profile_test.info.yml @@ -0,0 +1,12 @@ +name: Profile +type: module +description: 'Test module for Profile.' +# core: 8.x +dependencies: + - profile + +# Information added by Drupal.org packaging script on 2016-08-31 +version: '8.x-1.0-alpha5+4-dev' +core: '8.x' +project: 'profile' +datestamp: 1472611474 diff --git a/sites/all/modules/contrib/users/profile/tests/profile_test/test_views/views.view.users.yml b/sites/all/modules/contrib/users/profile/tests/profile_test/test_views/views.view.users.yml new file mode 100644 index 000000000..87832c3ef --- /dev/null +++ b/sites/all/modules/contrib/users/profile/tests/profile_test/test_views/views.view.users.yml @@ -0,0 +1,243 @@ +langcode: en +status: true +dependencies: + module: + - profile + - user +id: users +label: Users +module: views +description: '' +tag: '' +base_table: users_field_data +base_field: uid +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: perm + options: + perm: 'access user profiles' + cache: + type: tag + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: full + options: + items_per_page: 0 + offset: 0 + id: 0 + total_pages: null + tags: + previous: '‹ Previous' + next: 'Next ›' + first: '« First' + last: 'Last »' + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + quantity: 9 + style: + type: default + row: + type: fields + options: + default_field_elements: true + inline: { } + separator: '' + hide_empty: false + fields: + name: + id: name + table: users_field_data + field: name + entity_type: user + entity_field: name + label: '' + alter: + alter_text: false + make_link: false + absolute: false + trim: false + word_boundary: false + ellipsis: false + strip_tags: false + html: false + hide_empty: false + empty_zero: false + plugin_id: field + relationship: none + group_type: group + admin_label: '' + exclude: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_alter_empty: true + click_sort_column: value + type: user_name + settings: { } + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + profile_id: + id: profile_id + table: profile + field: profile_id + relationship: profile + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: number_integer + settings: + thousand_separator: '' + prefix_suffix: true + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: profile + entity_field: profile_id + plugin_id: field + filters: + status: + value: true + table: users_field_data + field: status + plugin_id: boolean + entity_type: user + entity_field: status + id: status + expose: + operator: '' + group: 1 + sorts: { } + title: Users + header: { } + footer: { } + empty: { } + relationships: + profile: + id: profile + table: users_field_data + field: profile + relationship: none + group_type: group + admin_label: Profile + required: true + entity_type: user + plugin_id: standard + arguments: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - user.permissions + tags: { } + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: 1 + display_options: + display_extenders: { } + path: users + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - user.permissions + tags: { } diff --git a/sites/all/modules/contrib/users/profile/tests/src/Functional/ProfileRegisterFormTest.php b/sites/all/modules/contrib/users/profile/tests/src/Functional/ProfileRegisterFormTest.php new file mode 100644 index 000000000..10adc20a3 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/tests/src/Functional/ProfileRegisterFormTest.php @@ -0,0 +1,66 @@ +type->id(); + $field_name = $this->field->getName(); + + $this->field->setRequired(TRUE); + $this->field->save(); + + // Allow registration without administrative approval and log in user + // directly after registering. + \Drupal::configFactory()->getEditable('user.settings') + ->set('register', USER_REGISTER_VISITORS) + ->set('verify_mail', 0) + ->save(); + user_role_grant_permissions(AccountInterface::AUTHENTICATED_ROLE, ['view own test profile']); + + // Verify that the additional profile field is attached and required. + $name = $this->randomMachineName(); + $pass_raw = $this->randomMachineName(); + $edit = [ + 'name' => $name, + 'mail' => $this->randomMachineName() . '@example.com', + 'pass[pass1]' => $pass_raw, + 'pass[pass2]' => $pass_raw, + ]; + $this->drupalPostForm('user/register', $edit, t('Create new account')); + + $this->assertSession()->pageTextContains(new FormattableMarkup('@name field is required.', ['@name' => $this->field->getLabel()])); + + // Verify that we can register. + $edit["entity_" . $id . "[$field_name][0][value]"] = $this->randomMachineName(); + $this->drupalPostForm(NULL, $edit, t('Create new account')); + $this->assertSession()->pageTextContains(new FormattableMarkup('Registration successful. You are now logged in.', [])); + + $new_user = user_load_by_name($name); + $this->assertTrue($new_user->isActive(), 'New account is active after registration.'); + + // Verify that a new profile was created for the new user ID. + $profile = $this->container->get('entity_type.manager') + ->getStorage('profile') + ->loadByUser($new_user, $this->type->id()); + + $this->assertEquals($profile->get($field_name)->value, $edit["entity_" . $id . "[$field_name][0][value]"], 'Field value found in loaded profile.'); + + // Verify that the profile field value appears on the user account page. + $this->drupalGet('user'); + $this->assertSession()->pageTextContains($edit["entity_" . $id . "[$field_name][0][value]"]); + } + +} diff --git a/sites/all/modules/contrib/users/profile/tests/src/Functional/ProfileTestBase.php b/sites/all/modules/contrib/users/profile/tests/src/Functional/ProfileTestBase.php new file mode 100644 index 000000000..87070439d --- /dev/null +++ b/sites/all/modules/contrib/users/profile/tests/src/Functional/ProfileTestBase.php @@ -0,0 +1,140 @@ +drupalPlaceBlock('local_tasks_block'); + $this->drupalPlaceBlock('local_actions_block'); + $this->drupalPlaceBlock('page_title_block'); + + $this->type = $this->createProfileType('test', 'Test profile', TRUE); + + $id = $this->type->id(); + + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'profile_fullname', + 'entity_type' => 'profile', + 'type' => 'text', + ]); + $field_storage->save(); + + $this->field = FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => $this->type->id(), + 'label' => 'Full name', + ]); + $this->field->save(); + + // Configure the default display. + $this->display = EntityViewDisplay::load("profile.{$this->type->id()}.default"); + if (!$this->display) { + $this->display = EntityViewDisplay::create([ + 'targetEntityType' => 'profile', + 'bundle' => $this->type->id(), + 'mode' => 'default', + 'status' => TRUE, + ]); + $this->display->save(); + } + $this->display + ->setComponent($this->field->getName(), ['type' => 'string']) + ->save(); + + // Configure rhe default form. + $this->form = EntityFormDisplay::load("profile.{$this->type->id()}.default"); + if (!$this->form) { + $this->form = EntityFormDisplay::create([ + 'targetEntityType' => 'profile', + 'bundle' => $this->type->id(), + 'mode' => 'default', + 'status' => TRUE, + ]); + $this->form->save(); + } + $this->form + ->setComponent($this->field->getName(), [ + 'type' => 'string_textfield', + ])->save(); + + $this->checkPermissions([ + 'administer profile types', + "view own $id profile", + "view any $id profile", + "add own $id profile", + "add any $id profile", + "edit own $id profile", + "edit any $id profile", + "delete own $id profile", + "delete any $id profile", + ]); + + user_role_grant_permissions(AccountInterface::AUTHENTICATED_ROLE, ['access user profiles']); + $this->adminUser = $this->drupalCreateUser([ + 'administer profile types', + 'administer profiles', + "view any $id profile", + "add any $id profile", + "edit any $id profile", + "delete any $id profile", + ]); + } + +} diff --git a/sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileAccessTest.php b/sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileAccessTest.php new file mode 100644 index 000000000..c71476b9b --- /dev/null +++ b/sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileAccessTest.php @@ -0,0 +1,210 @@ +installEntitySchema('profile'); + $this->installEntitySchema('view'); + $this->installConfig(['profile']); + $this->accessControlHandler = $this->container->get('entity_type.manager')->getAccessControlHandler('profile'); + $this->accessManager = $this->container->get('access_manager'); + $this->type = $this->createProfileType('test', 'Test profile', TRUE); + $this->createUser(); + } + + /** + * Tests profile create and permissions. + */ + public function testProfileCreateAccess() { + // Test user without any permissions. + $web_user1 = $this->createUser(); + + // Verify user does not have access to create. + $access = $this->accessControlHandler->createAccess($this->type->id(), $web_user1); + $this->assertFalse($access); + + // Verify access through route. + $this->assertFalse($this->accessManager->checkNamedRoute( + 'entity.profile.add_form', + ['user' => $web_user1->id(), 'profile_type' => $this->type->id()], + $web_user1 + )); + + + // Test user with permission to only add own profile. + $web_user2 = $this->createUser([], ["add own {$this->type->id()} profile"]); + $web_user1->addRole($web_user2->get('roles')->first()->target_id); + $web_user1->save(); + + // Verify user has access to add their own profile. + $access = $this->accessControlHandler->createAccess($this->type->id(), $web_user2); + $this->assertTrue($access); + + // User 2 can create own profile of type. + $this->assertTrue($this->accessManager->checkNamedRoute( + 'entity.profile.add_form', + ['user' => $web_user2->id(), 'profile_type' => $this->type->id()], + $web_user2 + )); + // User 1 cannot create a profile for User 2. + $this->assertFalse($this->accessManager->checkNamedRoute( + 'entity.profile.add_form', + ['user' => $web_user2->id(), 'profile_type' => $this->type->id()], + $web_user1 + )); + + // Create a new profile type. + $second_type = $this->createProfileType('test2', 'Test2 profile', TRUE); + $this->assertFalse($this->accessControlHandler->createAccess('test2', $web_user2)); + $this->assertFalse($this->accessManager->checkNamedRoute( + 'entity.profile.add_form', + ['user' => $web_user2->id(), 'profile_type' => $second_type->id()], + $web_user2 + )); + + // Test user with permission to only add any profile. + $web_user3 = $this->createUser([], ["add any {$this->type->id()} profile"]); + $this->assertTrue($this->accessControlHandler->createAccess($this->type->id(), $web_user3)); + + // Verify access through route. + $this->assertTrue($this->accessManager->checkNamedRoute( + 'entity.profile.add_form', + ['user' => $web_user3->id(), 'profile_type' => $this->type->id()], + $web_user3 + )); + $this->assertTrue($this->accessManager->checkNamedRoute( + 'entity.profile.add_form', + ['user' => $web_user2->id(), 'profile_type' => $this->type->id()], + $web_user3 + )); + } + + /** + * Tests profile view access. + */ + public function testProfileViewAccess() { + // Setup users. + $web_user1 = $this->createUser([], [ + 'access user profiles', + "view own {$this->type->id()} profile", + ]); + $web_user2 = $this->createUser([], [ + 'access user profiles', + "view any {$this->type->id()} profile", + ]); + + // Setup profiles. + $profile1 = Profile::create([ + 'uid' => $web_user1->id(), + 'type' => $this->type->id(), + ]); + $profile1->save(); + $profile2 = Profile::create([ + 'uid' => $web_user2->id(), + 'type' => $this->type->id(), + ]); + $profile2->save(); + + // Test user1 can only view own profiles. + $this->assertTrue($profile1->access('view', $web_user1)); + $this->assertFalse($profile2->access('view', $web_user1)); + + // Test user2 can view any profiles. + $this->assertTrue($profile1->access('view', $web_user2)); + $this->assertTrue($profile2->access('view', $web_user2)); + + $user_view_builder = $this->container->get('entity_type.manager')->getViewBuilder('user'); + + $this->container->get('current_user')->setAccount($web_user1); + $user2_view = $user_view_builder->view($web_user2); + $this->render($user2_view); + $this->assertNoText($this->type->label()); + + $this->container->get('current_user')->setAccount($web_user2); + $user1_view = $user_view_builder->view($web_user1); + $this->render($user1_view); + $this->assertText($this->type->label()); + } + + /** + * Tests profile edit flow and permissions. + */ + public function testProfileEditAccess() { + // Setup users. + $web_user1 = $this->createUser([], [ + "add own {$this->type->id()} profile", + "edit own {$this->type->id()} profile", + ]); + $web_user2 = $this->createUser([], [ + "add any {$this->type->id()} profile", + "edit any {$this->type->id()} profile", + ]); + + // Setup profiles. + $profile1 = Profile::create([ + 'uid' => $web_user1->id(), + 'type' => $this->type->id(), + ]); + $profile1->save(); + $profile2 = Profile::create([ + 'uid' => $web_user2->id(), + 'type' => $this->type->id(), + ]); + $profile2->save(); + + // Test user1 can only edit own profiles. + $this->assertTrue($profile1->access('edit', $web_user1)); + $this->assertFalse($profile2->access('edit', $web_user1)); + + // Test user2 can edit any profiles. + $this->assertTrue($profile1->access('edit', $web_user2)); + $this->assertTrue($profile2->access('edit', $web_user2)); + } + +} diff --git a/sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileTest.php b/sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileTest.php new file mode 100644 index 000000000..878762a05 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileTest.php @@ -0,0 +1,149 @@ +installEntitySchema('profile'); + $this->installEntitySchema('view'); + $this->installSchema('user', ['users_data']); + $this->installConfig(['profile', 'user']); + $this->profileStorage = $this->container->get('entity_type.manager') + ->getStorage('profile'); + $this->user1 = $this->createUser(); + $this->user2 = $this->createUser(); + } + + /** + * Tests the profile entity and its methods. + */ + public function testProfile() { + $types_data = [ + 'profile_type_0' => ['label' => $this->randomMachineName()], + 'profile_type_1' => ['label' => $this->randomMachineName()], + ]; + + /** @var \Drupal\profile\Entity\ProfileTypeInterface[] $types */ + $types = []; + foreach ($types_data as $id => $values) { + $types[$id] = ProfileType::create(['id' => $id] + $values); + $types[$id]->save(); + } + + + // Create a new profile. + /** @var \Drupal\profile\Entity\ProfileInterface $profile */ + $profile = $this->profileStorage->create($expected = [ + 'type' => $types['profile_type_0']->id(), + 'uid' => $this->user1->id(), + ]); + + $expected_label = new TranslatableMarkup('@type profile of @username (uid: @uid)', [ + '@type' => $types['profile_type_0']->label(), + '@username' => $this->user1->getDisplayName(), + '@uid' => $this->user1->id(), + ]); + + $this->assertEquals($expected_label, $profile->label()); + $this->assertEquals($profile->getOwnerId(), $this->user1->id()); + $this->assertEquals($profile->getCreatedTime(), REQUEST_TIME); + $this->assertEquals($profile->getChangedTime(), REQUEST_TIME); + + // Save the profile. + $profile->save(); + $this->assertEquals(REQUEST_TIME, $profile->getChangedTime()); + + // List profiles for the user and verify that the new profile appears. + $list = $this->profileStorage->loadByProperties(['uid' => $this->user1->id()]); + $list_ids = array_keys($list); + $this->assertEquals($list_ids, [$profile->id()]); + + // Create a second profile. + $user1_profile1 = $profile; + $user1_profile = $this->profileStorage->create([ + 'type' => $types['profile_type_0']->id(), + 'uid' => $this->user1->id(), + ]); + $user1_profile->save(); + + // List profiles for the user and verify that both profiles appear. + $list = $this->profileStorage->loadByProperties(['uid' => $this->user1->id()]); + $list_ids = array_keys($list); + $this->assertEquals($list_ids, [$user1_profile1->id(), $user1_profile->id()]); + + // Delete the second profile and verify that the first still exists. + $user1_profile->delete(); + $list = $this->profileStorage->loadByProperties(['uid' => $this->user1->id()]); + $list_ids = array_keys($list); + $this->assertEquals($list_ids, [$user1_profile1->id()]); + + // Create a profile for the second user. + $user2_profile1 = $this->profileStorage->create([ + 'type' => $types['profile_type_0']->id(), + 'uid' => $this->user2->id(), + ]); + $user2_profile1->save(); + + // Delete the first user and verify that all of its profiles are deleted. + $this->user1->delete(); + $list = $this->profileStorage->loadByProperties(['uid' => $this->user1->id()]); + $list_ids = array_keys($list); + $this->assertEquals($list_ids, []); + + // List profiles for the second user and verify that they still exist. + $list = $this->profileStorage->loadByProperties(['uid' => $this->user2->id()]); + $list_ids = array_keys($list); + $this->assertEquals($list_ids, [$user2_profile1->id()]); + } + +} diff --git a/sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileViewTest.php b/sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileViewTest.php new file mode 100644 index 000000000..e820b0808 --- /dev/null +++ b/sites/all/modules/contrib/users/profile/tests/src/Kernel/ProfileViewTest.php @@ -0,0 +1,106 @@ +installEntitySchema('user'); + $this->installEntitySchema('profile'); + $this->installEntitySchema('profile_type'); + + ViewTestData::createTestViews(get_class($this), ['profile_test']); + } + + /** + * Tests views relationship with multiple referenced entities. + * + * Relationship is required, so only users with profiles will be listed. + */ + public function testProfileRelationship() { + $profile_type = $this->createProfileType(); + + $user[0] = $user1 = User::create([ + 'name' => Unicode::strtolower($this->randomMachineName()), + 'status' => TRUE, + ]); + $user1->save(); + $user2 = User::create([ + 'name' => Unicode::strtolower($this->randomMachineName()), + 'status' => TRUE, + ]); + $user2->save(); + $user[1] = $user3 = User::create([ + 'name' => Unicode::strtolower($this->randomMachineName()), + 'status' => TRUE, + ]); + $user3->save(); + $user4 = User::create([ + 'name' => Unicode::strtolower($this->randomMachineName()), + 'status' => TRUE, + ]); + $user4->save(); + + $profile[0] = $this->createProfile($profile_type, $user1); + $profile[1] = $this->createProfile($profile_type, $user3); + + Views::viewsData()->clear(); + + // Check table relationship exists. + $views_data = Views::viewsData()->get('users_field_data'); + $this->assertEquals($views_data['profile']['relationship']['base'], 'profile'); + $this->assertEquals($views_data['profile']['relationship']['base field'], 'uid'); + + $view = Views::getView('users'); + $this->executeView($view); + + // Ensure values are populated for user and profiles. + foreach ($view->result as $index => $row) { + $this->assertEquals($row->uid, $user[$index]->id(), 'User ' . $user[$index]->id() . ' found on row: ' . $index); + $this->assertEquals($row->profile_users_field_data_profile_id, $profile[$index]->id(), 'Profile ' . $profile[$index]->id() . ' found on view: ' . $index); + } + } + + /** + * Create a user, and optionally a profile. + * + * @return \Drupal\user\UserInterface + * A newly created user. + */ + protected function createUser() { + $user = User::create([ + 'name' => Unicode::strtolower($this->randomMachineName()), + 'status' => TRUE, + ]); + $user->save(); + return $user; + } + +}