summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Plunkett2012-10-22 12:55:10 (GMT)
committer Tim Plunkett2012-10-22 18:11:32 (GMT)
commitc7b7c3e5ba7d17452850286fb7d36bba3136d5ee (patch)
tree7bf29de3535249ccd75303331c4ebfaa5c1da97e
parent43cd79241ef76580938e5e1e5c5d83682f79dbae (diff)
parent4942a158b745d30755ab84fb3f57d79bfad5f030 (diff)
Issue #1805996 by dawehner, tim.plunkett, damiankloip, xjm, et al: Add Views and Views UI to core.
-rw-r--r--core/modules/views/config/views.settings.yml35
-rw-r--r--core/modules/views/config/views.view.archive.yml103
-rw-r--r--core/modules/views/config/views.view.backlinks.yml123
-rw-r--r--core/modules/views/config/views.view.comments_recent.yml126
-rw-r--r--core/modules/views/config/views.view.frontpage.yml95
-rw-r--r--core/modules/views/config/views.view.glossary.yml150
-rw-r--r--core/modules/views/config/views.view.taxonomy_term.yml110
-rw-r--r--core/modules/views/config/views.view.tracker.yml150
-rw-r--r--core/modules/views/css/views.base-rtl.css3
-rw-r--r--core/modules/views/css/views.base.css32
-rw-r--r--core/modules/views/includes/ajax.inc373
-rw-r--r--core/modules/views/includes/cache.inc159
-rw-r--r--core/modules/views/js/ajax.js237
-rw-r--r--core/modules/views/js/ajax_view.js138
-rw-r--r--core/modules/views/js/base.js112
-rw-r--r--core/modules/views/js/jquery.ui.dialog.patch.js30
-rw-r--r--core/modules/views/js/views-contextual.js18
-rw-r--r--core/modules/views/lib/Drupal/views/Analyzer.php131
-rw-r--r--core/modules/views/lib/Drupal/views/ManyToOneHelper.php332
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/Type/JoinManager.php52
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/Type/PluginManager.php68
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/Type/WizardManager.php52
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php930
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/PluginBase.php230
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/PluginInterface.php14
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/access/AccessPluginBase.php101
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/access/None.php46
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/area/AreaPluginBase.php117
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/area/Broken.php44
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/area/Result.php105
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/area/Text.php119
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/area/TextCustom.php62
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/area/Title.php59
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/area/View.php92
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/ArgumentPluginBase.php1121
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/Broken.php41
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/Date.php145
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/Formula.php75
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/GroupByNumeric.php39
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/ManyToOne.php200
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/Null.php69
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/Numeric.php126
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/Standard.php23
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument/String.php289
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument_default/ArgumentDefaultPluginBase.php89
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Fixed.php48
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Php.php63
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Raw.php62
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php93
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/None.php41
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Numeric.php29
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Php.php63
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php361
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/cache/None.php38
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/cache/Time.php131
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/display/Attachment.php299
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/display/DefaultDisplay.php79
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php2717
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/display/Embed.php33
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/display/Feed.php260
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php654
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/display_extender/DefaultDisplayExtender.php26
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/display_extender/DisplayExtenderPluginBase.php70
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/Basic.php26
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/ExposedFormPluginBase.php322
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/InputRequired.php107
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Boolean.php92
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Broken.php41
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/ContextualLinks.php116
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Counter.php63
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Custom.php51
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Date.php120
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php1627
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/FileSize.php59
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/MachineName.php83
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Markup.php67
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Numeric.php159
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/PrerenderList.php125
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Serialized.php78
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Standard.php23
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/TimeInterval.php47
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Url.php53
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/field/Xss.php29
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/BooleanOperator.php194
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/BooleanOperatorString.php45
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/Broken.php43
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/Combine.php147
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/Date.php193
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/Equality.php55
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/FilterPluginBase.php1394
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/GroupByNumeric.php66
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/InOperator.php446
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/ManyToOne.php137
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/Numeric.php346
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/Standard.php23
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/filter/String.php351
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/join/JoinPluginBase.php284
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/join/Standard.php21
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/join/Subquery.php112
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/pager/Full.php443
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/pager/Mini.php40
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/pager/None.php89
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/pager/PagerPluginBase.php251
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/pager/Some.php76
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/query/QueryInterface.php17
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/query/QueryPluginBase.php167
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php1739
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/relationship/Broken.php41
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php390
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/relationship/RelationshipPluginBase.php169
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/relationship/Standard.php23
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/row/Fields.php109
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php175
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/row/RssFields.php202
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/sort/Broken.php41
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/sort/Date.php82
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/sort/GroupByNumeric.php47
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/sort/MenuHierarchy.php69
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/sort/Random.php30
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/sort/SortPluginBase.php221
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/sort/Standard.php23
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/style/DefaultStyle.php43
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/style/DefaultSummary.php94
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/style/Grid.php91
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/style/HtmlList.php82
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/style/Mapping.php143
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/style/Rss.php145
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php688
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/style/Table.php379
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/style/UnformattedSummary.php49
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardException.php15
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardInterface.php60
-rw-r--r--core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php1088
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/AnalyzeTest.php56
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/BasicTest.php190
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Comment/ArgumentUserUIDTest.php37
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Comment/CommentTestBase.php52
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Comment/DefaultViewRecentComments.php158
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Comment/FilterUserUIDTest.php48
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Comment/WizardTest.php90
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php164
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Entity/FieldEntityTest.php62
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Field/ApiDataTest.php160
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Field/FieldTestBase.php73
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Field/HandlerFieldFieldTest.php221
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/GlossaryTest.php60
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/AreaTest.php138
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/AreaTextTest.php61
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/ArgumentNullTest.php81
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/ArgumentStringTest.php55
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FieldBooleanTest.php86
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php90
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FieldCustomTest.php57
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FieldDateTest.php97
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FieldFileSizeTest.php74
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FieldTest.php967
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FieldUrlTest.php70
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FieldXssTest.php70
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FilterCombineTest.php111
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FilterDateTest.php153
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FilterEqualityTest.php180
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FilterInOperatorTest.php204
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FilterNumericTest.php414
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/FilterStringTest.php816
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAliasTest.php88
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php105
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/HandlerTest.php368
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/HandlerTestBase.php17
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/RelationshipTest.php181
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/SortDateTest.php208
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/SortRandomTest.php99
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Handler/SortTest.php131
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Language/ArgumentLanguage.php47
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Language/FieldLanguage.php42
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Language/FilterLanguage.php48
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Language/LanguageTestBase.php78
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/ModuleTest.php227
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Node/FieldTypeTest.php47
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Node/FilterUidRevisionTest.php57
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Node/NodeTestBase.php16
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Node/RevisionRelationships.php69
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/AccessTest.php120
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/ArgumentDefaultTest.php149
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/ArgumentValidatorTest.php46
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTest.php218
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayExtenderTest.php54
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayFeedTest.php74
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php44
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayTest.php122
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/ExposedFormTest.php182
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/FilterTest.php144
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/JoinTest.php152
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/PagerTest.php321
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/PluginTestBase.php17
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/QueryTest.php65
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/StyleMappingTest.php85
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTest.php311
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTestBase.php41
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Plugin/StyleUnformattedTest.php55
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/PluginInstanceTest.php106
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/PluginTypeListTest.php55
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php149
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Taxonomy/RelationshipNodeTermDataTest.php53
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Taxonomy/RelationshipRepresentativeNode.php42
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Taxonomy/TaxonomyTestBase.php137
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/TokenReplaceTest.php57
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/DefaultViewsTest.php129
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/DisplayExtenderUITest.php45
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/DisplayTest.php184
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/GroupByTest.php42
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/OverrideDisplaysTest.php202
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/RedirectTest.php50
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/RowUITest.php61
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/SettingsTest.php96
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/StorageTest.php47
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/StyleUITest.php61
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/TagTest.php57
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/UI/UITestBase.php35
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/User/AccessPermissionTest.php40
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/User/AccessRoleTest.php44
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/User/AccessTestBase.php65
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/User/ArgumentDefaultTest.php50
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/User/ArgumentValidateTest.php87
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/User/HandlerFieldUserNameTest.php63
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/User/RelationshipRepresentativeNode.php43
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/User/UserTestBase.php40
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/ViewElementTest.php117
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php231
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php473
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php445
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php141
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Wizard/BasicTest.php140
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Wizard/ItemsPerPageTest.php98
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Wizard/MenuTest.php60
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Wizard/SortingTest.php82
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php193
-rw-r--r--core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php32
-rw-r--r--core/modules/views/lib/Drupal/views/ViewExecutable.php2233
-rw-r--r--core/modules/views/lib/Drupal/views/ViewStorage.php380
-rw-r--r--core/modules/views/lib/Drupal/views/ViewStorageController.php116
-rw-r--r--core/modules/views/lib/Drupal/views/ViewStorageInterface.php34
-rw-r--r--core/modules/views/lib/Drupal/views/ViewsBundle.php37
-rw-r--r--core/modules/views/lib/Views/aggregator/Plugin/views/argument/CategoryCid.php41
-rw-r--r--core/modules/views/lib/Views/aggregator/Plugin/views/argument/Fid.php42
-rw-r--r--core/modules/views/lib/Views/aggregator/Plugin/views/argument/Iid.php42
-rw-r--r--core/modules/views/lib/Views/aggregator/Plugin/views/field/Category.php74
-rw-r--r--core/modules/views/lib/Views/aggregator/Plugin/views/field/TitleLink.php72
-rw-r--r--core/modules/views/lib/Views/aggregator/Plugin/views/field/Xss.php30
-rw-r--r--core/modules/views/lib/Views/aggregator/Plugin/views/filter/CategoryCid.php39
-rw-r--r--core/modules/views/lib/Views/aggregator/Plugin/views/row/Rss.php92
-rw-r--r--core/modules/views/lib/Views/block/Plugin/views/display/Block.php270
-rw-r--r--core/modules/views/lib/Views/book/Plugin/views/argument_default/Root.php36
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/argument/UserUid.php76
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/Comment.php86
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/Depth.php33
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/LastTimestamp.php45
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/Link.php74
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/LinkApprove.php47
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/LinkDelete.php40
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/LinkEdit.php63
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/LinkReply.php40
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/NcsLastCommentName.php77
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/NcsLastUpdated.php31
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/NodeComment.php38
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/NodeLink.php57
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/NodeNewComments.php124
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/field/Username.php71
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/filter/NcsLastUpdated.php37
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/filter/NodeComment.php33
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/filter/UserUid.php41
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/row/Rss.php168
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/row/View.php65
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/sort/NcsLastCommentName.php47
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/sort/NcsLastUpdated.php31
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/sort/Thread.php40
-rw-r--r--core/modules/views/lib/Views/comment/Plugin/views/wizard/Comment.php174
-rw-r--r--core/modules/views/lib/Views/contact/Plugin/views/field/ContactLink.php68
-rw-r--r--core/modules/views/lib/Views/field/Plugin/views/argument/FieldList.php74
-rw-r--r--core/modules/views/lib/Views/field/Plugin/views/argument/ListString.php76
-rw-r--r--core/modules/views/lib/Views/field/Plugin/views/field/Field.php854
-rw-r--r--core/modules/views/lib/Views/field/Plugin/views/filter/FieldList.php30
-rw-r--r--core/modules/views/lib/Views/field/Plugin/views/relationship/EntityReverse.php97
-rw-r--r--core/modules/views/lib/Views/file/Plugin/views/argument/Fid.php40
-rw-r--r--core/modules/views/lib/Views/file/Plugin/views/field/Extension.php32
-rw-r--r--core/modules/views/lib/Views/file/Plugin/views/field/File.php74
-rw-r--r--core/modules/views/lib/Views/file/Plugin/views/field/FileMime.php49
-rw-r--r--core/modules/views/lib/Views/file/Plugin/views/field/Status.php30
-rw-r--r--core/modules/views/lib/Views/file/Plugin/views/field/Uri.php46
-rw-r--r--core/modules/views/lib/Views/file/Plugin/views/filter/Status.php31
-rw-r--r--core/modules/views/lib/Views/file/Plugin/views/wizard/File.php74
-rw-r--r--core/modules/views/lib/Views/filter/Plugin/views/field/FormatName.php53
-rw-r--r--core/modules/views/lib/Views/language/Plugin/views/argument/Language.php46
-rw-r--r--core/modules/views/lib/Views/language/Plugin/views/field/Language.php50
-rw-r--r--core/modules/views/lib/Views/language/Plugin/views/filter/Language.php38
-rw-r--r--core/modules/views/lib/Views/locale/Plugin/views/field/LinkEdit.php77
-rw-r--r--core/modules/views/lib/Views/locale/Plugin/views/filter/Version.php42
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/CreatedDay.php55
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/CreatedFullDate.php48
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/CreatedMonth.php54
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/CreatedWeek.php40
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/CreatedYear.php32
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/CreatedYearMonth.php48
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/Nid.php36
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/Type.php47
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/UidRevision.php30
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument/Vid.php51
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument_default/Node.php40
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/argument_validator/Node.php144
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/HistoryUserTimestamp.php95
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/Language.php51
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/Link.php61
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/LinkDelete.php42
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/LinkEdit.php42
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/Node.php92
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/Path.php63
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/Revision.php80
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/RevisionLink.php82
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/RevisionLinkDelete.php47
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/RevisionLinkRevert.php47
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/field/Type.php61
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/filter/Access.php52
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/filter/HistoryUserTimestamp.php99
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/filter/Status.php36
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/filter/Type.php38
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/filter/UidRevision.php37
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/row/Rss.php181
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/row/View.php66
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/wizard/Node.php316
-rw-r--r--core/modules/views/lib/Views/node/Plugin/views/wizard/NodeRevision.php134
-rw-r--r--core/modules/views/lib/Views/search/Plugin/views/argument/Search.php116
-rw-r--r--core/modules/views/lib/Views/search/Plugin/views/field/Score.php93
-rw-r--r--core/modules/views/lib/Views/search/Plugin/views/filter/Search.php198
-rw-r--r--core/modules/views/lib/Views/search/Plugin/views/row/View.php54
-rw-r--r--core/modules/views/lib/Views/search/Plugin/views/sort/Score.php44
-rw-r--r--core/modules/views/lib/Views/search/ViewsSearchQuery.php84
-rw-r--r--core/modules/views/lib/Views/statistics/Plugin/views/field/AccesslogPath.php71
-rw-r--r--core/modules/views/lib/Views/system/Plugin/views/row/Entity.php141
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTid.php61
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTidDepth.php161
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/argument/IndexTidDepthModifier.php78
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/argument/Taxonomy.php40
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/argument/VocabularyMachineName.php41
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/argument/VocabularyVid.php40
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/argument_default/Tid.php170
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/argument_validator/Term.php202
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/field/Language.php32
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/field/LinkEdit.php79
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/field/Taxonomy.php102
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/field/TaxonomyIndexTid.php161
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/filter/TaxonomyIndexTid.php376
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/filter/TaxonomyIndexTidDepth.php111
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/filter/VocabularyMachineName.php37
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/filter/VocabularyVid.php37
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/relationship/NodeTermData.php105
-rw-r--r--core/modules/views/lib/Views/taxonomy/Plugin/views/wizard/TaxonomyTerm.php72
-rw-r--r--core/modules/views/lib/Views/translation/Plugin/views/argument/NodeTnid.php41
-rw-r--r--core/modules/views/lib/Views/translation/Plugin/views/field/NodeLinkTranslate.php41
-rw-r--r--core/modules/views/lib/Views/translation/Plugin/views/field/NodeTranslationLink.php66
-rw-r--r--core/modules/views/lib/Views/translation/Plugin/views/filter/NodeTnid.php58
-rw-r--r--core/modules/views/lib/Views/translation/Plugin/views/filter/NodeTnidChild.php36
-rw-r--r--core/modules/views/lib/Views/translation/Plugin/views/relationship/Translation.php113
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/access/Permission.php81
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/access/Role.php85
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/argument/RolesRid.php38
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/argument/Uid.php45
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/argument_default/CurrentUser.php32
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/argument_default/User.php85
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/argument_validator/User.php152
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/Language.php49
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/Link.php74
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/LinkCancel.php43
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/LinkEdit.php41
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/Mail.php56
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/Name.php98
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/Permissions.php91
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/Picture.php131
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/Roles.php79
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/field/User.php68
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/filter/Current.php53
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/filter/Name.php173
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/filter/Permissions.php47
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/filter/Roles.php40
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/row/View.php41
-rw-r--r--core/modules/views/lib/Views/user/Plugin/views/wizard/Users.php95
-rw-r--r--core/modules/views/modules/aggregator.views.inc358
-rw-r--r--core/modules/views/modules/book.views.inc113
-rw-r--r--core/modules/views/modules/comment.views.inc580
-rw-r--r--core/modules/views/modules/contact.views.inc21
-rw-r--r--core/modules/views/modules/field.views.inc461
-rw-r--r--core/modules/views/modules/file.views.inc532
-rw-r--r--core/modules/views/modules/filter.views.inc31
-rw-r--r--core/modules/views/modules/image.views.inc72
-rw-r--r--core/modules/views/modules/language.views.inc91
-rw-r--r--core/modules/views/modules/locale.views.inc192
-rw-r--r--core/modules/views/modules/node.views.inc767
-rw-r--r--core/modules/views/modules/poll.views.inc46
-rw-r--r--core/modules/views/modules/search.views.inc141
-rw-r--r--core/modules/views/modules/statistics.views.inc259
-rw-r--r--core/modules/views/modules/taxonomy.views.inc501
-rw-r--r--core/modules/views/modules/translation.views.inc120
-rw-r--r--core/modules/views/modules/user.views.inc481
-rw-r--r--core/modules/views/modules/views.views.inc112
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_access_none.yml27
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_access_perm.yml29
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_access_role.yml27
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_aggregate_count.yml57
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_alias.yml88
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_argument_default_fixed.yml56
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_comment_user_uid.yml48
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_destroy.yml160
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_display.yml87
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_executable_displays.yml24
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_exposed_form.yml27
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_feed_display.yml91
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_field_get_entity.yml64
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_field_tokens.yml45
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_field_type.yml20
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_filter_date_between.yml41
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_filter_group_override.yml54
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_filter_groups.yml111
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_filter_node_uid_revision.yml50
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_glossary.yml48
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_group_by_count.yml60
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_group_by_in_filters.yml53
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_groupwise_term.yml66
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_groupwise_user.yml76
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_handler_relationships.yml30
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_node_revision_nid.yml36
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_node_revision_vid.yml36
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_pager_full.yml31
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_pager_none.yml27
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_pager_some.yml30
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_plugin_argument_default_current_user.yml56
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_redirect_view.yml79
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_store_pager_settings.yml27
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_taxonomy_node_term_data.yml73
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_tokens.yml57
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_user_relationship.yml102
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_view.yml48
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_numeric.yml37
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_php.yml37
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_view_argument_validate_user.yml37
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_view_delete.yml29
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_view_fieldapi.yml34
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_view_handler_weight.yml63
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_view_pager_full_zero_items_per_page.yml47
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_view_status.yml16
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_views_handler_field_user_name.yml50
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_field.yml20
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_handler.yml25
-rw-r--r--core/modules/views/tests/views_test_config/config/views.view.test_views_move_to_table.yml20
-rw-r--r--core/modules/views/tests/views_test_config/views_test_config.info7
-rw-r--r--core/modules/views/tests/views_test_config/views_test_config.module1
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_access_dynamic.yml34
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_access_static.yml34
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_argument_default_current_user.yml53
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_click_sort.yml52
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_example_area.yml34
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_exposed_admin_ui.yml53
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_field_classes.yml33
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_field_output.yml26
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_filter.yml40
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_filter_in_operator_ui.yml39
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_handler_test_access.yml47
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_page_display.yml33
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_rename_reset_button.yml54
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_style_mapping.yml58
-rw-r--r--core/modules/views/tests/views_test_data/config/views.view.test_views_groupby_save.yml27
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/DynamicTest.php40
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/access/StaticTest.php40
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/area/TestExample.php43
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/argument_default/ArgumentDefaultTest.php41
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display/DisplayTest.php134
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display_extender/DisplayExtenderTest.php111
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display_extender/DisplayExtenderTest2.php23
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/field/FieldTest.php74
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/filter/FilterTest.php62
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/join/JoinTest.php58
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/query/QueryTest.php127
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/row/RowTest.php85
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/MappingTest.php70
-rw-r--r--core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/style/StyleTest.php110
-rw-r--r--core/modules/views/tests/views_test_data/templates/views-view--frontpage.tpl.php85
-rw-r--r--core/modules/views/tests/views_test_data/views_cache.test.css5
-rw-r--r--core/modules/views/tests/views_test_data/views_cache.test.js5
-rw-r--r--core/modules/views/tests/views_test_data/views_test_data.info7
-rw-r--r--core/modules/views/tests/views_test_data/views_test_data.install35
-rw-r--r--core/modules/views/tests/views_test_data/views_test_data.module194
-rw-r--r--core/modules/views/theme/theme.inc1159
-rw-r--r--core/modules/views/theme/views-exposed-form.tpl.php80
-rw-r--r--core/modules/views/theme/views-more.tpl.php19
-rw-r--r--core/modules/views/theme/views-view-field.tpl.php25
-rw-r--r--core/modules/views/theme/views-view-fields.tpl.php36
-rw-r--r--core/modules/views/theme/views-view-grid.tpl.php28
-rw-r--r--core/modules/views/theme/views-view-grouping.tpl.php25
-rw-r--r--core/modules/views/theme/views-view-list.tpl.php21
-rw-r--r--core/modules/views/theme/views-view-row-comment.tpl.php18
-rw-r--r--core/modules/views/theme/views-view-row-rss.tpl.php15
-rw-r--r--core/modules/views/theme/views-view-rss.tpl.php20
-rw-r--r--core/modules/views/theme/views-view-summary-unformatted.tpl.php20
-rw-r--r--core/modules/views/theme/views-view-summary.tpl.php20
-rw-r--r--core/modules/views/theme/views-view-table.tpl.php50
-rw-r--r--core/modules/views/theme/views-view-unformatted.tpl.php17
-rw-r--r--core/modules/views/theme/views-view.tpl.php90
-rw-r--r--core/modules/views/views.api.php642
-rw-r--r--core/modules/views/views.info6
-rw-r--r--core/modules/views/views.install78
-rw-r--r--core/modules/views/views.module2414
-rw-r--r--core/modules/views/views.tokens.inc138
-rw-r--r--core/modules/views/views_ui/admin.inc2774
-rw-r--r--core/modules/views/views_ui/css/views-admin-rtl.css88
-rw-r--r--core/modules/views/views_ui/css/views-admin.bartik-rtl.css21
-rw-r--r--core/modules/views/views_ui/css/views-admin.bartik.css122
-rw-r--r--core/modules/views/views_ui/css/views-admin.contextual.css61
-rw-r--r--core/modules/views/views_ui/css/views-admin.css377
-rw-r--r--core/modules/views/views_ui/css/views-admin.seven-rtl.css43
-rw-r--r--core/modules/views/views_ui/css/views-admin.seven.css376
-rw-r--r--core/modules/views/views_ui/css/views-admin.theme-rtl.css220
-rw-r--r--core/modules/views/views_ui/css/views-admin.theme.css1160
-rw-r--r--core/modules/views/views_ui/images/arrow-active.pngbin0 -> 313 bytes
-rw-r--r--core/modules/views/views_ui/images/close.pngbin0 -> 227 bytes
-rw-r--r--core/modules/views/views_ui/images/expanded-options.pngbin0 -> 228 bytes
-rw-r--r--core/modules/views/views_ui/images/loading-small.gifbin0 -> 2112 bytes
-rw-r--r--core/modules/views/views_ui/images/loading.gifbin0 -> 6733 bytes
-rw-r--r--core/modules/views/views_ui/images/overridden.gifbin0 -> 175 bytes
-rw-r--r--core/modules/views/views_ui/images/sprites.pngbin0 -> 1777 bytes
-rw-r--r--core/modules/views/views_ui/images/status-active.gifbin0 -> 2196 bytes
-rw-r--r--core/modules/views/views_ui/js/views-admin.js1138
-rw-r--r--core/modules/views/views_ui/lib/Drupal/views_ui/ViewListController.php160
-rw-r--r--core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php1999
-rw-r--r--core/modules/views/views_ui/theme/theme.inc521
-rw-r--r--core/modules/views/views_ui/theme/views-ui-display-tab-bucket.tpl.php16
-rw-r--r--core/modules/views/views_ui/theme/views-ui-display-tab-setting.tpl.php15
-rw-r--r--core/modules/views/views_ui/theme/views-ui-edit-item.tpl.php44
-rw-r--r--core/modules/views/views_ui/theme/views-ui-edit-view.tpl.php46
-rw-r--r--core/modules/views/views_ui/views_ui.info7
-rw-r--r--core/modules/views/views_ui/views_ui.module764
537 files changed, 77716 insertions, 0 deletions
diff --git a/core/modules/views/config/views.settings.yml b/core/modules/views/config/views.settings.yml
new file mode 100644
index 0000000..4b9c81c
--- /dev/null
+++ b/core/modules/views/config/views.settings.yml
@@ -0,0 +1,35 @@
+debug:
+ output: '0'
+ region: 'footer'
+display_extenders: { }
+no_javascript: '0'
+skip_cache: '0'
+sql_signature: '0'
+ui:
+ show:
+ additional_queries: '0'
+ advanced_column: '0'
+ listing_filters: '0'
+ master_display: '0'
+ performance_statistics: '0'
+ preview_information: '1'
+ sql_query:
+ enabled: '0'
+ where: 'above'
+ display_embed: '0'
+ custom_theme: '_default'
+ always_live_preview: '1'
+ always_live_preview_button: '1'
+ exposed_filter_any_label: 'new_any'
+field_rewrite_elements:
+ div: DIV
+ span: SPAN
+ h1: H1
+ h2: H2
+ h3: H3
+ h4: H4
+ h5: H5
+ h6: H6
+ p: P
+ strong: STRONG
+ em: EM
diff --git a/core/modules/views/config/views.view.archive.yml b/core/modules/views/config/views.view.archive.yml
new file mode 100644
index 0000000..f2b3a62
--- /dev/null
+++ b/core/modules/views/config/views.view.archive.yml
@@ -0,0 +1,103 @@
+disabled: true
+api_version: '3.0'
+module: node
+name: archive
+description: 'Display a list of months that link to content for that month.'
+tag: default
+base_table: node
+human_name: Archive
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ title: 'Monthly archive'
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ sorts:
+ created:
+ id: created
+ table: node
+ field: created
+ order: DESC
+ arguments:
+ created_year_month:
+ id: created_year_month
+ table: node
+ field: created_year_month
+ default_action: summary
+ exception:
+ title_enable: 1
+ title_enable: 1
+ title: '%1'
+ default_argument_type: fixed
+ summary:
+ sort_order: desc
+ format: default_summary
+ summary_options:
+ override: true
+ items_per_page: '30'
+ specify_validation: 1
+ filters:
+ status:
+ id: status
+ table: node
+ field: status
+ value: 1
+ group: 0
+ expose:
+ operator: false
+ style:
+ type: default
+ row:
+ type: node
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: archive
+ block_1:
+ id: block_1
+ display_title: Block
+ display_plugin: block
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ defaults:
+ arguments: false
+ arguments:
+ created_year_month:
+ id: created_year_month
+ table: node
+ field: created_year_month
+ default_action: summary
+ exception:
+ title_enable: 1
+ title_enable: 1
+ title: '%1'
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ summary_options:
+ items_per_page: '30'
+ specify_validation: 1
diff --git a/core/modules/views/config/views.view.backlinks.yml b/core/modules/views/config/views.view.backlinks.yml
new file mode 100644
index 0000000..8c1f2a7
--- /dev/null
+++ b/core/modules/views/config/views.view.backlinks.yml
@@ -0,0 +1,123 @@
+disabled: true
+api_version: '3.0'
+module: search
+name: backlinks
+description: 'Displays a list of nodes that link to the node, using the search backlinks table.'
+tag: default
+base_table: node
+human_name: Backlinks
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ options:
+ items_per_page: 30
+ empty:
+ text:
+ id: area
+ table: views
+ field: area
+ empty: false
+ content: 'No backlinks found.'
+ format: '1'
+ fields:
+ title:
+ id: title
+ table: node
+ field: title
+ label: ''
+ link_to_node: 1
+ arguments:
+ nid:
+ id: nid
+ table: search_node_links_to
+ field: nid
+ default_action: 'not found'
+ title_enable: 1
+ title: 'Pages that link to %1'
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ specify_validation: 1
+ validate:
+ type: node
+ filters:
+ status:
+ id: status
+ table: node
+ field: status
+ value: 1
+ group: 0
+ expose:
+ operator: false
+ style:
+ type: html_list
+ options:
+ type: ol
+ row:
+ type: fields
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: node/%/backlinks
+ menu:
+ type: tab
+ title: 'What links here'
+ weight: '0'
+ block_1:
+ id: block
+ display_title: 'What links here'
+ display_plugin: block
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ defaults:
+ use_more: false
+ style_plugin: false
+ style_options: false
+ row_plugin: false
+ row_options: false
+ arguments: false
+ use_more: true
+ arguments:
+ nid:
+ id: nid
+ table: search_node_links_to
+ field: nid
+ default_action: default
+ title_enable: 1
+ title: 'What links here'
+ default_argument_type: node
+ summary:
+ format: default_summary
+ specify_validation: 1
+ validate:
+ type: node
+ style:
+ type: html_list
+ row:
+ type: fields
diff --git a/core/modules/views/config/views.view.comments_recent.yml b/core/modules/views/config/views.view.comments_recent.yml
new file mode 100644
index 0000000..6e86a0c
--- /dev/null
+++ b/core/modules/views/config/views.view.comments_recent.yml
@@ -0,0 +1,126 @@
+disabled: true
+api_version: '3.0'
+module: comment
+name: comments_recent
+description: 'Contains a block and a page to list recent comments; the block will automatically link to the page, which displays the comment body as well as a link to the node.'
+tag: default
+base_table: comment
+human_name: 'Recent comments'
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ title: 'Recent comments'
+ use_more: true
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: some
+ options:
+ items_per_page: 5
+ relationships:
+ nid:
+ id: nid
+ table: comment
+ field: nid
+ fields:
+ subject:
+ id: subject
+ table: comment
+ field: subject
+ label: ''
+ link_to_comment: 1
+ timestamp:
+ id: timestamp
+ table: comment
+ field: changed
+ label: ''
+ date_format: 'time ago'
+ sorts:
+ timestamp:
+ id: timestamp
+ table: comment
+ field: changed
+ order: DESC
+ filters:
+ status_extra:
+ id: status_extra
+ table: node
+ field: status_extra
+ relationship: nid
+ group: 0
+ style:
+ type: html_list
+ row:
+ type: fields
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ defaults:
+ style_plugin: false
+ style_options: false
+ row_plugin: false
+ row_options: false
+ fields: false
+ fields:
+ title:
+ id: title
+ table: node
+ field: title
+ relationship: nid
+ label: 'Reply to'
+ link_to_node: 1
+ timestamp:
+ id: timestamp
+ table: comment
+ field: changed
+ label: ''
+ date_format: 'time ago'
+ subject:
+ id: subject
+ table: comment
+ field: subject
+ label: ''
+ link_to_comment: 1
+ comment:
+ id: comment
+ table: field_data_comment_body
+ field: comment_body
+ label: ''
+ path: comments/recent
+ style:
+ type: html_list
+ row:
+ type: fields
+ options:
+ inline:
+ title: title
+ timestamp: timestamp
+ separator: ' '
+ block_1:
+ id: block_1
+ display_title: Block
+ display_plugin: block
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
diff --git a/core/modules/views/config/views.view.frontpage.yml b/core/modules/views/config/views.view.frontpage.yml
new file mode 100644
index 0000000..a635a0e
--- /dev/null
+++ b/core/modules/views/config/views.view.frontpage.yml
@@ -0,0 +1,95 @@
+disabled: true
+api_version: '3.0'
+module: node
+name: frontpage
+description: 'Emulates the default Drupal front page; you may set the default home page path to this view to make it your front page.'
+tag: default
+base_table: node
+human_name: 'Front page'
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ style:
+ type: default
+ row:
+ type: node
+ options:
+ links: 1
+ sorts:
+ sticky:
+ id: sticky
+ table: node
+ field: sticky
+ order: DESC
+ created:
+ id: created
+ table: node
+ field: created
+ order: DESC
+ filters:
+ promote:
+ id: promote
+ table: node
+ field: promote
+ value: '1'
+ group: 0
+ expose:
+ operator: false
+ status:
+ id: status
+ table: node
+ field: status
+ value: '1'
+ group: 0
+ expose:
+ operator: false
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: frontpage
+ feed_1:
+ id: feed_1
+ display_title: Feed
+ display_plugin: feed
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ defaults:
+ title: false
+ title: 'Front page feed'
+ pager:
+ type: some
+ style:
+ type: rss
+ row:
+ type: node_rss
+ path: rss.xml
+ displays:
+ default: default
+ page: page
+ sitename_title: '1'
diff --git a/core/modules/views/config/views.view.glossary.yml b/core/modules/views/config/views.view.glossary.yml
new file mode 100644
index 0000000..966da7c
--- /dev/null
+++ b/core/modules/views/config/views.view.glossary.yml
@@ -0,0 +1,150 @@
+disabled: true
+api_version: '3.0'
+module: node
+name: glossary
+description: 'A list of all content, by letter.'
+tag: default
+base_table: node
+human_name: Glossary
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ use_ajax: true
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ options:
+ items_per_page: 36
+ fields:
+ title:
+ id: title
+ table: node
+ field: title
+ link_to_node: 1
+ name:
+ id: name
+ table: users
+ field: name
+ label: Author
+ link_to_user: 1
+ relationship: uid
+ changed:
+ id: changed
+ table: node
+ field: changed
+ label: 'Last update'
+ date_format: large
+ arguments:
+ title:
+ id: title
+ table: node
+ field: title
+ default_action: default
+ exception:
+ title_enable: 1
+ default_argument_type: fixed
+ default_argument_options:
+ argument: a
+ summary:
+ format: default_summary
+ specify_validation: 1
+ glossary: 1
+ limit: '1'
+ case: upper
+ path_case: lower
+ transform_dash: 0
+ relationships:
+ uid:
+ id: uid
+ table: node
+ field: uid
+ style:
+ type: table
+ options:
+ columns:
+ title: title
+ name: name
+ changed: changed
+ default: title
+ info:
+ title:
+ sortable: 1
+ separator: ''
+ name:
+ sortable: 1
+ separator: ''
+ changed:
+ sortable: 1
+ separator: ''
+ override: 1
+ sticky: 0
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: glossary
+ menu:
+ type: normal
+ title: Glossary
+ weight: '0'
+ attachment_1:
+ id: attachment_1
+ display_title: Attachment
+ display_plugin: attachment
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ pager:
+ type: none
+ options:
+ offset: '0'
+ defaults:
+ arguments: false
+ arguments:
+ title:
+ id: title
+ table: node
+ field: title
+ default_action: summary
+ exception:
+ title_enable: 1
+ default_argument_type: fixed
+ default_argument_options:
+ argument: a
+ summary:
+ format: unformatted_summary
+ summary_options:
+ items_per_page: '25'
+ inline: 1
+ separator: ' | '
+ specify_validation: 1
+ glossary: 1
+ limit: '1'
+ case: upper
+ path_case: lower
+ transform_dash: 0
+ displays:
+ default: default
+ page: page_1
+ inherit_arguments: 0
diff --git a/core/modules/views/config/views.view.taxonomy_term.yml b/core/modules/views/config/views.view.taxonomy_term.yml
new file mode 100644
index 0000000..742c5d7
--- /dev/null
+++ b/core/modules/views/config/views.view.taxonomy_term.yml
@@ -0,0 +1,110 @@
+disabled: true
+api_version: '3.0'
+module: taxonomy
+name: taxonomy_term
+description: 'A view to emulate Drupal core''s handling of taxonomy/term.'
+tag: default
+base_table: node
+human_name: 'Taxonomy term'
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ sorts:
+ sticky:
+ id: sticky
+ table: node
+ field: sticky
+ order: DESC
+ created:
+ id: created
+ table: node
+ field: created
+ order: DESC
+ arguments:
+ term_node_tid_depth:
+ id: term_node_tid_depth
+ table: node
+ field: term_node_tid_depth
+ default_action: 'not found'
+ exception:
+ title_enable: 1
+ title_enable: 1
+ title: '%1'
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ specify_validation: 1
+ validate:
+ type: taxonomy_term
+ depth: '0'
+ break_phrase: 1
+ term_node_tid_depth_modifier:
+ id: term_node_tid_depth_modifier
+ table: node
+ field: term_node_tid_depth_modifier
+ exception:
+ title_enable: 1
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ specify_validation: 1
+ filters:
+ status_extra:
+ id: status_extra
+ table: node
+ field: status_extra
+ group: 0
+ expose:
+ operator: false
+ style:
+ type: default
+ row:
+ type: node
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: taxonomy/term/%
+ feed_1:
+ id: feed_1
+ display_title: Feed
+ display_plugin: feed
+ position: '3'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ pager:
+ type: full
+ options:
+ items_per_page: 15
+ path: taxonomy/term/%/%/feed
+ displays:
+ page: page
+ default: 0
+ style:
+ type: rss
+ row:
+ type: node_rss
diff --git a/core/modules/views/config/views.view.tracker.yml b/core/modules/views/config/views.view.tracker.yml
new file mode 100644
index 0000000..5d31e2f
--- /dev/null
+++ b/core/modules/views/config/views.view.tracker.yml
@@ -0,0 +1,150 @@
+disabled: true
+api_version: '3.0'
+module: node
+name: tracker
+description: 'Shows all new activity on system.'
+tag: default
+base_table: node
+human_name: Tracker
+core: '8'
+display:
+ default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: '1'
+ display_options:
+ query:
+ type: views_query
+ options:
+ query_comment: false
+ title: 'Recent posts'
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ options:
+ items_per_page: '25'
+ relationships:
+ uid:
+ id: uid
+ table: node
+ field: uid
+ fields:
+ type:
+ id: type
+ table: node
+ field: type
+ title:
+ id: title
+ table: node
+ field: title
+ name:
+ id: name
+ table: users
+ field: name
+ relationship: uid
+ label: Author
+ comment_count:
+ id: comment_count
+ table: node_comment_statistics
+ field: comment_count
+ label: Replies
+ last_comment_timestamp:
+ id: last_comment_timestamp
+ table: node_comment_statistics
+ field: last_comment_timestamp
+ label: 'Last Post'
+ timestamp:
+ id: timestamp
+ table: history
+ field: timestamp
+ label: ''
+ link_to_node: 0
+ comments: 1
+ new_comments:
+ id: new_comments
+ table: node
+ field: new_comments
+ label: ''
+ hide_empty: true
+ suffix: ' new'
+ link_to_comment: 1
+ sorts:
+ last_comment_timestamp:
+ id: last_comment_timestamp
+ table: node_comment_statistics
+ field: last_comment_timestamp
+ arguments:
+ uid_touch:
+ id: uid_touch
+ table: node
+ field: uid_touch
+ exception:
+ title_enable: 1
+ title_enable: 1
+ title: 'Recent posts for %1'
+ default_argument_type: fixed
+ summary:
+ format: default_summary
+ specify_validation: 1
+ filters:
+ status:
+ id: status
+ table: node
+ field: status
+ value: '1'
+ group: 0
+ expose:
+ operator: false
+ style:
+ type: table
+ options:
+ columns:
+ type: type
+ title: title
+ name: name
+ comment_count: comment_count
+ last_comment_timestamp: last_comment_timestamp
+ timestamp: title
+ new_comments: comment_count
+ default: last_comment_timestamp
+ info:
+ type:
+ sortable: 1
+ separator: ''
+ title:
+ sortable: 1
+ separator: ' '
+ name:
+ sortable: 1
+ separator: ''
+ comment_count:
+ sortable: 1
+ separator: '<br />'
+ last_comment_timestamp:
+ sortable: 1
+ separator: '&nbsp;'
+ timestamp:
+ separator: ''
+ new_comments:
+ separator: ''
+ override: 1
+ order: desc
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: '2'
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: tracker
+ menu:
+ type: normal
+ title: 'Recent posts'
diff --git a/core/modules/views/css/views.base-rtl.css b/core/modules/views/css/views.base-rtl.css
new file mode 100644
index 0000000..3755668
--- /dev/null
+++ b/core/modules/views/css/views.base-rtl.css
@@ -0,0 +1,3 @@
+.views-exposed-form .views-exposed-widget {
+ float: right; /* RTL */
+}
diff --git a/core/modules/views/css/views.base.css b/core/modules/views/css/views.base.css
new file mode 100644
index 0000000..51750a5
--- /dev/null
+++ b/core/modules/views/css/views.base.css
@@ -0,0 +1,32 @@
+.views-exposed-form .views-exposed-widget {
+ float: left; /* LTR */
+}
+
+.views-exposed-form .views-exposed-widget .form-submit {
+ margin-top: 0.5em;
+}
+
+.views-exposed-form .form-item,
+.views-exposed-form .form-submit {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.views-exposed-widgets {
+ margin-bottom: 0.5em;
+}
+
+/* table style column align */
+.views-align-left {
+ text-align: left;
+}
+.views-align-right {
+ text-align: right;
+}
+.views-align-center {
+ text-align: center;
+}
+
+.view .progress-disabled {
+ float: none;
+}
diff --git a/core/modules/views/includes/ajax.inc b/core/modules/views/includes/ajax.inc
new file mode 100644
index 0000000..b19d311
--- /dev/null
+++ b/core/modules/views/includes/ajax.inc
@@ -0,0 +1,373 @@
+<?php
+
+/**
+ * @file
+ * Handles the server side AJAX interactions of Views.
+ */
+
+use Symfony\Component\HttpFoundation\JsonResponse;
+
+/**
+ * @defgroup ajax Views AJAX library
+ * @{
+ * Handles the server side AJAX interactions of Views.
+ */
+
+/**
+ * Menu callback to load a view via AJAX.
+ */
+function views_ajax() {
+ $request = drupal_container()->get('request');
+ $name = $request->request->get('view_name');
+ $display_id = $request->request->get('view_display_id');
+ if (isset($name) && isset($display_id)) {
+ $args = $request->request->get('view_args');
+ $args = isset($args) && $args !== '' ? explode('/', $args) : array();
+ $path = $request->request->get('view_path');
+ $dom_id = $request->request->get('view_dom_id');
+ $dom_id = isset($dom_id) ? preg_replace('/[^a-zA-Z0-9_-]+/', '-', $dom_id) : NULL;
+ $pager_element = $request->request->get('pager_element');
+ $pager_element = isset($pager_element) ? intval($pager_element) : NULL;
+
+ $commands = array();
+
+ // Remove all of this stuff from the query of the request so it doesn't end
+ // up in pagers and tablesort URLs.
+ foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', 'ajax_html_ids', 'ajax_page_state') as $key) {
+ if ($request->query->has($key)) {
+ $request->query->remove($key);
+ }
+ if ($request->request->has($key)) {
+ $request->request->remove($key);
+ }
+ }
+
+ // Load the view.
+ $view = views_get_view($name);
+ if ($view && $view->access($display_id)) {
+ // Fix the current path for paging.
+ if (!empty($path)) {
+ _current_path($path);
+ }
+
+ // Add all $_POST data, because AJAX is always a post and many things,
+ // such as tablesorts, exposed filters and paging assume $_GET.
+ $request_all = $request->request->all();
+ $query_all = $request->query->all();
+ $request->query->replace($request_all + $query_all);
+
+ // Overwrite the destination.
+ // @see drupal_get_destination()
+ $origin_destination = $path;
+ $query = drupal_http_build_query($request->query->all());
+ if ($query != '') {
+ $origin_destination .= '?' . $query;
+ }
+ $destination = &drupal_static('drupal_get_destination');
+ $destination = array('destination' => $origin_destination);
+
+ // Override the display's pager_element with the one actually used.
+ if (isset($pager_element)) {
+ $commands[] = views_ajax_command_scroll_top('.view-dom-id-' . $dom_id);
+ $view->displayHandlers[$display_id]->setOption('pager_element', $pager_element);
+ }
+ // Reuse the same DOM id so it matches that in Drupal.settings.
+ $view->dom_id = $dom_id;
+
+ $commands[] = ajax_command_replace('.view-dom-id-' . $dom_id, $view->preview($display_id, $args));
+ }
+ drupal_alter('views_ajax_data', $commands, $view);
+ return array('#type' => 'ajax', '#commands' => $commands);
+ }
+}
+
+/**
+ * Creates a Drupal AJAX 'viewsSetForm' command.
+ *
+ * @param $output
+ * The form to display in the modal.
+ * @param $title
+ * The title.
+ * @param $url
+ * An optional URL.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_set_form($output, $title, $url = NULL) {
+ $command = array(
+ 'command' => 'viewsSetForm',
+ 'output' => $output,
+ 'title' => $title,
+ );
+ if (isset($url)) {
+ $command['url'] = $url;
+ }
+ return $command;
+}
+
+/**
+ * Creates a Drupal AJAX 'viewsDismissForm' command.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_dismiss_form() {
+ $command = array(
+ 'command' => 'viewsDismissForm',
+ );
+ return $command;
+}
+
+/**
+ * Creates a Drupal AJAX 'viewsHilite' command.
+ *
+ * @param $selector
+ * The selector to highlight
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_hilite($selector) {
+ return array(
+ 'command' => 'viewsHilite',
+ 'selector' => $selector,
+ );
+}
+
+/**
+ * Creates a Drupal AJAX 'addTab' command.
+ *
+ * @param $id
+ * The DOM ID.
+ * @param $title
+ * The title.
+ * @param $body
+ * The body.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_add_tab($id, $title, $body) {
+ $command = array(
+ 'command' => 'viewsAddTab',
+ 'id' => $id,
+ 'title' => $title,
+ 'body' => $body,
+ );
+ return $command;
+}
+
+/**
+ * Scroll to top of the current view.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_scroll_top($selector) {
+ $command = array(
+ 'command' => 'viewsScrollTop',
+ 'selector' => $selector,
+ );
+ return $command;
+}
+
+/**
+ * Shows Save and Cancel buttons.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_show_buttons() {
+ $command = array(
+ 'command' => 'viewsShowButtons',
+ );
+ return $command;
+}
+
+/**
+ * Trigger the Views live preview.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_trigger_preview() {
+ $command = array(
+ 'command' => 'viewsTriggerPreview',
+ );
+ return $command;
+}
+
+/**
+ * Replace the page title.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ */
+function views_ajax_command_replace_title($title) {
+ $command = array(
+ 'command' => 'viewsReplaceTitle',
+ 'title' => $title,
+ 'siteName' => config('system.site')->get('name'),
+ );
+ return $command;
+}
+
+/**
+ * Return an AJAX error.
+ */
+function views_ajax_error($message) {
+ $commands = array();
+ $commands[] = views_ajax_command_set_form($message, t('Error'));
+ return $commands;
+}
+
+/**
+ * Wrapper around drupal_build_form to handle some AJAX stuff automatically.
+ * This makes some assumptions about the client.
+ */
+function views_ajax_form_wrapper($form_id, &$form_state) {
+ // This won't override settings already in.
+ $form_state += array(
+ 'rerender' => FALSE,
+ 'no_redirect' => !empty($form_state['ajax']),
+ 'no_cache' => TRUE,
+ 'build_info' => array(
+ 'args' => array(),
+ ),
+ );
+
+ $form = drupal_build_form($form_id, $form_state);
+ $output = drupal_render($form);
+
+ // These forms have the title built in, so set the title here:
+ if (empty($form_state['ajax']) && !empty($form_state['title'])) {
+ drupal_set_title($form_state['title']);
+ drupal_add_css(drupal_get_path('module', 'views_ui') . '/css/views-admin.css');
+ }
+
+ if (!empty($form_state['ajax']) && (empty($form_state['executed']) || !empty($form_state['rerender']))) {
+ // If the form didn't execute and we're using ajax, build up a
+ // Ajax command list to execute.
+ $commands = array();
+
+ $display = '';
+ if ($messages = theme('status_messages')) {
+ $display = '<div class="views-messages">' . $messages . '</div>';
+ }
+ $display .= $output;
+
+ $title = empty($form_state['title']) ? '' : $form_state['title'];
+
+ $url = empty($form_state['url']) ? url(current_path(), array('absolute' => TRUE)) : $form_state['url'];
+
+ $commands[] = views_ajax_command_set_form($display, $title, $url);
+
+ if (!empty($form_state['#section'])) {
+ $commands[] = views_ajax_command_hilite('.' . drupal_clean_css_identifier($form_state['#section']));
+ }
+
+ return $commands;
+ }
+
+ // These forms have the title built in, so set the title here:
+ if (empty($form_state['ajax']) && !empty($form_state['title'])) {
+ drupal_set_title($form_state['title']);
+ }
+
+ return $output;
+}
+
+/**
+ * Page callback for views user autocomplete
+ */
+function views_ajax_autocomplete_user($string = '') {
+ // The user enters a comma-separated list of user name. We only autocomplete the last name.
+ $array = drupal_explode_tags($string);
+
+ // Fetch last name
+ $last_string = trim(array_pop($array));
+ $matches = array();
+ if ($last_string != '') {
+ $prefix = count($array) ? implode(', ', $array) . ', ' : '';
+
+ if (strpos('anonymous', strtolower($last_string)) !== FALSE) {
+ $matches[$prefix . 'Anonymous'] = 'Anonymous';
+ }
+
+ $result = db_select('users', 'u')
+ ->fields('u', array('uid', 'name'))
+ ->condition('u.name', db_like($last_string) . '%', 'LIKE')
+ ->range(0, 10)
+ ->execute()
+ ->fetchAllKeyed();
+
+ foreach ($result as $account) {
+ $n = $account;
+ // Commas and quotes in terms are special cases, so encode 'em.
+ if (strpos($account, ',') !== FALSE || strpos($account, '"') !== FALSE) {
+ $n = '"' . str_replace('"', '""', $account) . '"';
+ }
+ $matches[$prefix . $n] = check_plain($account);
+ }
+ }
+
+ return new JsonResponse($matches);
+}
+
+/**
+ * Page callback for views taxonomy autocomplete.
+ *
+ * @param $vid
+ * The vocabulary id of the tags which should be returned.
+ *
+ * @param $tags_typed
+ * The typed string of the user.
+ *
+ * @see taxonomy_autocomplete()
+ */
+function views_ajax_autocomplete_taxonomy($vid, $tags_typed = '') {
+ // The user enters a comma-separated list of tags. We only autocomplete the last tag.
+ $tags_typed = drupal_explode_tags($tags_typed);
+ $tag_last = drupal_strtolower(array_pop($tags_typed));
+
+ $matches = array();
+ if ($tag_last != '') {
+
+ $query = db_select('taxonomy_term_data', 't');
+ $query->addTag('translatable');
+ $query->addTag('term_access');
+
+ // Do not select already entered terms.
+ if (!empty($tags_typed)) {
+ $query->condition('t.name', $tags_typed, 'NOT IN');
+ }
+ // Select rows that match by term name.
+ $tags_return = $query
+ ->fields('t', array('tid', 'name'))
+ ->condition('t.vid', $vid)
+ ->condition('t.name', '%' . db_like($tag_last) . '%', 'LIKE')
+ ->range(0, 10)
+ ->execute()
+ ->fetchAllKeyed();
+
+ $prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
+
+ $term_matches = array();
+ foreach ($tags_return as $tid => $name) {
+ $n = $name;
+ // Term names containing commas or quotes must be wrapped in quotes.
+ if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
+ $n = '"' . str_replace('"', '""', $name) . '"';
+ }
+ // Add term name to list of matches.
+ $term_matches[$prefix . $n] = check_plain($name);
+ }
+ }
+
+ return new JsonResponse($term_matches);
+}
+
+/**
+ * @}
+ */
diff --git a/core/modules/views/includes/cache.inc b/core/modules/views/includes/cache.inc
new file mode 100644
index 0000000..95d2322
--- /dev/null
+++ b/core/modules/views/includes/cache.inc
@@ -0,0 +1,159 @@
+<?php
+
+/**
+ * @file
+ * Load Views' data so that it knows what is available to build queries from.
+ */
+
+/**
+ * Fetch Views' data from the cache.
+ *
+ * $param string|null $table
+ * (optional) The name of the table for which to fetch Views' data. If
+ * NULL, data for all tables will be retrieved
+ * @param bool $reset
+ * (optional) Whether to rebuild the cache. Defaults to FALSE.
+ *
+ * @return array
+ * An associative array of views data for the given table. If $table is
+ * NULL, the array will be keyed by table name, with each key corresponding
+ * to the data array for that table.
+ *
+ * @see hook_views_data()
+ */
+function _views_fetch_data($table = NULL, $reset = FALSE) {
+ $cache = &drupal_static(__FUNCTION__ . '_cache');
+ $recursion_protection = &drupal_static(__FUNCTION__ . '_recursion_protected');
+ $fully_loaded = &drupal_static(__FUNCTION__ . '_fully_loaded');
+ if ($reset) {
+ $cache = NULL;
+ $fully_loaded = FALSE;
+ }
+ if ($table) {
+ if (!isset($cache[$table])) {
+ $cid = 'views_data:' . $table;
+ $data = views_cache_get($cid, TRUE);
+ if (!empty($data->data)) {
+ $cache[$table] = $data->data;
+ }
+ else {
+ // No cache entry, rebuild.
+ $cache = _views_fetch_data_build();
+ $fully_loaded = TRUE;
+ }
+ }
+ if (isset($cache[$table])) {
+ return $cache[$table];
+ }
+ }
+ else {
+ if (!$fully_loaded) {
+ $data = views_cache_get('views_data', TRUE);
+ if (!empty($data->data)) {
+ $cache = $data->data;
+ }
+
+ if (empty($cache)) {
+ $cache = _views_fetch_data_build();
+ }
+ $fully_loaded = TRUE;
+ }
+ return $cache;
+ }
+ // Return an empty array if there is no match.
+ return array();
+}
+
+/**
+ * Build, set the views data cache if empty and return the views data.
+ *
+ * @return array
+ * The views_data of all tables.
+ */
+function _views_fetch_data_build() {
+ views_include_handlers();
+ $cache = module_invoke_all('views_data');
+ foreach (module_implements('views_data_alter') as $module) {
+ $function = $module . '_views_data_alter';
+ $function($cache);
+ }
+ _views_data_process_entity_types($cache);
+
+ // Keep a record with all data.
+ views_cache_set('views_data', $cache, TRUE);
+ // Save data in seperate cache entries.
+ foreach ($cache as $key => $data) {
+ $cid = 'views_data:' . $key;
+ views_cache_set($cid, $data, TRUE);
+ }
+ return $cache;
+}
+
+/**
+ * Links tables having an 'entity type' specified to the respective generic entity-type tables.
+ */
+function _views_data_process_entity_types(&$data) {
+ foreach ($data as $table_name => $table_info) {
+ // Add in a join from the entity-table if an entity-type is given.
+ if (!empty($table_info['table']['entity type'])) {
+ $entity_table = 'views_entity_' . $table_info['table']['entity type'];
+
+ $data[$entity_table]['table']['join'][$table_name] = array(
+ 'left_table' => $table_name,
+ );
+ $data[$entity_table]['table']['entity type'] = $table_info['table']['entity type'];
+ // Copy over the default table group if we have none yet.
+ if (!empty($table_info['table']['group']) && empty($data[$entity_table]['table']['group'])) {
+ $data[$entity_table]['table']['group'] = $table_info['table']['group'];
+ }
+ }
+ }
+}
+
+/**
+ * Set a cached item in the views cache.
+ *
+ * This is just a convenience wrapper around cache_set().
+ *
+ * @param $cid
+ * The cache ID of the data to store.
+ * @param $data
+ * The data to store in the cache. Complex data types will be automatically serialized before insertion.
+ * Strings will be stored as plain text and not serialized.
+ * @param $use_language
+ * If TRUE, the data will be cached specific to the currently active language.
+ */
+function views_cache_set($cid, $data, $use_language = FALSE) {
+ if (config('views.settings')->get('skip_cache')) {
+ return;
+ }
+ if ($use_language) {
+ $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
+ }
+
+ cache('views_info')->set($cid, $data);
+}
+
+/**
+ * Return data from the persistent views cache.
+ *
+ * This is just a convenience wrapper around cache_get().
+ *
+ * @param int $cid
+ * The cache ID of the data to retrieve.
+ * @param bool $use_language
+ * If TRUE, the data will be requested specific to the currently active language.
+ *
+ * @return stdClass|bool
+ * The cache or FALSE on failure.
+ */
+function views_cache_get($cid, $use_language = FALSE) {
+ if (config('views.settings')->get('skip_cache')) {
+ return FALSE;
+ }
+ if ($use_language) {
+ $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
+ }
+
+ return cache('views_info')->get($cid);
+}
diff --git a/core/modules/views/js/ajax.js b/core/modules/views/js/ajax.js
new file mode 100644
index 0000000..65ebfa4
--- /dev/null
+++ b/core/modules/views/js/ajax.js
@@ -0,0 +1,237 @@
+/**
+ * @file
+ * Handles AJAX submission and response in Views UI.
+ */
+(function ($) {
+
+ "use strict";
+
+ Drupal.ajax.prototype.commands.viewsSetForm = function (ajax, response, status) {
+ var ajax_title = Drupal.settings.views.ajax.title;
+ var ajax_body = Drupal.settings.views.ajax.id;
+ var ajax_popup = Drupal.settings.views.ajax.popup;
+ $(ajax_title).html(response.title);
+ $(ajax_body).html(response.output);
+ $(ajax_popup).dialog('open');
+ Drupal.attachBehaviors($(ajax_popup), ajax.settings);
+ if (response.url) {
+ // Identify the button that was clicked so that .ajaxSubmit() can use it.
+ // We need to do this for both .click() and .mousedown() since JavaScript
+ // code might trigger either behavior.
+ var $submit_buttons = $('input[type=submit], button', ajax_body);
+ $submit_buttons.click(function(event) {
+ this.form.clk = this;
+ });
+ $submit_buttons.mousedown(function(event) {
+ this.form.clk = this;
+ });
+
+ $('form', ajax_body).once('views-ajax-submit-processed').each(function() {
+ var element_settings = { 'url': response.url, 'event': 'submit', 'progress': { 'type': 'throbber' } };
+ var $form = $(this);
+ var id = $form.attr('id');
+ Drupal.ajax[id] = new Drupal.ajax(id, this, element_settings);
+ Drupal.ajax[id].form = $form;
+ });
+ }
+ Drupal.viewsUi.resizeModal();
+ };
+
+ Drupal.ajax.prototype.commands.viewsDismissForm = function (ajax, response, status) {
+ Drupal.ajax.prototype.commands.viewsSetForm({}, {'title': '', 'output': Drupal.settings.views.ajax.defaultForm});
+ $(Drupal.settings.views.ajax.popup).dialog('close');
+ };
+
+ Drupal.ajax.prototype.commands.viewsHilite = function (ajax, response, status) {
+ $('.hilited').removeClass('hilited');
+ $(response.selector).addClass('hilited');
+ };
+
+ Drupal.ajax.prototype.commands.viewsAddTab = function (ajax, response, status) {
+ var id = '#views-tab-' + response.id;
+ $('#views-tabset').viewsAddTab(id, response.title, 0);
+ $(id).html(response.body).addClass('views-tab');
+
+ // Update the preview widget to preview the new tab.
+ var display_id = id.replace('#views-tab-', '');
+ $("#preview-display-id").append('<option selected="selected" value="' + display_id + '">' + response.title + '</option>');
+
+ Drupal.attachBehaviors(id);
+ var instance = $.viewsUi.tabs.instances[$('#views-tabset').get(0).UI_TABS_UUID];
+ $('#views-tabset').viewsClickTab(instance.$tabs.length);
+ };
+
+ Drupal.ajax.prototype.commands.viewsShowButtons = function (ajax, response, status) {
+ $('div.views-edit-view div.form-actions').removeClass('js-hide');
+ $('div.views-edit-view div.view-changed.messages').removeClass('js-hide');
+ };
+
+ Drupal.ajax.prototype.commands.viewsTriggerPreview = function (ajax, response, status) {
+ if ($('input#edit-displays-live-preview').is(':checked')) {
+ $('#preview-submit').trigger('click');
+ }
+ };
+
+ Drupal.ajax.prototype.commands.viewsReplaceTitle = function (ajax, response, status) {
+ // In case we're in the overlay, get a reference to the underlying window.
+ var doc = parent.document;
+ // For the <title> element, make a best-effort attempt to replace the page
+ // title and leave the site name alone. If the theme doesn't use the site
+ // name in the <title> element, this will fail.
+ var oldTitle = doc.title;
+ // Escape the site name, in case it has special characters in it, so we can
+ // use it in our regex.
+ var escapedSiteName = response.siteName.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+ var re = new RegExp('.+ (.) ' + escapedSiteName);
+ doc.title = oldTitle.replace(re, response.title + ' $1 ' + response.siteName);
+
+ $('h1.page-title').text(response.title);
+ $('h1#overlay-title').text(response.title);
+ };
+
+ /**
+ * Get rid of irritating tabledrag messages
+ */
+ Drupal.theme.tableDragChangedWarning = function () {
+ return [];
+ };
+
+ /**
+ * Trigger preview when the "live preview" checkbox is checked.
+ */
+ Drupal.behaviors.livePreview = {
+ attach: function (context) {
+ $('input#edit-displays-live-preview', context).once('views-ajax-processed').click(function() {
+ if ($(this).is(':checked')) {
+ $('#preview-submit').click();
+ }
+ });
+ }
+ };
+
+ /**
+ * Sync preview display.
+ */
+ Drupal.behaviors.syncPreviewDisplay = {
+ attach: function (context) {
+ $("#views-tabset a").once('views-ajax-processed').click(function() {
+ var href = $(this).attr('href');
+ // Cut of #views-tabset.
+ var display_id = href.substr(11);
+ // Set the form element.
+ $("#views-live-preview #preview-display-id").val(display_id);
+ }).addClass('views-ajax-processed');
+ }
+ };
+
+ Drupal.behaviors.viewsAjax = {
+ collapseReplaced: false,
+ attach: function (context, settings) {
+ if (!settings.views) {
+ return;
+ }
+ // Create a jQuery UI dialog, but leave it closed.
+ var dialog_area = $(settings.views.ajax.popup, context);
+ dialog_area.dialog({
+ 'autoOpen': false,
+ 'dialogClass': 'views-ui-dialog',
+ 'modal': true,
+ 'position': 'center',
+ 'resizable': false,
+ 'width': 750
+ });
+
+ var base_element_settings = {
+ 'event': 'click',
+ 'progress': { 'type': 'throbber' }
+ };
+ // Bind AJAX behaviors to all items showing the class.
+ $('a.views-ajax-link', context).once('views-ajax-processed').each(function () {
+ var element_settings = base_element_settings;
+ // Set the URL to go to the anchor.
+ if ($(this).attr('href')) {
+ element_settings.url = $(this).attr('href');
+ }
+ var base = $(this).attr('id');
+ Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
+ });
+
+ $('div#views-live-preview a')
+ .once('views-ajax-processed').each(function () {
+ // We don't bind to links without a URL.
+ if (!$(this).attr('href')) {
+ return true;
+ }
+
+ var element_settings = base_element_settings;
+ // Set the URL to go to the anchor.
+ element_settings.url = $(this).attr('href');
+ if (Drupal.Views.getPath(element_settings.url).substring(0, 21) !== 'admin/structure/views') {
+ return true;
+ }
+
+ element_settings.wrapper = 'views-live-preview';
+ element_settings.method = 'html';
+ var base = $(this).attr('id');
+ Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
+ });
+
+ // Within a live preview, make exposed widget form buttons re-trigger the
+ // Preview button.
+ // @todo Revisit this after fixing Views UI to display a Preview outside
+ // of the main Edit form.
+ $('div#views-live-preview input[type=submit]')
+ .once('views-ajax-processed').each(function(event) {
+ $(this).click(function () {
+ this.form.clk = this;
+ return true;
+ });
+ var element_settings = base_element_settings;
+ // Set the URL to go to the anchor.
+ element_settings.url = $(this.form).attr('action');
+ if (Drupal.Views.getPath(element_settings.url).substring(0, 21) !== 'admin/structure/views') {
+ return true;
+ }
+
+ element_settings.wrapper = 'views-live-preview';
+ element_settings.method = 'html';
+ element_settings.event = 'click';
+
+ var base = $(this).attr('id');
+ Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
+ });
+
+ if (!this.collapseReplaced && Drupal.collapseScrollIntoView) {
+ this.collapseReplaced = true;
+ Drupal.collapseScrollIntoView = function (node) {
+ for (var $parent = $(node); $parent.get(0) !== document && $parent.size() !== 0; $parent = $parent.parent()) {
+ if ($parent.css('overflow') === 'scroll' || $parent.css('overflow') === 'auto') {
+ if (Drupal.viewsUi.resizeModal) {
+ // If the modal is already at the max height, don't bother with
+ // this since the only reason to do it is to grow the modal.
+ if ($('.views-ui-dialog').height() < parseInt($(window).height() * .8)) {
+ Drupal.viewsUi.resizeModal('', true);
+ }
+ }
+ return;
+ }
+ }
+
+ var h = document.documentElement.clientHeight || document.body.clientHeight || 0;
+ var offset = document.documentElement.scrollTop || document.body.scrollTop || 0;
+ var posY = $(node).offset().top;
+ var fudge = 55;
+ if (posY + node.offsetHeight + fudge > h + offset) {
+ if (node.offsetHeight > h) {
+ window.scrollTo(0, posY);
+ }
+ else {
+ window.scrollTo(0, posY + node.offsetHeight - h + fudge);
+ }
+ }
+ };
+ }
+ }
+ };
+
+})(jQuery);
diff --git a/core/modules/views/js/ajax_view.js b/core/modules/views/js/ajax_view.js
new file mode 100644
index 0000000..dafc886
--- /dev/null
+++ b/core/modules/views/js/ajax_view.js
@@ -0,0 +1,138 @@
+/**
+ * @file
+ * Handles AJAX fetching of views, including filter submission and response.
+ */
+(function ($) {
+
+"use strict";
+
+/**
+ * Attaches the AJAX behavior to Views exposed filter forms and key View links.
+ */
+Drupal.behaviors.ViewsAjaxView = {};
+Drupal.behaviors.ViewsAjaxView.attach = function() {
+ if (Drupal.settings && Drupal.settings.views && Drupal.settings.views.ajaxViews) {
+ $.each(Drupal.settings.views.ajaxViews, function(i, settings) {
+ Drupal.views.instances[i] = new Drupal.views.ajaxView(settings);
+ });
+ }
+};
+
+Drupal.views = {};
+Drupal.views.instances = {};
+
+/**
+ * Javascript object for a certain view.
+ */
+Drupal.views.ajaxView = function(settings) {
+ var selector = '.view-dom-id-' + settings.view_dom_id;
+ this.$view = $(selector);
+
+ // Retrieve the path to use for views' ajax.
+ var ajax_path = Drupal.settings.views.ajax_path;
+
+ // If there are multiple views this might've ended up showing up multiple times.
+ if (ajax_path.constructor.toString().indexOf("Array") !== -1) {
+ ajax_path = ajax_path[0];
+ }
+
+ // Check if there are any GET parameters to send to views.
+ var queryString = window.location.search || '';
+ if (queryString !== '') {
+ // Remove the question mark and Drupal path component if any.
+ var queryString = queryString.slice(1).replace(/q=[^&]+&?|&?render=[^&]+/, '');
+ if (queryString !== '') {
+ // If there is a '?' in ajax_path, clean url are on and & should be used to add parameters.
+ queryString = ((/\?/.test(ajax_path)) ? '&' : '?') + queryString;
+ }
+ }
+
+ this.element_settings = {
+ url: ajax_path + queryString,
+ submit: settings,
+ setClick: true,
+ event: 'click',
+ selector: selector,
+ progress: { type: 'throbber' }
+ };
+
+ this.settings = settings;
+
+ // Add the ajax to exposed forms.
+ this.$exposed_form = $('form#views-exposed-form-'+ settings.view_name.replace(/_/g, '-') + '-' + settings.view_display_id.replace(/_/g, '-'));
+ this.$exposed_form.once(jQuery.proxy(this.attachExposedFormAjax, this));
+
+ // Add the ajax to pagers.
+ this.$view
+ // Don't attach to nested views. Doing so would attach multiple behaviors
+ // to a given element.
+ .filter(jQuery.proxy(this.filterNestedViews, this))
+ .once(jQuery.proxy(this.attachPagerAjax, this));
+};
+
+Drupal.views.ajaxView.prototype.attachExposedFormAjax = function() {
+ var button = $('input[type=submit], input[type=image]', this.$exposed_form);
+ button = button[0];
+
+ this.exposedFormAjax = new Drupal.ajax($(button).attr('id'), button, this.element_settings);
+};
+
+Drupal.views.ajaxView.prototype.filterNestedViews= function() {
+ // If there is at least one parent with a view class, this view
+ // is nested (e.g., an attachment). Bail.
+ return !this.$view.parents('.view').size();
+};
+
+/**
+ * Attach the ajax behavior to each link.
+ */
+Drupal.views.ajaxView.prototype.attachPagerAjax = function() {
+ this.$view.find('ul.pager > li > a, th.views-field a, .attachment .views-summary a')
+ .each(jQuery.proxy(this.attachPagerLinkAjax, this));
+};
+
+/**
+ * Attach the ajax behavior to a singe link.
+ */
+Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function(id, link) {
+ var $link = $(link);
+ var viewData = {};
+ var href = $link.attr('href');
+ // Construct an object using the settings defaults and then overriding
+ // with data specific to the link.
+ $.extend(
+ viewData,
+ this.settings,
+ Drupal.Views.parseQueryString(href),
+ // Extract argument data from the URL.
+ Drupal.Views.parseViewArgs(href, this.settings.view_base_path)
+ );
+
+ // For anchor tags, these will go to the target of the anchor rather
+ // than the usual location.
+ $.extend(viewData, Drupal.Views.parseViewArgs(href, this.settings.view_base_path));
+
+ this.element_settings.submit = viewData;
+ this.pagerAjax = new Drupal.ajax(false, $link, this.element_settings);
+};
+
+Drupal.ajax.prototype.commands.viewsScrollTop = function (ajax, response, status) {
+ // Scroll to the top of the view. This will allow users
+ // to browse newly loaded content after e.g. clicking a pager
+ // link.
+ var offset = $(response.selector).offset();
+ // We can't guarantee that the scrollable object should be
+ // the body, as the view could be embedded in something
+ // more complex such as a modal popup. Recurse up the DOM
+ // and scroll the first element that has a non-zero top.
+ var scrollTarget = response.selector;
+ while ($(scrollTarget).scrollTop() === 0 && $(scrollTarget).parent()) {
+ scrollTarget = $(scrollTarget).parent();
+ }
+ // Only scroll upward
+ if (offset.top - 10 < $(scrollTarget).scrollTop()) {
+ $(scrollTarget).animate({scrollTop: (offset.top - 10)}, 500);
+ }
+};
+
+})(jQuery);
diff --git a/core/modules/views/js/base.js b/core/modules/views/js/base.js
new file mode 100644
index 0000000..90482e4
--- /dev/null
+++ b/core/modules/views/js/base.js
@@ -0,0 +1,112 @@
+/**
+ * @file
+ * Some basic behaviors and utility functions for Views.
+ */
+(function ($) {
+
+ "use strict";
+
+ Drupal.Views = {};
+
+ /**
+ * jQuery UI tabs, Views integration component
+ */
+ Drupal.behaviors.viewsTabs = {
+ attach: function (context) {
+ if ($.viewsUi && $.viewsUi.tabs) {
+ $('#views-tabset').once('views-processed').viewsTabs({
+ selectedClass: 'active'
+ });
+ }
+
+ $('a.views-remove-link').once('views-processed').click(function(event) {
+ var id = $(this).attr('id').replace('views-remove-link-', '');
+ $('#views-row-' + id).hide();
+ $('#views-removed-' + id).attr('checked', true);
+ event.preventDefault();
+ });
+ /**
+ * Here is to handle display deletion
+ * (checking in the hidden checkbox and hiding out the row)
+ */
+ $('a.display-remove-link')
+ .addClass('display-processed')
+ .click(function() {
+ var id = $(this).attr('id').replace('display-remove-link-', '');
+ $('#display-row-' + id).hide();
+ $('#display-removed-' + id).attr('checked', true);
+ event.preventDefault();
+ });
+ }
+ };
+
+ /**
+ * Helper function to parse a querystring.
+ */
+ Drupal.Views.parseQueryString = function (query) {
+ var args = {};
+ var pos = query.indexOf('?');
+ if (pos !== -1) {
+ query = query.substring(pos + 1);
+ }
+ var pairs = query.split('&');
+ for(var i in pairs) {
+ if (typeof(pairs[i]) === 'string') {
+ var pair = pairs[i].split('=');
+ // Ignore the 'q' path argument, if present.
+ if (pair[0] !== 'q' && pair[1]) {
+ args[decodeURIComponent(pair[0].replace(/\+/g, ' '))] = decodeURIComponent(pair[1].replace(/\+/g, ' '));
+ }
+ }
+ }
+ return args;
+ };
+
+ /**
+ * Helper function to return a view's arguments based on a path.
+ */
+ Drupal.Views.parseViewArgs = function (href, viewPath) {
+ var returnObj = {};
+ var path = Drupal.Views.getPath(href);
+ // Ensure we have a correct path.
+ if (viewPath && path.substring(0, viewPath.length + 1) === viewPath + '/') {
+ var args = decodeURIComponent(path.substring(viewPath.length + 1, path.length));
+ returnObj.view_args = args;
+ returnObj.view_path = path;
+ }
+ return returnObj;
+ };
+
+ /**
+ * Strip off the protocol plus domain from an href.
+ */
+ Drupal.Views.pathPortion = function (href) {
+ // Remove e.g. http://example.com if present.
+ var protocol = window.location.protocol;
+ if (href.substring(0, protocol.length) === protocol) {
+ // 2 is the length of the '//' that normally follows the protocol
+ href = href.substring(href.indexOf('/', protocol.length + 2));
+ }
+ return href;
+ };
+
+ /**
+ * Return the Drupal path portion of an href.
+ */
+ Drupal.Views.getPath = function (href) {
+ href = Drupal.Views.pathPortion(href);
+ href = href.substring(Drupal.settings.basePath.length, href.length);
+ // 3 is the length of the '?q=' added to the url without clean urls.
+ if (href.substring(0, 3) === '?q=') {
+ href = href.substring(3, href.length);
+ }
+ var chars = ['#', '?', '&'];
+ for (var i in chars) {
+ if (href.indexOf(chars[i]) > -1) {
+ href = href.substr(0, href.indexOf(chars[i]));
+ }
+ }
+ return href;
+ };
+
+})(jQuery);
diff --git a/core/modules/views/js/jquery.ui.dialog.patch.js b/core/modules/views/js/jquery.ui.dialog.patch.js
new file mode 100644
index 0000000..1fb9b0b
--- /dev/null
+++ b/core/modules/views/js/jquery.ui.dialog.patch.js
@@ -0,0 +1,30 @@
+/**
+ * This is part of a patch to address a jQueryUI bug. The bug is responsible
+ * for the inability to scroll a page when a modal dialog is active. If the content
+ * of the dialog extends beyond the bottom of the viewport, the user is only able
+ * to scroll with a mousewheel or up/down keyboard keys.
+ *
+ * @see http://bugs.jqueryui.com/ticket/4671
+ * @see https://bugs.webkit.org/show_bug.cgi?id=19033
+ * @see views_ui.module
+ * @see js/jquery.ui.dialog.min.js
+ *
+ * This javascript patch overwrites the $.ui.dialog.overlay.events object to remove
+ * the mousedown, mouseup and click events from the list of events that are bound
+ * in $.ui.dialog.overlay.create
+ *
+ * The original code for this object:
+ * $.ui.dialog.overlay.events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
+ * function(event) { return event + '.dialog-overlay'; }).join(' '),
+ *
+ */
+
+(function ($, undefined) {
+
+ "use strict";
+
+ if ($.ui && $.ui.dialog) {
+ $.ui.dialog.overlay.events = $.map('focus,keydown,keypress'.split(','),
+ function(event) { return event + '.dialog-overlay'; }).join(' ');
+ }
+}(jQuery));
diff --git a/core/modules/views/js/views-contextual.js b/core/modules/views/js/views-contextual.js
new file mode 100644
index 0000000..3c3ae96
--- /dev/null
+++ b/core/modules/views/js/views-contextual.js
@@ -0,0 +1,18 @@
+/**
+ * @file
+ * Javascript related to contextual links.
+ */
+(function ($) {
+
+"use strict";
+
+Drupal.behaviors.viewsContextualLinks = {
+ attach: function (context) {
+ // If there are views-related contextual links attached to the main page
+ // content, find the smallest region that encloses both the links and the
+ // view, and display it as a contextual links region.
+ $('.views-contextual-links-page', context).closest(':has(.view)').addClass('contextual-region');
+ }
+};
+
+})(jQuery);
diff --git a/core/modules/views/lib/Drupal/views/Analyzer.php b/core/modules/views/lib/Drupal/views/Analyzer.php
new file mode 100644
index 0000000..8021e0d
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Analyzer.php
@@ -0,0 +1,131 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Analyzer.
+ */
+
+namespace Drupal\views;
+
+use Drupal\views\ViewExecutable;
+
+/**
+ * This tool is a small plugin manager to perform analysis on a view and
+ * report results to the user. This tool is meant to let modules that
+ * provide data to Views also help users properly use that data by
+ * detecting invalid configurations. Views itself comes with only a
+ * small amount of analysis tools, but more could easily be added either
+ * by modules or as patches to Views itself.
+ */
+class Analyzer {
+
+ /**
+ * The view to analyze.
+ *
+ * @var Drupal\views\ViewExecutable.
+ */
+ protected $view;
+
+ /**
+ * Constructs the analyzer object.
+ *
+ * @param Drupal\views\ViewExecutable $view
+ * (optional) The view to analyze.
+ */
+ function __construct(ViewExecutable $view = NULL) {
+ if (isset($view)) {
+ $this->view = $view;
+ }
+ }
+
+ /**
+ * Sets the view which is analyzed by this analyzer.
+ *
+ * @param Drupal\views\ViewExecutable
+ * The view to analyze.
+ */
+ public function setView(ViewExecutable $view = NULL) {
+ $this->view = $view;
+ }
+
+ /**
+ * Analyzes a review and return the results.
+ *
+ * @return array
+ * An array of analyze results organized into arrays keyed by 'ok',
+ * 'warning' and 'error'.
+ */
+ public function getMessages() {
+ $this->view->initDisplay();
+ $messages = module_invoke_all('views_analyze', $this->view);
+
+ return $messages;
+ }
+
+ /**
+ * Formats the analyze result into a message string.
+ *
+ * This is based upon the format of drupal_set_message which uses separate
+ * boxes for "ok", "warning" and "error".
+ */
+ public function formatMessages(array $messages) {
+ if (empty($messages)) {
+ $messages = array($this->formatMessage(t('View analysis can find nothing to report.'), 'ok'));
+ }
+
+ $types = array('ok' => array(), 'warning' => array(), 'error' => array());
+ foreach ($messages as $message) {
+ if (empty($types[$message['type']])) {
+ $types[$message['type']] = array();
+ }
+ $types[$message['type']][] = $message['message'];
+ }
+
+ $output = '';
+ foreach ($types as $type => $messages) {
+ $type .= ' messages';
+ $message = '';
+ if (count($messages) > 1) {
+ $message = theme('item_list', array('items' => $messages));
+ }
+ elseif ($messages) {
+ $message = array_shift($messages);
+ }
+
+ if ($message) {
+ $output .= "<div class=\"$type\">$message</div>";
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Formats an analysis message.
+ *
+ * This tool should be called by any module responding to the analyze hook
+ * to properly format the message. It is usually used in the form:
+ * @code
+ * $ret[] = Analyzer::formatMessage(t('This is the message'), 'ok');
+ * @endcode
+ *
+ * The 'ok' status should be used to provide information about things
+ * that are acceptable. In general analysis isn't interested in 'ok'
+ * messages, but instead the 'warning', which is a category for items
+ * that may be broken unless the user knows what he or she is doing,
+ * and 'error' for items that are definitely broken are much more useful.
+ *
+ * @param string $message
+ * @param string $type
+ * The type of message. This should be "ok", "warning" or "error". Other
+ * values can be used but how they are treated by the output routine
+ * is undefined.
+ *
+ * @return array
+ * A single formatted message, consisting of a key message and a key type.
+ */
+ static function formatMessage($message, $type = 'error') {
+ return array('message' => $message, 'type' => $type);
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/ManyToOneHelper.php b/core/modules/views/lib/Drupal/views/ManyToOneHelper.php
new file mode 100644
index 0000000..18320eb
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/ManyToOneHelper.php
@@ -0,0 +1,332 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\ManyToOneHelper.
+ */
+
+namespace Drupal\views;
+
+use Drupal\views\Plugin\views\HandlerBase;
+
+/**
+ * This many to one helper object is used on both arguments and filters.
+ *
+ * @todo This requires extensive documentation on how this class is to
+ * be used. For now, look at the arguments and filters that use it. Lots
+ * of stuff is just pass-through but there are definitely some interesting
+ * areas where they interact.
+ *
+ * Any handler that uses this can have the following possibly additional
+ * definition terms:
+ * - numeric: If true, treat this field as numeric, using %d instead of %s in
+ * queries.
+ *
+ */
+class ManyToOneHelper {
+
+ function __construct($handler) {
+ $this->handler = $handler;
+ }
+
+ public static function defineOptions(&$options) {
+ $options['reduce_duplicates'] = array('default' => FALSE, 'bool' => TRUE);
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ $form['reduce_duplicates'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Reduce duplicates'),
+ '#description' => t('This filter can cause items that have more than one of the selected options to appear as duplicate results. If this filter causes duplicate results to occur, this checkbox can reduce those duplicates; however, the more terms it has to search for, the less performant the query will be, so use this with caution. Shouldn\'t be set on single-value fields, as it may cause values to disappear from display, if used on an incompatible field.'),
+ '#default_value' => !empty($this->handler->options['reduce_duplicates']),
+ '#weight' => 4,
+ );
+ }
+
+ /**
+ * Sometimes the handler might want us to use some kind of formula, so give
+ * it that option. If it wants us to do this, it must set $helper->formula = TRUE
+ * and implement handler->get_formula();
+ */
+ public function getField() {
+ if (!empty($this->formula)) {
+ return $this->handler->get_formula();
+ }
+ else {
+ return $this->handler->tableAlias . '.' . $this->handler->realField;
+ }
+ }
+
+ /**
+ * Add a table to the query.
+ *
+ * This is an advanced concept; not only does it add a new instance of the table,
+ * but it follows the relationship path all the way down to the relationship
+ * link point and adds *that* as a new relationship and then adds the table to
+ * the relationship, if necessary.
+ */
+ function add_table($join = NULL, $alias = NULL) {
+ // This is used for lookups in the many_to_one table.
+ $field = $this->handler->relationship . '_' . $this->handler->table . '.' . $this->handler->field;
+
+ if (empty($join)) {
+ $join = $this->getJoin();
+ }
+
+ // See if there's a chain between us and the base relationship. If so, we need
+ // to create a new relationship to use.
+ $relationship = $this->handler->relationship;
+
+ // Determine the primary table to seek
+ if (empty($this->handler->query->relationships[$relationship])) {
+ $base_table = $this->handler->query->base_table;
+ }
+ else {
+ $base_table = $this->handler->query->relationships[$relationship]['base'];
+ }
+
+ // Cycle through the joins. This isn't as error-safe as the normal
+ // ensure_path logic. Perhaps it should be.
+ $r_join = clone $join;
+ while ($r_join->leftTable != $base_table) {
+ $r_join = HandlerBase::getTableJoin($r_join->leftTable, $base_table);
+ }
+ // If we found that there are tables in between, add the relationship.
+ if ($r_join->table != $join->table) {
+ $relationship = $this->handler->query->add_relationship($this->handler->table . '_' . $r_join->table, $r_join, $r_join->table, $this->handler->relationship);
+ }
+
+ // And now add our table, using the new relationship if one was used.
+ $alias = $this->handler->query->add_table($this->handler->table, $relationship, $join, $alias);
+
+ // Store what values are used by this table chain so that other chains can
+ // automatically discard those values.
+ if (empty($this->handler->view->many_to_one_tables[$field])) {
+ $this->handler->view->many_to_one_tables[$field] = $this->handler->value;
+ }
+ else {
+ $this->handler->view->many_to_one_tables[$field] = array_merge($this->handler->view->many_to_one_tables[$field], $this->handler->value);
+ }
+
+ return $alias;
+ }
+
+ public function getJoin() {
+ return $this->handler->getJoin();
+ }
+
+ /**
+ * Provide the proper join for summary queries. This is important in part because
+ * it will cooperate with other arguments if possible.
+ */
+ function summary_join() {
+ $field = $this->handler->relationship . '_' . $this->handler->table . '.' . $this->handler->field;
+ $join = $this->getJoin();
+
+ // shortcuts
+ $options = $this->handler->options;
+ $view = &$this->handler->view;
+ $query = &$this->handler->query;
+
+ if (!empty($options['require_value'])) {
+ $join->type = 'INNER';
+ }
+
+ if (empty($options['add_table']) || empty($view->many_to_one_tables[$field])) {
+ return $query->ensure_table($this->handler->table, $this->handler->relationship, $join);
+ }
+ else {
+ if (!empty($view->many_to_one_tables[$field])) {
+ foreach ($view->many_to_one_tables[$field] as $value) {
+ $join->extra = array(
+ array(
+ 'field' => $this->handler->realField,
+ 'operator' => '!=',
+ 'value' => $value,
+ 'numeric' => !empty($this->definition['numeric']),
+ ),
+ );
+ }
+ }
+ return $this->add_table($join);
+ }
+ }
+
+ /**
+ * Override ensureMyTable so we can control how this joins in.
+ * The operator actually has influence over joining.
+ */
+ public function ensureMyTable() {
+ if (!isset($this->handler->tableAlias)) {
+ // Case 1: Operator is an 'or' and we're not reducing duplicates.
+ // We hence get the absolute simplest:
+ $field = $this->handler->relationship . '_' . $this->handler->table . '.' . $this->handler->field;
+ if ($this->handler->operator == 'or' && empty($this->handler->options['reduce_duplicates'])) {
+ if (empty($this->handler->options['add_table']) && empty($this->handler->view->many_to_one_tables[$field])) {
+ // query optimization, INNER joins are slightly faster, so use them
+ // when we know we can.
+ $join = $this->getJoin();
+ if (isset($join)) {
+ $join->type = 'INNER';
+ }
+ $this->handler->tableAlias = $this->handler->query->ensure_table($this->handler->table, $this->handler->relationship, $join);
+ $this->handler->view->many_to_one_tables[$field] = $this->handler->value;
+ }
+ else {
+ $join = $this->getJoin();
+ $join->type = 'LEFT';
+ if (!empty($this->handler->view->many_to_one_tables[$field])) {
+ foreach ($this->handler->view->many_to_one_tables[$field] as $value) {
+ $join->extra = array(
+ array(
+ 'field' => $this->handler->realField,
+ 'operator' => '!=',
+ 'value' => $value,
+ 'numeric' => !empty($this->handler->definition['numeric']),
+ ),
+ );
+ }
+ }
+
+ $this->handler->tableAlias = $this->add_table($join);
+ }
+
+ return $this->handler->tableAlias;
+ }
+
+ // Case 2: it's an 'and' or an 'or'.
+ // We do one join per selected value.
+ if ($this->handler->operator != 'not') {
+ // Clone the join for each table:
+ $this->handler->tableAliases = array();
+ foreach ($this->handler->value as $value) {
+ $join = $this->getJoin();
+ if ($this->handler->operator == 'and') {
+ $join->type = 'INNER';
+ }
+ $join->extra = array(
+ array(
+ 'field' => $this->handler->realField,
+ 'value' => $value,
+ 'numeric' => !empty($this->handler->definition['numeric']),
+ ),
+ );
+
+ // The table alias needs to be unique to this value across the
+ // multiple times the filter or argument is called by the view.
+ if (!isset($this->handler->view->many_to_one_aliases[$field][$value])) {
+ if (!isset($this->handler->view->many_to_one_count[$this->handler->table])) {
+ $this->handler->view->many_to_one_count[$this->handler->table] = 0;
+ }
+ $this->handler->view->many_to_one_aliases[$field][$value] = $this->handler->table . '_value_' . ($this->handler->view->many_to_one_count[$this->handler->table]++);
+ }
+ $alias = $this->handler->tableAliases[$value] = $this->add_table($join, $this->handler->view->many_to_one_aliases[$field][$value]);
+
+ // and set table_alias to the first of these.
+ if (empty($this->handler->tableAlias)) {
+ $this->handler->tableAlias = $alias;
+ }
+ }
+ }
+ // Case 3: it's a 'not'.
+ // We just do one join. We'll add a where clause during
+ // the query phase to ensure that $table.$field IS NULL.
+ else {
+ $join = $this->getJoin();
+ $join->type = 'LEFT';
+ $join->extra = array();
+ $join->extra_type = 'OR';
+ foreach ($this->handler->value as $value) {
+ $join->extra[] = array(
+ 'field' => $this->handler->realField,
+ 'value' => $value,
+ 'numeric' => !empty($this->handler->definition['numeric']),
+ );
+ }
+
+ $this->handler->tableAlias = $this->add_table($join);
+ }
+ }
+ return $this->handler->tableAlias;
+ }
+
+ /**
+ * Provides a unique placeholders for handlers.
+ */
+ protected function placeholder() {
+ return $this->handler->query->placeholder($this->handler->options['table'] . '_' . $this->handler->options['field']);
+ }
+
+ function add_filter() {
+ if (empty($this->handler->value)) {
+ return;
+ }
+ $this->handler->ensureMyTable();
+
+ // Shorten some variables:
+ $field = $this->getField();
+ $options = $this->handler->options;
+ $operator = $this->handler->operator;
+ $formula = !empty($this->formula);
+ $value = $this->handler->value;
+ if (empty($options['group'])) {
+ $options['group'] = 0;
+ }
+
+ // add_condition determines whether a single expression is enough(FALSE) or the
+ // conditions should be added via an db_or()/db_and() (TRUE).
+ $add_condition = TRUE;
+ if ($operator == 'not') {
+ $value = NULL;
+ $operator = 'IS NULL';
+ $add_condition = FALSE;
+ }
+ elseif ($operator == 'or' && empty($options['reduce_duplicates'])) {
+ if (count($value) > 1) {
+ $operator = 'IN';
+ }
+ else {
+ $value = is_array($value) ? array_pop($value) : $value;
+ $operator = '=';
+ }
+ $add_condition = FALSE;
+ }
+
+ if (!$add_condition) {
+ if ($formula) {
+ $placeholder = $this->placeholder();
+ if ($operator == 'IN') {
+ $operator = "$operator IN($placeholder)";
+ }
+ else {
+ $operator = "$operator $placeholder";
+ }
+ $placeholders = array(
+ $placeholder => $value,
+ ) + $this->placeholders;
+ $this->handler->query->add_where_expression($options['group'], "$field $operator", $placeholders);
+ }
+ else {
+ $placeholder = $this->placeholder();
+ if (count($this->handler->value) > 1) {
+ $this->query->add_where_expression(0, "$field $operator($placeholder)", array($placeholder => $value));
+ }
+ else {
+ $this->handler->query->add_where_expression(0, "$field $operator $placeholder", array($placeholder => $value));
+ }
+ }
+ }
+
+ if ($add_condition) {
+ $field = $this->handler->realField;
+ $clause = $operator == 'or' ? db_or() : db_and();
+ foreach ($this->handler->tableAliases as $value => $alias) {
+ $clause->condition("$alias.$field", $value);
+ }
+
+ // implode on either AND or OR.
+ $this->handler->query->add_where($options['group'], $clause);
+ }
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Type/JoinManager.php b/core/modules/views/lib/Drupal/views/Plugin/Type/JoinManager.php
new file mode 100644
index 0000000..b8608e8
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/Type/JoinManager.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\Type\JoinManager.
+ */
+
+namespace Drupal\views\Plugin\Type;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Core\Plugin\Discovery\AlterDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Plugin\Discovery\CacheDecorator;
+
+class JoinManager extends PluginManagerBase {
+
+ /**
+ * A list of Drupal core modules.
+ *
+ * @var array
+ */
+ protected $coreModules = array();
+
+ /**
+ * Constructs a JoinManager object.
+ */
+ public function __construct() {
+ // @todo Remove this hack in http://drupal.org/node/1708404.
+ views_init();
+
+ $this->discovery = new CacheDecorator(new AlterDecorator(new AnnotatedClassDiscovery('views', 'join'), 'views_plugins_join'), 'views:join', 'views_info');
+ $this->factory = new DefaultFactory($this);
+
+ $this->coreModules = views_core_modules();
+ }
+
+ /**
+ * Overrides Drupal\Component\Plugin\PluginManagerBase::processDefinition().
+ */
+ public function processDefinition(&$definition, $plugin_id) {
+ parent::processDefinition($definition, $plugin_id);
+
+ $module = isset($definition['module']) ? $definition['module'] : 'views';
+ $module_dir = in_array($module, $this->coreModules) ? 'views' : $module;
+
+ $definition += array(
+ 'module' => $module_dir,
+ );
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Type/PluginManager.php b/core/modules/views/lib/Drupal/views/Plugin/Type/PluginManager.php
new file mode 100644
index 0000000..19df7a8
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/Type/PluginManager.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\Type\PluginManager.
+ */
+
+namespace Drupal\views\Plugin\Type;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Core\Plugin\Discovery\AlterDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Plugin\Discovery\CacheDecorator;
+
+class PluginManager extends PluginManagerBase {
+
+ /**
+ * A list of Drupal core modules.
+ *
+ * @var array
+ */
+ protected $coreModules = array();
+
+ /**
+ * Constructs a PluginManager object.
+ */
+ public function __construct($type) {
+ // @todo Remove this hack in http://drupal.org/node/1708404.
+ views_init();
+
+ $this->discovery = new CacheDecorator(new AlterDecorator(new AnnotatedClassDiscovery('views', $type), 'views_plugins_' . $type), 'views:' . $type, 'views_info');
+ $this->factory = new DefaultFactory($this);
+ $this->coreModules = views_core_modules();
+ $this->defaults += array(
+ 'parent' => 'parent',
+ 'plugin_type' => $type,
+ );
+ }
+
+ /**
+ * Overrides Drupal\Component\Plugin\PluginManagerBase::processDefinition().
+ */
+ public function processDefinition(&$definition, $plugin_id) {
+ parent::processDefinition($definition, $plugin_id);
+
+ $module = isset($definition['module']) ? $definition['module'] : 'views';
+ // If this module is a core module, use views as the module directory.
+ $module_dir = in_array($module, $this->coreModules) ? 'views' : $module;
+ $theme_path = drupal_get_path('module', $module_dir);
+
+ // Setup automatic path/file finding for theme registration.
+ if ($module_dir == 'views') {
+ $theme_path .= '/theme';
+ $theme_file = 'theme.inc';
+ }
+ else {
+ $theme_file = "$module.views.inc";
+ }
+
+ $definition += array(
+ 'module' => $module_dir,
+ 'theme path' => $theme_path,
+ 'theme file' => $theme_file,
+ );
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Type/WizardManager.php b/core/modules/views/lib/Drupal/views/Plugin/Type/WizardManager.php
new file mode 100644
index 0000000..1586e13
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/Type/WizardManager.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\Type\WizardManager.
+ */
+
+namespace Drupal\views\Plugin\Type;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Core\Plugin\Discovery\AlterDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Plugin\Discovery\CacheDecorator;
+
+class WizardManager extends PluginManagerBase {
+
+ /**
+ * A list of Drupal core modules.
+ *
+ * @var array
+ */
+ protected $coreModules = array();
+
+ /**
+ * Constructs a WizardManager object.
+ */
+ public function __construct() {
+ // @todo Remove this hack in http://drupal.org/node/1708404.
+ views_init();
+
+ $this->discovery = new CacheDecorator(new AlterDecorator(new AnnotatedClassDiscovery('views', 'wizard'), 'views_plugins_wizard'), 'views:wizard', 'views_info');
+ $this->factory = new DefaultFactory($this);
+
+ $this->coreModules = views_core_modules();
+ }
+
+ /**
+ * Overrides Drupal\Component\Plugin\PluginManagerBase::processDefinition().
+ */
+ public function processDefinition(&$definition, $plugin_id) {
+ parent::processDefinition($definition, $plugin_id);
+
+ $module = isset($definition['module']) ? $definition['module'] : 'views';
+ $module_dir = in_array($module, $this->coreModules) ? 'views' : $module;
+
+ $definition += array(
+ 'module' => $module_dir,
+ );
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
new file mode 100644
index 0000000..e67614e
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
@@ -0,0 +1,930 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\HandlerBase.
+ */
+
+namespace Drupal\views\Plugin\views;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\views\Plugin\views\PluginBase;
+use Drupal\views\ViewExecutable;
+use Drupal\Core\Database\Database;
+
+abstract class HandlerBase extends PluginBase {
+
+ /**
+ * Where the $query object will reside:
+ *
+ * @var Drupal\views\Plugin\views\query\QueryPluginBase
+ */
+ public $query = NULL;
+
+ /**
+ * The table this handler is attached to.
+ *
+ * @var string
+ */
+ public $table;
+
+ /**
+ * The alias of the table of this handler which is used in the query.
+ *
+ * @var string
+ */
+ public $tableAlias;
+
+ /**
+ * When a table has been moved this property is set.
+ *
+ * @var string
+ */
+ public $actualTable;
+
+ /**
+ * The actual field in the database table, maybe different
+ * on other kind of query plugins/special handlers.
+ *
+ * @var string
+ */
+ public $realField;
+
+ /**
+ * With field you can override the realField if the real field is not set.
+ *
+ * @var string
+ */
+ public $field;
+
+ /**
+ * When a field has been moved this property is set.
+ *
+ * @var string
+ */
+ public $actualField;
+
+ /**
+ * The relationship used for this field.
+ *
+ * @var string
+ */
+ public $relationship = NULL;
+
+ /**
+ * Constructs a Handler object.
+ */
+ public function __construct(array $configuration, $plugin_id, DiscoveryInterface $discovery) {
+ parent::__construct($configuration, $plugin_id, $discovery);
+ $this->is_handler = TRUE;
+ }
+
+ /**
+ * Init the handler with necessary data.
+ *
+ * @param Drupal\views\ViewExecutable $view
+ * The $view object this handler is attached to.
+ * @param array $options
+ * The item from the database; the actual contents of this will vary
+ * based upon the type of handler.
+ */
+ public function init(ViewExecutable $view, &$options) {
+ $this->setOptionDefaults($this->options, $this->defineOptions());
+ $this->view = &$view;
+ $display_id = $this->view->current_display;
+ // Check to see if this handler type is defaulted. Note that
+ // we have to do a lookup because the type is singular but the
+ // option is stored as the plural.
+
+ // If the 'moved to' keyword moved our handler, let's fix that now.
+ if (isset($this->actualTable)) {
+ $options['table'] = $this->actualTable;
+ }
+
+ if (isset($this->actualField)) {
+ $options['field'] = $this->actualField;
+ }
+
+ $types = ViewExecutable::viewsHandlerTypes();
+ $plural = $this->definition['plugin_type'];
+ if (isset($types[$plural]['plural'])) {
+ $plural = $types[$plural]['plural'];
+ }
+ if ($this->view->display_handler->isDefaulted($plural)) {
+ $display_id = 'default';
+ }
+
+ $this->unpackOptions($this->options, $options);
+
+ // This exist on most handlers, but not all. So they are still optional.
+ if (isset($options['table'])) {
+ $this->table = $options['table'];
+ }
+
+ // Allow alliases on both fields and tables.
+ if (isset($this->definition['real table'])) {
+ $this->table = $this->definition['real table'];
+ }
+
+ if (isset($this->definition['real field'])) {
+ $this->realField = $this->definition['real field'];
+ }
+
+ if (isset($this->definition['field'])) {
+ $this->realField = $this->definition['field'];
+ }
+
+ if (isset($options['field'])) {
+ $this->field = $options['field'];
+ if (!isset($this->realField)) {
+ $this->realField = $options['field'];
+ }
+ }
+
+ $this->query = &$view->query;
+ }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['id'] = array('default' => '');
+ $options['table'] = array('default' => '');
+ $options['field'] = array('default' => '');
+ $options['relationship'] = array('default' => 'none');
+ $options['group_type'] = array('default' => 'group');
+ $options['admin_label'] = array('default' => '', 'translatable' => TRUE);
+
+ return $options;
+ }
+
+ /**
+ * Return a string representing this handler's name in the UI.
+ */
+ public function adminLabel($short = FALSE) {
+ if (!empty($this->options['admin_label'])) {
+ $title = check_plain($this->options['admin_label']);
+ return $title;
+ }
+ $title = ($short && isset($this->definition['title short'])) ? $this->definition['title short'] : $this->definition['title'];
+ return t('!group: !title', array('!group' => $this->definition['group'], '!title' => $title));
+ }
+
+ /**
+ * Shortcut to get a handler's raw field value.
+ *
+ * This should be overridden for handlers with formulae or other
+ * non-standard fields. Because this takes an argument, fields
+ * overriding this can just call return parent::getField($formula)
+ */
+ public function getField($field = NULL) {
+ if (!isset($field)) {
+ if (!empty($this->formula)) {
+ $field = $this->get_formula();
+ }
+ else {
+ $field = $this->tableAlias . '.' . $this->realField;
+ }
+ }
+
+ // If grouping, check to see if the aggregation method needs to modify the field.
+ if ($this->view->display_handler->useGroupBy()) {
+ $this->view->initQuery();
+ if ($this->query) {
+ $info = $this->query->get_aggregation_info();
+ if (!empty($info[$this->options['group_type']]['method'])) {
+ $method = $info[$this->options['group_type']]['method'];
+ if (method_exists($this->query, $method)) {
+ return $this->query->$method($this->options['group_type'], $field);
+ }
+ }
+ }
+ }
+
+ return $field;
+ }
+
+ /**
+ * Sanitize the value for output.
+ *
+ * @param $value
+ * The value being rendered.
+ * @param $type
+ * The type of sanitization needed. If not provided, check_plain() is used.
+ *
+ * @return string
+ * Returns the safe value.
+ */
+ protected function sanitizeValue($value, $type = NULL) {
+ switch ($type) {
+ case 'xss':
+ $value = filter_xss($value);
+ break;
+ case 'xss_admin':
+ $value = filter_xss_admin($value);
+ break;
+ case 'url':
+ $value = check_url($value);
+ break;
+ default:
+ $value = check_plain($value);
+ break;
+ }
+ return $value;
+ }
+
+ /**
+ * Transform a string by a certain method.
+ *
+ * @param $string
+ * The input you want to transform.
+ * @param $option
+ * How do you want to transform it, possible values:
+ * - upper: Uppercase the string.
+ * - lower: lowercase the string.
+ * - ucfirst: Make the first char uppercase.
+ * - ucwords: Make each word in the string uppercase.
+ *
+ * @return string
+ * The transformed string.
+ */
+ protected function caseTransform($string, $option) {
+ global $multibyte;
+
+ switch ($option) {
+ default:
+ return $string;
+ case 'upper':
+ return drupal_strtoupper($string);
+ case 'lower':
+ return drupal_strtolower($string);
+ case 'ucfirst':
+ return drupal_strtoupper(drupal_substr($string, 0, 1)) . drupal_substr($string, 1);
+ case 'ucwords':
+ if ($multibyte == UNICODE_MULTIBYTE) {
+ return mb_convert_case($string, MB_CASE_TITLE);
+ }
+ else {
+ return ucwords($string);
+ }
+ }
+ }
+
+ /**
+ * Validate the options form.
+ */
+ public function validateOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Build the options form.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ // Some form elements belong in a fieldset for presentation, but can't
+ // be moved into one because of the form_state['values'] hierarchy. Those
+ // elements can add a #fieldset => 'fieldset_name' property, and they'll
+ // be moved to their fieldset during pre_render.
+ $form['#pre_render'][] = 'views_ui_pre_render_add_fieldset_markup';
+
+ $form['admin_label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Administrative title'),
+ '#description' => t('This title will be displayed on the views edit page instead of the default one. This might be useful if you have the same item twice.'),
+ '#default_value' => $this->options['admin_label'],
+ '#fieldset' => 'more',
+ );
+
+ // This form is long and messy enough that the "Administrative title" option
+ // belongs in a "more options" fieldset at the bottom of the form.
+ $form['more'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('More'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#weight' => 150,
+ );
+ // Allow to alter the default values brought into the form.
+ drupal_alter('views_handler_options', $this->options, $view);
+ }
+
+ /**
+ * Perform any necessary changes to the form values prior to storage.
+ * There is no need for this function to actually store the data.
+ */
+ public function submitOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Provides the handler some groupby.
+ */
+ public function usesGroupBy() {
+ return TRUE;
+ }
+ /**
+ * Provide a form for aggregation settings.
+ */
+ public function buildGroupByForm(&$form, &$form_state) {
+ $view = &$form_state['view'];
+ $display_id = $form_state['display_id'];
+ $types = ViewExecutable::viewsHandlerTypes();
+ $type = $form_state['type'];
+ $id = $form_state['id'];
+
+ $form['#title'] = check_plain($view->display[$display_id]['display_title']) . ': ';
+ $form['#title'] .= t('Configure aggregation settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $this->adminLabel()));
+
+ $form['#section'] = $display_id . '-' . $type . '-' . $id;
+
+ $view->initQuery();
+ $info = $view->query->get_aggregation_info();
+ foreach ($info as $id => $aggregate) {
+ $group_types[$id] = $aggregate['title'];
+ }
+
+ $form['group_type'] = array(
+ '#type' => 'select',
+ '#title' => t('Aggregation type'),
+ '#default_value' => $this->options['group_type'],
+ '#description' => t('Select the aggregation function to use on this field.'),
+ '#options' => $group_types,
+ );
+ }
+
+ /**
+ * Perform any necessary changes to the form values prior to storage.
+ * There is no need for this function to actually store the data.
+ */
+ public function submitGroupByForm(&$form, &$form_state) {
+ $item =& $form_state['handler']->options;
+
+ $item['group_type'] = $form_state['values']['options']['group_type'];
+ }
+
+ /**
+ * If a handler has 'extra options' it will get a little settings widget and
+ * another form called extra_options.
+ */
+ public function hasExtraOptions() { return FALSE; }
+
+ /**
+ * Provide defaults for the handler.
+ */
+ public function defineExtraOptions(&$option) { }
+
+ /**
+ * Provide a form for setting options.
+ */
+ public function buildExtraOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Validate the options form.
+ */
+ public function validateExtraOptionsForm($form, &$form_state) { }
+
+ /**
+ * Perform any necessary changes to the form values prior to storage.
+ * There is no need for this function to actually store the data.
+ */
+ public function submitExtraOptionsForm($form, &$form_state) { }
+
+ /**
+ * Determine if a handler can be exposed.
+ */
+ public function canExpose() { return FALSE; }
+
+ /**
+ * Set new exposed option defaults when exposed setting is flipped
+ * on.
+ */
+ public function defaultExposeOptions() { }
+
+ /**
+ * Get information about the exposed form for the form renderer.
+ */
+ public function exposedInfo() { }
+
+ /**
+ * Render our chunk of the exposed handler form when selecting
+ */
+ public function buildExposedForm(&$form, &$form_state) { }
+
+ /**
+ * Validate the exposed handler form
+ */
+ public function validateExposed(&$form, &$form_state) { }
+
+ /**
+ * Submit the exposed handler form
+ */
+ public function submitExposed(&$form, &$form_state) { }
+
+ /**
+ * Form for exposed handler options.
+ */
+ public function buildExposeForm(&$form, &$form_state) { }
+
+ /**
+ * Validate the options form.
+ */
+ public function validateExposeForm($form, &$form_state) { }
+
+ /**
+ * Perform any necessary changes to the form exposes prior to storage.
+ * There is no need for this function to actually store the data.
+ */
+ public function submitExposeForm($form, &$form_state) { }
+
+ /**
+ * Shortcut to display the expose/hide button.
+ */
+ public function showExposeButton(&$form, &$form_state) { }
+
+ /**
+ * Shortcut to display the exposed options form.
+ */
+ public function showExposeForm(&$form, &$form_state) {
+ if (empty($this->options['exposed'])) {
+ return;
+ }
+
+ $this->buildExposeForm($form, $form_state);
+
+ // When we click the expose button, we add new gadgets to the form but they
+ // have no data in $_POST so their defaults get wiped out. This prevents
+ // these defaults from getting wiped out. This setting will only be TRUE
+ // during a 2nd pass rerender.
+ if (!empty($form_state['force_expose_options'])) {
+ foreach (element_children($form['expose']) as $id) {
+ if (isset($form['expose'][$id]['#default_value']) && !isset($form['expose'][$id]['#value'])) {
+ $form['expose'][$id]['#value'] = $form['expose'][$id]['#default_value'];
+ }
+ }
+ }
+ }
+
+ /**
+ * Check whether current user has access to this handler.
+ *
+ * @return boolean
+ */
+ public function access() {
+ if (isset($this->definition['access callback']) && function_exists($this->definition['access callback'])) {
+ if (isset($this->definition['access arguments']) && is_array($this->definition['access arguments'])) {
+ return call_user_func_array($this->definition['access callback'], $this->definition['access arguments']);
+ }
+ return $this->definition['access callback']();
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Run before the view is built.
+ *
+ * This gives all the handlers some time to set up before any handler has
+ * been fully run.
+ */
+ public function preQuery() { }
+
+ /**
+ * Run after the view is executed, before the result is cached.
+ *
+ * This gives all the handlers some time to modify values. This is primarily
+ * used so that handlers that pull up secondary data can put it in the
+ * $values so that the raw data can be utilized externally.
+ */
+ public function postExecute(&$values) { }
+
+ /**
+ * Provides a unique placeholders for handlers.
+ *
+ * @return string
+ * A placeholder which contains the table and the fieldname.
+ */
+ protected function placeholder() {
+ return $this->query->placeholder($this->table . '_' . $this->field);
+ }
+
+ /**
+ * Called just prior to query(), this lets a handler set up any relationship
+ * it needs.
+ */
+ public function setRelationship() {
+ // Ensure this gets set to something.
+ $this->relationship = NULL;
+
+ // Don't process non-existant relationships.
+ if (empty($this->options['relationship']) || $this->options['relationship'] == 'none') {
+ return;
+ }
+
+ $relationship = $this->options['relationship'];
+
+ // Ignore missing/broken relationships.
+ if (empty($this->view->relationship[$relationship])) {
+ return;
+ }
+
+ // Check to see if the relationship has already processed. If not, then we
+ // cannot process it.
+ if (empty($this->view->relationship[$relationship]->alias)) {
+ return;
+ }
+
+ // Finally!
+ $this->relationship = $this->view->relationship[$relationship]->alias;
+ }
+
+ /**
+ * Ensure the main table for this handler is in the query. This is used
+ * a lot.
+ */
+ public function ensureMyTable() {
+ if (!isset($this->tableAlias)) {
+ $this->tableAlias = $this->query->ensure_table($this->table, $this->relationship);
+ }
+ return $this->tableAlias;
+ }
+
+ /**
+ * Provide text for the administrative summary
+ */
+ public function adminSummary() { }
+
+ /**
+ * Determine if this item is 'exposed', meaning it provides form elements
+ * to let users modify the view.
+ *
+ * @return TRUE/FALSE
+ */
+ public function isExposed() {
+ return !empty($this->options['exposed']);
+ }
+
+ /**
+ * Returns TRUE if the exposed filter works like a grouped filter.
+ */
+ public function isAGroup() { return FALSE; }
+
+ /**
+ * Define if the exposed input has to be submitted multiple times.
+ * This is TRUE when exposed filters grouped are using checkboxes as
+ * widgets.
+ */
+ public function multipleExposedInput() { return FALSE; }
+
+ /**
+ * Take input from exposed handlers and assign to this handler, if necessary.
+ */
+ public function acceptExposedInput($input) { return TRUE; }
+
+ /**
+ * If set to remember exposed input in the session, store it there.
+ */
+ public function storeExposedInput($input, $status) { return TRUE; }
+
+ /**
+ * Get the join object that should be used for this handler.
+ *
+ * This method isn't used a great deal, but it's very handy for easily
+ * getting the join if it is necessary to make some changes to it, such
+ * as adding an 'extra'.
+ */
+ public function getJoin() {
+ // get the join from this table that links back to the base table.
+ // Determine the primary table to seek
+ if (empty($this->query->relationships[$this->relationship])) {
+ $base_table = $this->query->base_table;
+ }
+ else {
+ $base_table = $this->query->relationships[$this->relationship]['base'];
+ }
+
+ $join = $this->getTableJoin($this->table, $base_table);
+ if ($join) {
+ return clone $join;
+ }
+ }
+
+ /**
+ * Validates the handler against the complete View.
+ *
+ * This is called when the complete View is being validated. For validating
+ * the handler options form use validateOptionsForm().
+ *
+ * @see views_handler::validateOptionsForm()
+ *
+ * @return
+ * Empty array if the handler is valid; an array of error strings if it is not.
+ */
+ public function validate() { return array(); }
+
+ /**
+ * Determine if the handler is considered 'broken', meaning it's a
+ * a placeholder used when a handler can't be found.
+ */
+ public function broken() { }
+
+ /**
+ * Creates cross-database SQL date formatting.
+ *
+ * @param string $format
+ * A format string for the result, like 'Y-m-d H:i:s'.
+ *
+ * @return string
+ * An appropriate SQL string for the DB type and field type.
+ */
+ public function getSQLFormat($format) {
+ $db_type = Database::getConnection()->databaseType();
+ $field = $this->getSQLDateField();
+ switch ($db_type) {
+ case 'mysql':
+ $replace = array(
+ 'Y' => '%Y',
+ 'y' => '%y',
+ 'M' => '%b',
+ 'm' => '%m',
+ 'n' => '%c',
+ 'F' => '%M',
+ 'D' => '%a',
+ 'd' => '%d',
+ 'l' => '%W',
+ 'j' => '%e',
+ 'W' => '%v',
+ 'H' => '%H',
+ 'h' => '%h',
+ 'i' => '%i',
+ 's' => '%s',
+ 'A' => '%p',
+ );
+ $format = strtr($format, $replace);
+ return "DATE_FORMAT($field, '$format')";
+ case 'pgsql':
+ $replace = array(
+ 'Y' => 'YYYY',
+ 'y' => 'YY',
+ 'M' => 'Mon',
+ 'm' => 'MM',
+ 'n' => 'MM', // no format for Numeric representation of a month, without leading zeros
+ 'F' => 'Month',
+ 'D' => 'Dy',
+ 'd' => 'DD',
+ 'l' => 'Day',
+ 'j' => 'DD', // no format for Day of the month without leading zeros
+ 'W' => 'WW',
+ 'H' => 'HH24',
+ 'h' => 'HH12',
+ 'i' => 'MI',
+ 's' => 'SS',
+ 'A' => 'AM',
+ );
+ $format = strtr($format, $replace);
+ return "TO_CHAR($field, '$format')";
+ case 'sqlite':
+ $replace = array(
+ 'Y' => '%Y', // 4 digit year number
+ 'y' => '%Y', // no format for 2 digit year number
+ 'M' => '%m', // no format for 3 letter month name
+ 'm' => '%m', // month number with leading zeros
+ 'n' => '%m', // no format for month number without leading zeros
+ 'F' => '%m', // no format for full month name
+ 'D' => '%d', // no format for 3 letter day name
+ 'd' => '%d', // day of month number with leading zeros
+ 'l' => '%d', // no format for full day name
+ 'j' => '%d', // no format for day of month number without leading zeros
+ 'W' => '%W', // ISO week number
+ 'H' => '%H', // 24 hour hour with leading zeros
+ 'h' => '%H', // no format for 12 hour hour with leading zeros
+ 'i' => '%M', // minutes with leading zeros
+ 's' => '%S', // seconds with leading zeros
+ 'A' => '', // no format for AM/PM
+ );
+ $format = strtr($format, $replace);
+ return "strftime('$format', $field, 'unixepoch')";
+ }
+ }
+
+ /**
+ * Creates cross-database SQL dates.
+ *
+ * @return string
+ * An appropriate SQL string for the db type and field type.
+ */
+ public function getSQLDateField() {
+ $field = "$this->tableAlias.$this->realField";
+ $db_type = Database::getConnection()->databaseType();
+ $offset = $this->getTimezone();
+ if (isset($offset) && !is_numeric($offset)) {
+ $dtz = new \DateTimeZone($offset);
+ $dt = new \DateTime('now', $dtz);
+ $offset_seconds = $dtz->getOffset($dt);
+ }
+
+ switch ($db_type) {
+ case 'mysql':
+ $field = "DATE_ADD('19700101', INTERVAL $field SECOND)";
+ if (!empty($offset)) {
+ $field = "($field + INTERVAL $offset_seconds SECOND)";
+ }
+ return $field;
+ case 'pgsql':
+ $field = "TO_TIMESTAMP($field)";
+ if (!empty($offset)) {
+ $field = "($field + INTERVAL '$offset_seconds SECONDS')";
+ }
+ return $field;
+ case 'sqlite':
+ if (!empty($offset)) {
+ $field = "($field + '$offset_seconds')";
+ }
+ return $field;
+ }
+ }
+
+ /**
+ * Figure out what timezone we're in; needed for some date manipulations.
+ */
+ public static function getTimezone() {
+ global $user;
+ if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
+ $timezone = $user->timezone;
+ }
+ else {
+ $timezone = variable_get('date_default_timezone', 0);
+ }
+
+ // set up the database timezone
+ $db_type = Database::getConnection()->databaseType();
+ if (in_array($db_type, array('mysql', 'pgsql'))) {
+ $offset = '+00:00';
+ static $already_set = FALSE;
+ if (!$already_set) {
+ if ($db_type == 'pgsql') {
+ db_query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE");
+ }
+ elseif ($db_type == 'mysql') {
+ db_query("SET @@session.time_zone = '$offset'");
+ }
+
+ $already_set = TRUE;
+ }
+ }
+
+ return $timezone;
+ }
+
+ /**
+ * Fetches a handler to join one table to a primary table from the data cache.
+ *
+ * @param string $table
+ * The table to join from.
+ * @param string $base_table
+ * The table to join to.
+ *
+ * @return Drupal\views\Plugin\views\join\JoinPluginBase
+ */
+ public static function getTableJoin($table, $base_table) {
+ $data = views_fetch_data($table);
+ if (isset($data['table']['join'][$base_table])) {
+ $join_info = $data['table']['join'][$base_table];
+ if (!empty($join_info['join_id'])) {
+ $id = $join_info['join_id'];
+ }
+ else {
+ $id = 'standard';
+ }
+
+ $configuration = $join_info;
+ // Fill in some easy defaults.
+ if (empty($configuration['table'])) {
+ $configuration['table'] = $table;
+ }
+ // If this is empty, it's a direct link.
+ if (empty($configuration['left_table'])) {
+ $configuration['left_table'] = $base_table;
+ }
+
+ if (isset($join_info['arguments'])) {
+ foreach ($join_info['arguments'] as $key => $argument) {
+ $configuration[$key] = $argument;
+ }
+ }
+
+ $join = drupal_container()->get('plugin.manager.views.join')->createInstance($id, $configuration);
+
+ return $join;
+ }
+ }
+
+ /**
+ * Breaks x,y,z and x+y+z into an array. Numeric only.
+ *
+ * @param string $str
+ * The string to parse.
+ * @param Drupal\views\Plugin\views\HandlerBase|null $handler
+ * The handler object to use as a base. If not specified one will
+ * be created.
+ *
+ * @return Drupal\views\Plugin\views\HandlerBase|stdClass $handler
+ * The new handler object.
+ */
+ public static function breakPhrase($str, &$handler = NULL) {
+ if (!$handler) {
+ $handler = new \stdClass();
+ }
+
+ // Set up defaults:
+
+ if (!isset($handler->value)) {
+ $handler->value = array();
+ }
+
+ if (!isset($handler->operator)) {
+ $handler->operator = 'or';
+ }
+
+ if (empty($str)) {
+ return $handler;
+ }
+
+ if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str)) {
+ // The '+' character in a query string may be parsed as ' '.
+ $handler->operator = 'or';
+ $handler->value = preg_split('/[+ ]/', $str);
+ }
+ elseif (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) {
+ $handler->operator = 'and';
+ $handler->value = explode(',', $str);
+ }
+
+ // Keep an 'error' value if invalid strings were given.
+ if (!empty($str) && (empty($handler->value) || !is_array($handler->value))) {
+ $handler->value = array(-1);
+ return $handler;
+ }
+
+ // Doubly ensure that all values are numeric only.
+ foreach ($handler->value as $id => $value) {
+ $handler->value[$id] = intval($value);
+ }
+
+ return $handler;
+ }
+
+ /**
+ * Breaks x,y,z and x+y+z into an array. Works for strings.
+ *
+ * @param string $str
+ * The string to parse.
+ * @param Drupal\views\Plugin\views\HandlerBase|null $handler
+ * The object to use as a base. If not specified one will
+ * be created.
+ *
+ * @return Drupal\views\Plugin\views\HandlerBase|stdClass $handler
+ * The new handler object.
+ */
+ public static function breakPhraseString($str, &$handler = NULL) {
+ if (!$handler) {
+ $handler = new \stdClass();
+ }
+
+ // Set up defaults:
+ if (!isset($handler->value)) {
+ $handler->value = array();
+ }
+
+ if (!isset($handler->operator)) {
+ $handler->operator = 'or';
+ }
+
+ if ($str == '') {
+ return $handler;
+ }
+
+ // Determine if the string has 'or' operators (plus signs) or 'and' operators
+ // (commas) and split the string accordingly. If we have an 'and' operator,
+ // spaces are treated as part of the word being split, but otherwise they are
+ // treated the same as a plus sign.
+ $or_wildcard = '[^\s+,]';
+ $and_wildcard = '[^+,]';
+ if (preg_match("/^({$or_wildcard}+[+ ])+{$or_wildcard}+$/", $str)) {
+ $handler->operator = 'or';
+ $handler->value = preg_split('/[+ ]/', $str);
+ }
+ elseif (preg_match("/^({$and_wildcard}+,)*{$and_wildcard}+$/", $str)) {
+ $handler->operator = 'and';
+ $handler->value = explode(',', $str);
+ }
+
+ // Keep an 'error' value if invalid strings were given.
+ if (!empty($str) && (empty($handler->value) || !is_array($handler->value))) {
+ $handler->value = array(-1);
+ return $handler;
+ }
+
+ // Doubly ensure that all values are strings only.
+ foreach ($handler->value as $id => $value) {
+ $handler->value[$id] = (string) $value;
+ }
+
+ return $handler;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/PluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/PluginBase.php
new file mode 100644
index 0000000..118eb1c
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/PluginBase.php
@@ -0,0 +1,230 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\PluginBase.
+ */
+
+namespace Drupal\views\Plugin\views;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Component\Plugin\PluginBase as ComponentPluginBase;
+
+abstract class PluginBase extends ComponentPluginBase {
+
+ /**
+ * Options for this plugin will be held here.
+ *
+ * @var array
+ */
+ public $options = array();
+
+ /**
+ * The top object of a view.
+ *
+ * @var Drupal\views\ViewExecutable
+ */
+ public $view = NULL;
+
+ /**
+ * The display object this plugin is for.
+ *
+ * For display plugins this is empty.
+ *
+ * @todo find a better description
+ *
+ * @var Drupal\views\Plugin\views\display\DisplayPluginBase
+ */
+ public $displayHandler;
+
+ /**
+ * Plugins's definition
+ *
+ * @var array
+ */
+ public $definition;
+
+ /**
+ * Denotes whether the plugin has an additional options form.
+ *
+ * @var bool
+ */
+ protected $usesOptions = FALSE;
+
+
+ /**
+ * Constructs a Plugin object.
+ */
+ public function __construct(array $configuration, $plugin_id, DiscoveryInterface $discovery) {
+ parent::__construct($configuration, $plugin_id, $discovery);
+
+ $this->definition = $this->discovery->getDefinition($plugin_id) + $configuration;
+ }
+
+ /**
+ * Information about options for all kinds of purposes will be held here.
+ * @code
+ * 'option_name' => array(
+ * - 'default' => default value,
+ * - 'translatable' => (optional) TRUE/FALSE (wrap in t() on export if true),
+ * - 'contains' => (optional) array of items this contains, with its own
+ * defaults, etc. If contains is set, the default will be ignored and
+ * assumed to be array().
+ * - 'bool' => (optional) TRUE/FALSE Is the value a boolean value. This will
+ * change the export format to TRUE/FALSE instead of 1/0.
+ * ),
+ *
+ * @return array
+ * Returns the options of this handler/plugin.
+ */
+ protected function defineOptions() { return array(); }
+
+ protected function setOptionDefaults(&$storage, $options, $level = 0) {
+ foreach ($options as $option => $definition) {
+ if (isset($definition['contains']) && is_array($definition['contains'])) {
+ $storage[$option] = array();
+ $this->setOptionDefaults($storage[$option], $definition['contains'], $level++);
+ }
+ elseif (!empty($definition['translatable']) && !empty($definition['default'])) {
+ $storage[$option] = t($definition['default']);
+ }
+ else {
+ $storage[$option] = isset($definition['default']) ? $definition['default'] : NULL;
+ }
+ }
+ }
+
+ /**
+ * Unpack options over our existing defaults, drilling down into arrays
+ * so that defaults don't get totally blown away.
+ */
+ public function unpackOptions(&$storage, $options, $definition = NULL, $all = TRUE, $check = TRUE) {
+ if ($check && !is_array($options)) {
+ return;
+ }
+
+ if (!isset($definition)) {
+ $definition = $this->defineOptions();
+ }
+
+ foreach ($options as $key => $value) {
+ if (is_array($value)) {
+ // Ignore arrays with no definition.
+ if (!$all && empty($definition[$key])) {
+ continue;
+ }
+
+ if (!isset($storage[$key]) || !is_array($storage[$key])) {
+ $storage[$key] = array();
+ }
+
+ // If we're just unpacking our known options, and we're dropping an
+ // unknown array (as might happen for a dependent plugin fields) go
+ // ahead and drop that in.
+ if (!$all && isset($definition[$key]) && !isset($definition[$key]['contains'])) {
+ $storage[$key] = $value;
+ continue;
+ }
+
+ $this->unpackOptions($storage[$key], $value, isset($definition[$key]['contains']) ? $definition[$key]['contains'] : array(), $all, FALSE);
+ }
+ else if ($all || !empty($definition[$key])) {
+ $storage[$key] = $value;
+ }
+ }
+ }
+
+ /**
+ * Clears a plugin.
+ */
+ public function destroy() {
+ unset($this->view, $this->display, $this->query);
+ }
+
+ /**
+ * Init will be called after construct, when the plugin is attached to a
+ * view and a display.
+ */
+
+ /**
+ * Provide a form to edit options for this plugin.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ // Some form elements belong in a fieldset for presentation, but can't
+ // be moved into one because of the form_state['values'] hierarchy. Those
+ // elements can add a #fieldset => 'fieldset_name' property, and they'll
+ // be moved to their fieldset during pre_render.
+ $form['#pre_render'][] = 'views_ui_pre_render_add_fieldset_markup';
+ }
+
+ /**
+ * Validate the options form.
+ */
+ public function validateOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Handle any special handling on the validate form.
+ */
+ public function submitOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Add anything to the query that we might need to.
+ */
+ public function query() { }
+
+ /**
+ * Provide a full list of possible theme templates used by this style.
+ */
+ public function themeFunctions() {
+ return views_theme_functions($this->definition['theme'], $this->view, $this->view->display_handler->display);
+ }
+
+ /**
+ * Provide a list of additional theme functions for the theme information page
+ */
+ public function additionalThemeFunctions() {
+ $funcs = array();
+ if (!empty($this->definition['additional themes'])) {
+ foreach ($this->definition['additional themes'] as $theme => $type) {
+ $funcs[] = views_theme_functions($theme, $this->view, $this->view->display_handler->display);
+ }
+ }
+ return $funcs;
+ }
+
+ /**
+ * Validate that the plugin is correct and can be saved.
+ *
+ * @return
+ * An array of error strings to tell the user what is wrong with this
+ * plugin.
+ */
+ public function validate() { return array(); }
+
+ /**
+ * Returns the summary of the settings in the display.
+ */
+ public function summaryTitle() {
+ return t('Settings');
+ }
+
+ /**
+ * Return the human readable name of the display.
+ *
+ * This appears on the ui beside each plugin and beside the settings link.
+ */
+ public function pluginTitle() {
+ if (isset($this->definition['short_title'])) {
+ return check_plain($this->definition['short_title']);
+ }
+ return check_plain($this->definition['title']);
+ }
+
+ /**
+ * Returns the usesOptions property.
+ */
+ public function usesOptions() {
+ return $this->usesOptions;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/PluginInterface.php b/core/modules/views/lib/Drupal/views/Plugin/views/PluginInterface.php
new file mode 100644
index 0000000..5b18791
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/PluginInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\PluginInterface.
+ */
+
+namespace Drupal\views\Plugin\views;
+
+use Drupal\Component\Plugin\PluginInspectionInterface;
+
+interface PluginInterface extends PluginInspectionInterface {
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/access/AccessPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/access/AccessPluginBase.php
new file mode 100644
index 0000000..81ad219
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/access/AccessPluginBase.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\access\AccessPluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\access;
+
+use Drupal\views\Plugin\views\PluginBase;
+use Drupal\views\ViewExecutable;
+
+/**
+ * @defgroup views_access_plugins Views access plugins
+ * @{
+ * The base plugin to handle access to a view.
+ *
+ * Therefore it primarily has to implement the access and the get_access_callback
+ * method.
+ */
+
+/**
+ * The base plugin to handle access control.
+ */
+abstract class AccessPluginBase extends PluginBase {
+
+ /**
+ * Initialize the plugin.
+ *
+ * @param $view
+ * The view object.
+ * @param $display
+ * The display handler.
+ */
+ public function init(ViewExecutable $view, &$display, $options = NULL) {
+ $this->setOptionDefaults($this->options, $this->defineOptions());
+ $this->view = &$view;
+ $this->displayHandler = &$display;
+
+ $this->unpackOptions($this->options, $options);
+ }
+
+ /**
+ * Retrieve the options when this is a new access
+ * control plugin
+ */
+ protected function defineOptions() { return array(); }
+
+ /**
+ * Provide the default form for setting options.
+ */
+ public function buildOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Provide the default form form for validating options
+ */
+ public function validateOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Provide the default form form for submitting options
+ */
+ public function submitOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Return a string to display as the clickable title for the
+ * access control.
+ */
+ public function summaryTitle() {
+ return t('Unknown');
+ }
+
+ /**
+ * Determine if the current user has access or not.
+ *
+ * @param Drupal\user\User $account
+ * The user who wants to access this view.
+ *
+ * @return TRUE
+ * Returns whether the user has access to the view.
+ */
+ abstract public function access($account);
+
+ /**
+ * Determine the access callback and arguments.
+ *
+ * This information will be embedded in the menu in order to reduce
+ * performance hits during menu item access testing, which happens
+ * a lot.
+ *
+ * @return array
+ * The first item of the array should be the function to call,and the
+ * second item should be an array of arguments. The first item may also be
+ * TRUE (bool only) which will indicate no access control.
+ */
+ abstract function get_access_callback();
+
+}
+
+/**
+ * @}
+ */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/access/None.php b/core/modules/views/lib/Drupal/views/Plugin/views/access/None.php
new file mode 100644
index 0000000..923598c
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/access/None.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\access\None.
+ */
+
+namespace Drupal\views\Plugin\views\access;
+
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Access plugin that provides no access control at all.
+ *
+ * @ingroup views_access_plugins
+ *
+ * @Plugin(
+ * id = "none",
+ * title = @Translation("None"),
+ * help = @Translation("Will be available to all users.")
+ * )
+ */
+class None extends AccessPluginBase {
+
+ public function summaryTitle() {
+ return t('Unrestricted');
+ }
+
+ /**
+ * Implements Drupal\views\Plugin\views\access\AccessPluginBase::access().
+ */
+ public function access($account) {
+ // No access control.
+ return TRUE;
+ }
+
+ /**
+ * Implements Drupal\views\Plugin\views\access\AccessPluginBase::get_access_callback().
+ */
+ public function get_access_callback() {
+ // No access control.
+ return TRUE;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/AreaPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/AreaPluginBase.php
new file mode 100644
index 0000000..073f7d8
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/AreaPluginBase.php
@@ -0,0 +1,117 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\area\AreaPluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\area;
+
+use Drupal\views\ViewExecutable;
+use Drupal\views\Plugin\views\PluginBase;
+use Drupal\views\Plugin\views\HandlerBase;
+
+/**
+ * @defgroup views_area_handlers Views area handlers
+ * @{
+ * Handlers to tell Views what can display in header, footer
+ * and empty text in a view.
+ */
+
+/**
+ * Base class for area handlers.
+ *
+ * @ingroup views_area_handlers
+ */
+abstract class AreaPluginBase extends HandlerBase {
+
+ /**
+ * Overrides Drupal\views\Plugin\views\HandlerBase::init().
+ *
+ * Make sure that no result area handlers are set to be shown when the result
+ * is empty.
+ */
+ public function init(ViewExecutable $view, &$options) {
+ $this->setOptionDefaults($this->options, $this->defineOptions());
+ parent::init($view, $options);
+
+ if (isset($this->handler_type) && ($this->handler_type == 'empty')) {
+ $this->options['empty'] = TRUE;
+ }
+ }
+
+ /**
+ * Get this area's label.
+ */
+ public function label() {
+ if (!isset($this->options['label'])) {
+ return $this->adminLabel();
+ }
+ return $this->options['label'];
+ }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $this->definition['field'] = !empty($this->definition['field']) ? $this->definition['field'] : '';
+ $label = !empty($this->definition['label']) ? $this->definition['label'] : $this->definition['field'];
+ $options['label'] = array('default' => $label, 'translatable' => TRUE);
+ $options['empty'] = array('default' => FALSE, 'bool' => TRUE);
+
+ return $options;
+ }
+
+ /**
+ * Provide extra data to the administration form
+ */
+ public function adminSummary() {
+ return $this->label();
+ }
+
+ /**
+ * Default options form that provides the label widget that all fields
+ * should have.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ $form['label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Label'),
+ '#default_value' => isset($this->options['label']) ? $this->options['label'] : '',
+ '#description' => t('The label for this area that will be displayed only administratively.'),
+ );
+
+
+ if ($form_state['type'] != 'empty') {
+ $form['empty'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Display even if view has no result'),
+ '#default_value' => isset($this->options['empty']) ? $this->options['empty'] : 0,
+ );
+ }
+ }
+
+ /**
+ * Don't run a query
+ */
+ public function query() { }
+
+ /**
+ * Render the area
+ */
+ function render($empty = FALSE) {
+ return '';
+ }
+
+ /**
+ * Area handlers shouldn't have groupby.
+ */
+ public function usesGroupBy() {
+ return FALSE;
+ }
+
+}
+
+/**
+ * @}
+ */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/Broken.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/Broken.php
new file mode 100644
index 0000000..a7d00c9
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/Broken.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\area\Broken
+ */
+
+namespace Drupal\views\Plugin\views\area;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * A special handler to take the place of missing or broken handlers.
+ *
+ * @ingroup views_area_handlers
+ *
+ * @Plugin(
+ * id = "broken"
+ * )
+ */
+class Broken extends AreaPluginBase {
+
+ public function adminLabel($short = FALSE) {
+ return t('Broken/missing handler');
+ }
+
+ public function defineOptions() { return array(); }
+ public function ensureMyTable() { /* No table to ensure! */ }
+ public function query($group_by = FALSE) { /* No query to run */ }
+ function render($empty = FALSE) { return ''; }
+ public function buildOptionsForm(&$form, &$form_state) {
+ $form['markup'] = array(
+ '#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
+ );
+ }
+
+ /**
+ * Determine if the handler is considered 'broken'
+ */
+ public function broken() {
+ return TRUE;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/Result.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/Result.php
new file mode 100644
index 0000000..11a5f94
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/Result.php
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\area\Result.
+ */
+
+namespace Drupal\views\Plugin\views\area;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Views area handler to display some configurable result summary.
+ *
+ * @ingroup views_area_handlers
+ *
+ * @Plugin(
+ * id = "result"
+ * )
+ */
+class Result extends AreaPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['content'] = array(
+ 'default' => 'Displaying @start - @end of @total',
+ 'translatable' => TRUE,
+ );
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ $variables = array(
+ 'items' => array(
+ '@start -- the initial record number in the set',
+ '@end -- the last record number in the set',
+ '@total -- the total records in the set',
+ '@name -- the human-readable name of the view',
+ '@per_page -- the number of items per page',
+ '@current_page -- the current page number',
+ '@current_record_count -- the current page record count',
+ '@page_count -- the total page count',
+ ),
+ );
+ $list = theme('item_list', $variables);
+ $form['content'] = array(
+ '#title' => t('Display'),
+ '#type' => 'textarea',
+ '#rows' => 3,
+ '#default_value' => $this->options['content'],
+ '#description' => t('You may use HTML code in this field. The following tokens are supported:') . $list,
+ );
+ }
+
+
+ /**
+ * Find out the information to render.
+ */
+ function render($empty = FALSE) {
+ // Must have options and does not work on summaries.
+ if (!isset($this->options['content']) || $this->view->plugin_name == 'default_summary') {
+ return;
+ }
+ $output = '';
+ $format = $this->options['content'];
+ // Calculate the page totals.
+ $current_page = (int) $this->view->getCurrentPage() + 1;
+ $per_page = (int) $this->view->getItemsPerPage();
+ $count = count($this->view->result);
+ // @TODO: Maybe use a possible is views empty functionality.
+ // Not every view has total_rows set, use view->result instead.
+ $total = isset($this->view->total_rows) ? $this->view->total_rows : count($this->view->result);
+ $name = check_plain($this->view->storage->getHumanName());
+ if ($per_page === 0) {
+ $page_count = 1;
+ $start = 1;
+ $end = $total;
+ }
+ else {
+ $page_count = (int) ceil($total / $per_page);
+ $total_count = $current_page * $per_page;
+ if ($total_count > $total) {
+ $total_count = $total;
+ }
+ $start = ($current_page - 1) * $per_page + 1;
+ $end = $total_count;
+ }
+ $current_record_count = ($end - $start) + 1;
+ // Get the search information.
+ $items = array('start', 'end', 'total', 'name', 'per_page', 'current_page', 'current_record_count', 'page_count');
+ $replacements = array();
+ foreach ($items as $item) {
+ $replacements["@$item"] = ${$item};
+ }
+ // Send the output.
+ if (!empty($total)) {
+ $output .= filter_xss_admin(str_replace(array_keys($replacements), array_values($replacements), $format));
+ }
+ return $output;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/Text.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/Text.php
new file mode 100644
index 0000000..b696414
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/Text.php
@@ -0,0 +1,119 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\area\Text.
+ */
+
+namespace Drupal\views\Plugin\views\area;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Views area text handler.
+ *
+ * @ingroup views_area_handlers
+ *
+ * @Plugin(
+ * id = "text"
+ * )
+ */
+class Text extends AreaPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['content'] = array('default' => '', 'translatable' => TRUE, 'format_key' => 'format');
+ $options['format'] = array('default' => NULL);
+ $options['tokenize'] = array('default' => FALSE, 'bool' => TRUE);
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $form['content'] = array(
+ '#type' => 'text_format',
+ '#default_value' => $this->options['content'],
+ '#rows' => 6,
+ '#format' => isset($this->options['format']) ? $this->options['format'] : filter_default_format(),
+ '#wysiwyg' => FALSE,
+ );
+
+
+ $form['tokenize'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Use replacement tokens from the first row'),
+ '#default_value' => $this->options['tokenize'],
+ );
+
+ // Get a list of the available fields and arguments for token replacement.
+ $options = array();
+ foreach ($this->view->display_handler->getHandlers('field') as $field => $handler) {
+ $options[t('Fields')]["[$field]"] = $handler->adminLabel();
+ }
+
+ $count = 0; // This lets us prepare the key as we want it printed.
+ foreach ($this->view->display_handler->getHandlers('argument') as $arg => $handler) {
+ $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->adminLabel()));
+ $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->adminLabel()));
+ }
+
+ if (!empty($options)) {
+ $output = '<p>' . t('The following tokens are available. If you would like to have the characters \'[\' and \']\' please use the html entity codes \'%5B\' or \'%5D\' or they will get replaced with empty space.' . '</p>');
+ foreach (array_keys($options) as $type) {
+ if (!empty($options[$type])) {
+ $items = array();
+ foreach ($options[$type] as $key => $value) {
+ $items[] = $key . ' == ' . $value;
+ }
+ $output .= theme('item_list',
+ array(
+ 'items' => $items,
+ 'type' => $type
+ ));
+ }
+ }
+
+ $form['token_help'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Replacement patterns'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#value' => $output,
+ '#id' => 'edit-options-token-help',
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[tokenize]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+ }
+ }
+
+ public function submitOptionsForm(&$form, &$form_state) {
+ $form_state['values']['options']['format'] = $form_state['values']['options']['content']['format'];
+ $form_state['values']['options']['content'] = $form_state['values']['options']['content']['value'];
+ parent::submitOptionsForm($form, $form_state);
+ }
+
+ function render($empty = FALSE) {
+ $format = isset($this->options['format']) ? $this->options['format'] : filter_default_format();
+ if (!$empty || !empty($this->options['empty'])) {
+ return $this->render_textarea($this->options['content'], $format);
+ }
+ return '';
+ }
+
+ /**
+ * Render a text area, using the proper format.
+ */
+ function render_textarea($value, $format) {
+ if ($value) {
+ if ($this->options['tokenize']) {
+ $value = $this->view->style_plugin->tokenize_value($value, 0);
+ }
+ return check_markup($value, $format, '', FALSE);
+ }
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/TextCustom.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/TextCustom.php
new file mode 100644
index 0000000..9e7c172
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/TextCustom.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\area\TextCustom.
+ */
+
+namespace Drupal\views\Plugin\views\area;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Views area text handler.
+ *
+ * @ingroup views_area_handlers
+ *
+ * @Plugin(
+ * id = "text_custom"
+ * )
+ */
+class TextCustom extends Text {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ unset($options['format']);
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ // Alter the form element, to be a regular text area.
+ $form['content']['#type'] = 'textarea';
+ unset($form['content']['#format']);
+ unset($form['content']['#wysiwyg']);
+ }
+
+ // Empty, so we don't inherit submitOptionsForm from the parent.
+ public function submitOptionsForm(&$form, &$form_state) {
+ }
+
+ function render($empty = FALSE) {
+ if (!$empty || !empty($this->options['empty'])) {
+ return $this->render_textarea_custom($this->options['content']);
+ }
+
+ return '';
+ }
+
+ /**
+ * Render a text area with filter_xss_admin.
+ */
+ function render_textarea_custom($value) {
+ if ($value) {
+ if ($this->options['tokenize']) {
+ $value = $this->view->style_plugin->tokenize_value($value, 0);
+ }
+ return $this->sanitizeValue($value, 'xss_admin');
+ }
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/Title.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/Title.php
new file mode 100644
index 0000000..e9185a0
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/Title.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\area\Title.
+ */
+
+namespace Drupal\views\Plugin\views\area;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Views area title override handler.
+ *
+ * @ingroup views_area_handlers
+ *
+ * @Plugin(
+ * id = "title"
+ * )
+ */
+class Title extends AreaPluginBase {
+
+ /**
+ * Overrides Drupal\views\Plugin\views\AreaPluginBase::defineOptions().
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['title'] = array('default' => '', 'translatable' => TRUE);
+
+ return $options;
+ }
+
+ /**
+ * Overrides Drupal\views\Plugin\views\AreaPluginBase::buildOptionsForm().
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Overridden title'),
+ '#default_value' => $this->options['title'],
+ '#description' => t('Override the title of this view when it is empty.'),
+ );
+
+ }
+
+ /**
+ * Overrides Drupal\views\Plugin\views\AreaPluginBase::render().
+ */
+ function render($empty = FALSE) {
+ if (!empty($this->options['title'])) {
+ $this->view->setTitle(filter_xss_admin($this->options['title']), PASS_THROUGH);
+ }
+
+ return '';
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/View.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/View.php
new file mode 100644
index 0000000..018996c
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/View.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\area\View.
+ */
+
+namespace Drupal\views\Plugin\views\area;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Views area handlers. Insert a view inside of an area.
+ *
+ * @ingroup views_area_handlers
+ *
+ * @Plugin(
+ * id = "view"
+ * )
+ */
+class View extends AreaPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['view_to_insert'] = array('default' => '');
+ $options['inherit_arguments'] = array('default' => FALSE, 'bool' => TRUE);
+ return $options;
+ }
+
+ /**
+ * Default options form that provides the label widget that all fields
+ * should have.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $view_display = $this->view->storage->name . ':' . $this->view->current_display;
+
+ $options = array('' => t('-Select-'));
+ $options += views_get_views_as_options(FALSE, 'all', $view_display, FALSE, TRUE);
+ $form['view_to_insert'] = array(
+ '#type' => 'select',
+ '#title' => t('View to insert'),
+ '#default_value' => $this->options['view_to_insert'],
+ '#description' => t('The view to insert into this area.'),
+ '#options' => $options,
+ );
+
+ $form['inherit_arguments'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Inherit contextual filters'),
+ '#default_value' => $this->options['inherit_arguments'],
+ '#description' => t('If checked, this view will receive the same contextual filters as its parent.'),
+ );
+ }
+
+ /**
+ * Render the area
+ */
+ function render($empty = FALSE) {
+ if (!empty($this->options['view_to_insert'])) {
+ list($view_name, $display_id) = explode(':', $this->options['view_to_insert']);
+
+ $view = views_get_view($view_name);
+ if (empty($view) || !$view->access($display_id)) {
+ return;
+ }
+ $view->setDisplay($display_id);
+
+ // Avoid recursion
+ $view->parent_views += $this->view->parent_views;
+ $view->parent_views[] = "$view_name:$display_id";
+
+ // Check if the view is part of the parent views of this view
+ $search = "$view_name:$display_id";
+ if (in_array($search, $this->view->parent_views)) {
+ drupal_set_message(t("Recursion detected in view @view display @display.", array('@view' => $view_name, '@display' => $display_id)), 'error');
+ }
+ else {
+ if (!empty($this->options['inherit_arguments']) && !empty($this->view->args)) {
+ return $view->preview($display_id, $this->view->args);
+ }
+ else {
+ return $view->preview($display_id);
+ }
+ }
+ }
+ return '';
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/ArgumentPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/ArgumentPluginBase.php
new file mode 100644
index 0000000..ef30140
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/ArgumentPluginBase.php
@@ -0,0 +1,1121 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\ArgumentPluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\views\Plugin\views\PluginBase;
+use Drupal\views\ViewExecutable;
+use Drupal\views\Plugin\views\HandlerBase;
+
+/**
+ * @defgroup views_argument_handlers Views argument handlers
+ * Handlers to tell Views how to contextually filter queries.
+ * @{
+ */
+
+/**
+ * Base class for arguments.
+ *
+ * The basic argument works for very simple arguments such as nid and uid
+ *
+ * Definition terms for this handler:
+ * - name field: The field to use for the name to use in the summary, which is
+ * the displayed output. For example, for the node: nid argument,
+ * the argument itself is the nid, but node.title is displayed.
+ * - name table: The table to use for the name, should it not be in the same
+ * table as the argument.
+ * - empty field name: For arguments that can have no value, such as taxonomy
+ * which can have "no term", this is the string which
+ * will be displayed for this lack of value. Be sure to use
+ * t().
+ * - validate type: A little used string to allow an argument to restrict
+ * which validator is available to just one. Use the
+ * validator ID. This probably should not be used at all,
+ * and may disappear or change.
+ * - numeric: If set to TRUE this field is numeric and will use %d instead of
+ * %s in queries.
+ *
+ * @ingroup views_argument_handlers
+ */
+abstract class ArgumentPluginBase extends HandlerBase {
+
+ var $validator = NULL;
+ var $argument = NULL;
+ var $value = NULL;
+
+ /**
+ * The table to use for the name, should it not be in the same table as the argument.
+ * @var string
+ */
+ var $name_table;
+
+ /**
+ * The field to use for the name to use in the summary, which is
+ * the displayed output. For example, for the node: nid argument,
+ * the argument itself is the nid, but node.title is displayed.
+ * @var string
+ */
+ var $name_field;
+
+ /**
+ * Overrides Drupal\views\Plugin\views\HandlerBase:init().
+ */
+ public function init(ViewExecutable $view, &$options) {
+ parent::init($view, $options);
+
+ if (!empty($this->definition['name field'])) {
+ $this->name_field = $this->definition['name field'];
+ }
+ if (!empty($this->definition['name table'])) {
+ $this->name_table = $this->definition['name table'];
+ }
+ }
+
+ /**
+ * Give an argument the opportunity to modify the breadcrumb, if it wants.
+ * This only gets called on displays where a breadcrumb is actually used.
+ *
+ * The breadcrumb will be in the form of an array, with the keys being
+ * the path and the value being the already sanitized title of the path.
+ */
+ function set_breadcrumb(&$breadcrumb) { }
+
+ /**
+ * Determine if the argument can generate a breadcrumb
+ *
+ * @return TRUE/FALSE
+ */
+ function uses_breadcrumb() {
+ $info = $this->default_actions($this->options['default_action']);
+ return !empty($info['breadcrumb']);
+ }
+
+ function is_exception($arg = NULL) {
+ if (!isset($arg)) {
+ $arg = isset($this->argument) ? $this->argument : NULL;
+ }
+ return !empty($this->options['exception']['value']) && $this->options['exception']['value'] === $arg;
+ }
+
+ function exception_title() {
+ // If title overriding is off for the exception, return the normal title.
+ if (empty($this->options['exception']['title_enable'])) {
+ return $this->get_title();
+ }
+ return $this->options['exception']['title'];
+ }
+
+ /**
+ * Determine if the argument needs a style plugin.
+ *
+ * @return TRUE/FALSE
+ */
+ public function needsStylePlugin() {
+ $info = $this->default_actions($this->options['default_action']);
+ $validate_info = $this->default_actions($this->options['validate']['fail']);
+ return !empty($info['style plugin']) || !empty($validate_info['style plugin']);
+ }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['default_action'] = array('default' => 'ignore');
+ $options['exception'] = array(
+ 'contains' => array(
+ 'value' => array('default' => 'all'),
+ 'title_enable' => array('default' => FALSE, 'bool' => TRUE),
+ 'title' => array('default' => 'All', 'translatable' => TRUE),
+ ),
+ );
+ $options['title_enable'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['title'] = array('default' => '', 'translatable' => TRUE);
+ $options['breadcrumb_enable'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['breadcrumb'] = array('default' => '', 'translatable' => TRUE);
+ $options['default_argument_type'] = array('default' => 'fixed');
+ $options['default_argument_options'] = array('default' => array());
+ $options['default_argument_skip_url'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['summary_options'] = array('default' => array());
+ $options['summary'] = array(
+ 'contains' => array(
+ 'sort_order' => array('default' => 'asc'),
+ 'number_of_records' => array('default' => 0),
+ 'format' => array('default' => 'default_summary'),
+ ),
+ );
+ $options['specify_validation'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['validate'] = array(
+ 'contains' => array(
+ 'type' => array('default' => 'none'),
+ 'fail' => array('default' => 'not found'),
+ ),
+ );
+ $options['validate_options'] = array('default' => array());
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $argument_text = $this->view->display_handler->getArgumentText();
+
+ $form['#pre_render'][] = 'views_ui_pre_render_move_argument_options';
+
+ $form['description'] = array(
+ '#markup' => $argument_text['description'],
+ '#theme_wrappers' => array('container'),
+ '#attributes' => array('class' => array('description')),
+ );
+
+ $form['no_argument'] = array(
+ '#type' => 'fieldset',
+ '#title' => $argument_text['filter value not present'],
+ );
+ // Everything in the fieldset is floated, so the last element needs to
+ // clear those floats.
+ $form['no_argument']['clearfix'] = array(
+ '#weight' => 1000,
+ '#markup' => '<div class="clearfix"></div>',
+ );
+ $form['default_action'] = array(
+ '#type' => 'radios',
+ '#process' => array(array($this, 'processContainerRadios')),
+ '#default_value' => $this->options['default_action'],
+ '#fieldset' => 'no_argument',
+ );
+
+ $form['exception'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Exceptions'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#fieldset' => 'no_argument',
+ );
+ $form['exception']['value'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Exception value'),
+ '#size' => 20,
+ '#default_value' => $this->options['exception']['value'],
+ '#description' => t('If this value is received, the filter will be ignored; i.e, "all values"'),
+ );
+ $form['exception']['title_enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Override title'),
+ '#default_value' => $this->options['exception']['title_enable'],
+ );
+ $form['exception']['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Override title'),
+ '#title_display' => 'invisible',
+ '#size' => 20,
+ '#default_value' => $this->options['exception']['title'],
+ '#description' => t('Override the view and other argument titles. Use "%1" for the first argument, "%2" for the second, etc.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[exception][title_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+
+ $options = array();
+ $defaults = $this->default_actions();
+ $validate_options = array();
+ foreach ($defaults as $id => $info) {
+ $options[$id] = $info['title'];
+ if (empty($info['default only'])) {
+ $validate_options[$id] = $info['title'];
+ }
+ if (!empty($info['form method'])) {
+ $this->{$info['form method']}($form, $form_state);
+ }
+ }
+ $form['default_action']['#options'] = $options;
+
+ $form['argument_present'] = array(
+ '#type' => 'fieldset',
+ '#title' => $argument_text['filter value present'],
+ );
+ $form['title_enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Override title'),
+ '#default_value' => $this->options['title_enable'],
+ '#fieldset' => 'argument_present',
+ );
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Provide title'),
+ '#title_display' => 'invisible',
+ '#default_value' => $this->options['title'],
+ '#description' => t('Override the view and other argument titles. Use "%1" for the first argument, "%2" for the second, etc.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[title_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#fieldset' => 'argument_present',
+ );
+
+ $form['breadcrumb_enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Override breadcrumb'),
+ '#default_value' => $this->options['breadcrumb_enable'],
+ '#fieldset' => 'argument_present',
+ );
+ $form['breadcrumb'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Provide breadcrumb'),
+ '#title_display' => 'invisible',
+ '#default_value' => $this->options['breadcrumb'],
+ '#description' => t('Enter a breadcrumb name you would like to use. See "Title" for percent substitutions.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[breadcrumb_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#fieldset' => 'argument_present',
+ );
+
+ $form['specify_validation'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Specify validation criteria'),
+ '#default_value' => $this->options['specify_validation'],
+ '#fieldset' => 'argument_present',
+ );
+
+ $form['validate'] = array(
+ '#type' => 'container',
+ '#fieldset' => 'argument_present',
+ );
+ $form['validate']['type'] = array(
+ '#type' => 'select',
+ '#title' => t('Validator'),
+ '#default_value' => $this->options['validate']['type'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[specify_validation]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+
+ $plugins = views_get_plugin_definitions('argument_validator');
+ foreach ($plugins as $id => $info) {
+ if (!empty($info['no_ui'])) {
+ continue;
+ }
+
+ $valid = TRUE;
+ if (!empty($info['type'])) {
+ $valid = FALSE;
+ if (empty($this->definition['validate type'])) {
+ continue;
+ }
+ foreach ((array) $info['type'] as $type) {
+ if ($type == $this->definition['validate type']) {
+ $valid = TRUE;
+ break;
+ }
+ }
+ }
+
+ // If we decide this validator is ok, add it to the list.
+ if ($valid) {
+ $plugin = $this->get_plugin('argument_validator', $id);
+ if ($plugin) {
+ if ($plugin->access() || $this->options['validate']['type'] == $id) {
+ $form['validate']['options'][$id] = array(
+ '#prefix' => '<div id="edit-options-validate-options-' . $id . '-wrapper">',
+ '#suffix' => '</div>',
+ '#type' => 'item',
+ // Even if the plugin has no options add the key to the form_state.
+ '#input' => TRUE, // trick it into checking input to make #process run
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[specify_validation]"]' => array('checked' => TRUE),
+ ':input[name="options[validate][type]"]' => array('value' => $id),
+ ),
+ ),
+ '#id' => 'edit-options-validate-options-' . $id,
+ );
+ $plugin->buildOptionsForm($form['validate']['options'][$id], $form_state);
+ $validate_types[$id] = $info['title'];
+ }
+ }
+ }
+ }
+
+ asort($validate_types);
+ $form['validate']['type']['#options'] = $validate_types;
+
+ $form['validate']['fail'] = array(
+ '#type' => 'select',
+ '#title' => t('Action to take if filter value does not validate'),
+ '#default_value' => $this->options['validate']['fail'],
+ '#options' => $validate_options,
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[specify_validation]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#fieldset' => 'argument_present',
+ );
+ }
+
+ public function validateOptionsForm(&$form, &$form_state) {
+ if (empty($form_state['values']['options'])) {
+ return;
+ }
+
+ // Let the plugins do validation.
+ $default_id = $form_state['values']['options']['default_argument_type'];
+ $plugin = $this->get_plugin('argument_default', $default_id);
+ if ($plugin) {
+ $plugin->validateOptionsForm($form['argument_default'][$default_id], $form_state, $form_state['values']['options']['argument_default'][$default_id]);
+ }
+
+ // summary plugin
+ $summary_id = $form_state['values']['options']['summary']['format'];
+ $plugin = $this->get_plugin('style', $summary_id);
+ if ($plugin) {
+ $plugin->validateOptionsForm($form['summary']['options'][$summary_id], $form_state, $form_state['values']['options']['summary']['options'][$summary_id]);
+ }
+
+ $validate_id = $form_state['values']['options']['validate']['type'];
+ $plugin = $this->get_plugin('argument_validator', $validate_id);
+ if ($plugin) {
+ $plugin->validateOptionsForm($form['validate']['options'][$default_id], $form_state, $form_state['values']['options']['validate']['options'][$validate_id]);
+ }
+
+ }
+
+ public function submitOptionsForm(&$form, &$form_state) {
+ if (empty($form_state['values']['options'])) {
+ return;
+ }
+
+ // Let the plugins make submit modifications if necessary.
+ $default_id = $form_state['values']['options']['default_argument_type'];
+ $plugin = $this->get_plugin('argument_default', $default_id);
+ if ($plugin) {
+ $options = &$form_state['values']['options']['argument_default'][$default_id];
+ $plugin->submitOptionsForm($form['argument_default'][$default_id], $form_state, $options);
+ // Copy the now submitted options to their final resting place so they get saved.
+ $form_state['values']['options']['default_argument_options'] = $options;
+ }
+
+ // summary plugin
+ $summary_id = $form_state['values']['options']['summary']['format'];
+ $plugin = $this->get_plugin('style', $summary_id);
+ if ($plugin) {
+ $options = &$form_state['values']['options']['summary']['options'][$summary_id];
+ $plugin->submitOptionsForm($form['summary']['options'][$summary_id], $form_state, $options);
+ // Copy the now submitted options to their final resting place so they get saved.
+ $form_state['values']['options']['summary_options'] = $options;
+ }
+
+ $validate_id = $form_state['values']['options']['validate']['type'];
+ $plugin = $this->get_plugin('argument_validator', $validate_id);
+ if ($plugin) {
+ $options = &$form_state['values']['options']['validate']['options'][$validate_id];
+ $plugin->submitOptionsForm($form['validate']['options'][$validate_id], $form_state, $options);
+ // Copy the now submitted options to their final resting place so they get saved.
+ $form_state['values']['options']['validate_options'] = $options;
+ }
+
+ // Clear out the content of title if it's not enabled.
+ $options =& $form_state['values']['options'];
+ if (empty($options['title_enable'])) {
+ $options['title'] = '';
+ }
+ }
+
+ /**
+ * Provide a list of default behaviors for this argument if the argument
+ * is not present.
+ *
+ * Override this method to provide additional (or fewer) default behaviors.
+ */
+ function default_actions($which = NULL) {
+ $defaults = array(
+ 'ignore' => array(
+ 'title' => t('Display all results for the specified field'),
+ 'method' => 'default_ignore',
+ 'breadcrumb' => TRUE, // generate a breadcrumb to here
+ ),
+ 'default' => array(
+ 'title' => t('Provide default value'),
+ 'method' => 'default_default',
+ 'form method' => 'default_argument_form',
+ 'has default argument' => TRUE,
+ 'default only' => TRUE, // this can only be used for missing argument, not validation failure
+ 'breadcrumb' => TRUE, // generate a breadcrumb to here
+ ),
+ 'not found' => array(
+ 'title' => t('Hide view'),
+ 'method' => 'default_not_found',
+ 'hard fail' => TRUE, // This is a hard fail condition
+ ),
+ 'summary' => array(
+ 'title' => t('Display a summary'),
+ 'method' => 'default_summary',
+ 'form method' => 'default_summary_form',
+ 'style plugin' => TRUE,
+ 'breadcrumb' => TRUE, // generate a breadcrumb to here
+ ),
+ 'empty' => array(
+ 'title' => t('Display contents of "No results found"'),
+ 'method' => 'default_empty',
+ 'breadcrumb' => TRUE, // generate a breadcrumb to here
+ ),
+ 'access denied' => array(
+ 'title' => t('Display "Access Denied"'),
+ 'method' => 'default_access_denied',
+ 'breadcrumb' => FALSE, // generate a breadcrumb to here
+ ),
+ );
+
+ if ($this->view->display_handler->hasPath()) {
+ $defaults['not found']['title'] = t('Show "Page not found"');
+ }
+
+ if ($which) {
+ if (!empty($defaults[$which])) {
+ return $defaults[$which];
+ }
+ }
+ else {
+ return $defaults;
+ }
+ }
+
+ /**
+ * Provide a form for selecting the default argument when the
+ * default action is set to provide default argument.
+ */
+ function default_argument_form(&$form, &$form_state) {
+ $plugins = views_get_plugin_definitions('argument_default');
+ $options = array();
+
+ $form['default_argument_skip_url'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Skip default argument for view URL'),
+ '#default_value' => $this->options['default_argument_skip_url'],
+ '#description' => t('Select whether to include this default argument when constructing the URL for this view. Skipping default arguments is useful e.g. in the case of feeds.')
+ );
+
+ $form['default_argument_type'] = array(
+ '#prefix' => '<div id="edit-options-default-argument-type-wrapper">',
+ '#suffix' => '</div>',
+ '#type' => 'select',
+ '#id' => 'edit-options-default-argument-type',
+ '#title' => t('Type'),
+ '#default_value' => $this->options['default_argument_type'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[default_action]"]' => array('value' => 'default'),
+ ),
+ ),
+ // Views custom key, moves this element to the appropriate container
+ // under the radio button.
+ '#argument_option' => 'default',
+ );
+
+ foreach ($plugins as $id => $info) {
+ if (!empty($info['no_ui'])) {
+ continue;
+ }
+ $plugin = $this->get_plugin('argument_default', $id);
+ if ($plugin) {
+ if ($plugin->access() || $this->options['default_argument_type'] == $id) {
+ $form['argument_default']['#argument_option'] = 'default';
+ $form['argument_default'][$id] = array(
+ '#prefix' => '<div id="edit-options-argument-default-options-' . $id . '-wrapper">',
+ '#suffix' => '</div>',
+ '#id' => 'edit-options-argument-default-options-' . $id,
+ '#type' => 'item',
+ // Even if the plugin has no options add the key to the form_state.
+ '#input' => TRUE,
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[default_action]"]' => array('value' => 'default'),
+ ':input[name="options[default_argument_type]"]' => array('value' => $id),
+ ),
+ ),
+ );
+ $options[$id] = $info['title'];
+ $plugin->buildOptionsForm($form['argument_default'][$id], $form_state);
+ }
+ }
+ }
+
+ asort($options);
+ $form['default_argument_type']['#options'] = $options;
+ }
+
+ /**
+ * Provide a form for selecting further summary options when the
+ * default action is set to display one.
+ */
+ function default_summary_form(&$form, &$form_state) {
+ $style_plugins = views_get_plugin_definitions('style');
+ $summary_plugins = array();
+ $format_options = array();
+ foreach ($style_plugins as $key => $plugin) {
+ if (isset($plugin['type']) && $plugin['type'] == 'summary') {
+ $summary_plugins[$key] = $plugin;
+ $format_options[$key] = $plugin['title'];
+ }
+ }
+
+ $form['summary'] = array(
+ // Views custom key, moves this element to the appropriate container
+ // under the radio button.
+ '#argument_option' => 'summary',
+ );
+ $form['summary']['sort_order'] = array(
+ '#type' => 'radios',
+ '#title' => t('Sort order'),
+ '#options' => array('asc' => t('Ascending'), 'desc' => t('Descending')),
+ '#default_value' => $this->options['summary']['sort_order'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[default_action]"]' => array('value' => 'summary'),
+ ),
+ ),
+ );
+ $form['summary']['number_of_records'] = array(
+ '#type' => 'radios',
+ '#title' => t('Sort by'),
+ '#default_value' => $this->options['summary']['number_of_records'],
+ '#options' => array(
+ 0 => $this->get_sort_name(),
+ 1 => t('Number of records')
+ ),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[default_action]"]' => array('value' => 'summary'),
+ ),
+ ),
+ );
+
+ $form['summary']['format'] = array(
+ '#type' => 'radios',
+ '#title' => t('Format'),
+ '#options' => $format_options,
+ '#default_value' => $this->options['summary']['format'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[default_action]"]' => array('value' => 'summary'),
+ ),
+ ),
+ );
+
+ foreach ($summary_plugins as $id => $info) {
+ $plugin = $this->get_plugin('style', $id);
+ if (!$plugin->usesOptions()) {
+ continue;
+ }
+ if ($plugin) {
+ $form['summary']['options'][$id] = array(
+ '#prefix' => '<div id="edit-options-summary-options-' . $id . '-wrapper">',
+ '#suffix' => '</div>',
+ '#id' => 'edit-options-summary-options-' . $id,
+ '#type' => 'item',
+ '#input' => TRUE, // trick it into checking input to make #process run
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[default_action]"]' => array('value' => 'summary'),
+ ':input[name="options[summary][format]"]' => array('value' => $id),
+ ),
+ ),
+ );
+ $options[$id] = $info['title'];
+ $plugin->buildOptionsForm($form['summary']['options'][$id], $form_state);
+ }
+ }
+ }
+
+ /**
+ * Handle the default action, which means our argument wasn't present.
+ *
+ * Override this method only with extreme care.
+ *
+ * @return
+ * A boolean value; if TRUE, continue building this view. If FALSE,
+ * building the view will be aborted here.
+ */
+ function default_action($info = NULL) {
+ if (!isset($info)) {
+ $info = $this->default_actions($this->options['default_action']);
+ }
+
+ if (!$info) {
+ return FALSE;
+ }
+
+ if (!empty($info['method args'])) {
+ return call_user_func_array(array(&$this, $info['method']), $info['method args']);
+ }
+ else {
+ return $this->{$info['method']}();
+ }
+ }
+
+ /**
+ * How to act if validation failes
+ */
+ public function validateFail() {
+ $info = $this->default_actions($this->options['validate']['fail']);
+ return $this->default_action($info);
+ }
+ /**
+ * Default action: ignore.
+ *
+ * If an argument was expected and was not given, in this case, simply
+ * ignore the argument entirely.
+ */
+ function default_ignore() {
+ return TRUE;
+ }
+
+ /**
+ * Default action: not found.
+ *
+ * If an argument was expected and was not given, in this case, report
+ * the view as 'not found' or hide it.
+ */
+ function default_not_found() {
+ // Set a failure condition and let the display manager handle it.
+ $this->view->build_info['fail'] = TRUE;
+ return FALSE;
+ }
+
+ /**
+ * Default action: access denied.
+ *
+ * If an argument was expected and was not given, in this case, report
+ * the view as 'access denied'.
+ */
+ function default_access_denied() {
+ $this->view->build_info['denied'] = TRUE;
+ return FALSE;
+ }
+
+ /**
+ * Default action: empty
+ *
+ * If an argument was expected and was not given, in this case, display
+ * the view's empty text
+ */
+ function default_empty() {
+ // We return with no query; this will force the empty text.
+ $this->view->built = TRUE;
+ $this->view->executed = TRUE;
+ $this->view->result = array();
+ return FALSE;
+ }
+
+ /**
+ * This just returns true. The view argument builder will know where
+ * to find the argument from.
+ */
+ function default_default() {
+ return TRUE;
+ }
+
+ /**
+ * Determine if the argument is set to provide a default argument.
+ */
+ function has_default_argument() {
+ $info = $this->default_actions($this->options['default_action']);
+ return !empty($info['has default argument']);
+ }
+
+ /**
+ * Get a default argument, if available.
+ */
+ function get_default_argument() {
+ $plugin = $this->get_plugin('argument_default');
+ if ($plugin) {
+ return $plugin->get_argument();
+ }
+ }
+
+ /**
+ * Process the summary arguments for display.
+ *
+ * For example, the validation plugin may want to alter an argument for use in
+ * the URL.
+ */
+ function process_summary_arguments(&$args) {
+ if ($this->options['validate']['type'] != 'none') {
+ if (isset($this->validator) || $this->validator = $this->get_plugin('argument_validator')) {
+ $this->validator->process_summary_arguments($args);
+ }
+ }
+ }
+
+ /**
+ * Default action: summary.
+ *
+ * If an argument was expected and was not given, in this case, display
+ * a summary query.
+ */
+ function default_summary() {
+ $this->view->build_info['summary'] = TRUE;
+ $this->view->build_info['summary_level'] = $this->options['id'];
+
+ // Change the display style to the summary style for this
+ // argument.
+ $this->view->plugin_name = $this->options['summary']['format'];
+ $this->view->style_options = $this->options['summary_options'];
+
+ // Clear out the normal primary field and whatever else may have
+ // been added and let the summary do the work.
+ $this->query->clear_fields();
+ $this->summary_query();
+
+ $by = $this->options['summary']['number_of_records'] ? 'num_records' : NULL;
+ $this->summary_sort($this->options['summary']['sort_order'], $by);
+
+ // Summaries have their own sorting and fields, so tell the View not
+ // to build these.
+ $this->view->build_sort = FALSE;
+ return TRUE;
+ }
+
+ /**
+ * Build the info for the summary query.
+ *
+ * This must:
+ * - add_groupby: group on this field in order to create summaries.
+ * - add_field: add a 'num_nodes' field for the count. Usually it will
+ * be a count on $view->base_field
+ * - set_count_field: Reset the count field so we get the right paging.
+ *
+ * @return
+ * The alias used to get the number of records (count) for this entry.
+ */
+ function summary_query() {
+ $this->ensureMyTable();
+ // Add the field.
+ $this->base_alias = $this->query->add_field($this->tableAlias, $this->realField);
+
+ $this->summary_name_field();
+ return $this->summary_basics();
+ }
+
+ /**
+ * Add the name field, which is the field displayed in summary queries.
+ * This is often used when the argument is numeric.
+ */
+ function summary_name_field() {
+ // Add the 'name' field. For example, if this is a uid argument, the
+ // name field would be 'name' (i.e, the username).
+
+ if (isset($this->name_table)) {
+ // if the alias is different then we're probably added, not ensured,
+ // so look up the join and add it instead.
+ if ($this->tableAlias != $this->name_table) {
+ $j = HandlerBase::getTableJoin($this->name_table, $this->table);
+ if ($j) {
+ $join = clone $j;
+ $join->leftTable = $this->tableAlias;
+ $this->name_table_alias = $this->query->add_table($this->name_table, $this->relationship, $join);
+ }
+ }
+ else {
+ $this->name_table_alias = $this->query->ensure_table($this->name_table, $this->relationship);
+ }
+ }
+ else {
+ $this->name_table_alias = $this->tableAlias;
+ }
+
+ if (isset($this->name_field)) {
+ $this->name_alias = $this->query->add_field($this->name_table_alias, $this->name_field);
+ }
+ else {
+ $this->name_alias = $this->base_alias;
+ }
+ }
+
+ /**
+ * Some basic summary behavior that doesn't need to be repeated as much as
+ * code that goes into summary_query()
+ */
+ function summary_basics($count_field = TRUE) {
+ // Add the number of nodes counter
+ $distinct = ($this->view->display_handler->getOption('distinct') && empty($this->query->no_distinct));
+
+ $count_alias = $this->query->add_field($this->query->base_table, $this->query->base_field, 'num_records', array('count' => TRUE, 'distinct' => $distinct));
+ $this->query->add_groupby($this->name_alias);
+
+ if ($count_field) {
+ $this->query->set_count_field($this->tableAlias, $this->realField);
+ }
+
+ $this->count_alias = $count_alias;
+ }
+
+ /**
+ * Sorts the summary based upon the user's selection. The base variant of
+ * this is usually adequte.
+ *
+ * @param $order
+ * The order selected in the UI.
+ */
+ function summary_sort($order, $by = NULL) {
+ $this->query->add_orderby(NULL, NULL, $order, (!empty($by) ? $by : $this->name_alias));
+ }
+
+ /**
+ * Provide the argument to use to link from the summary to the next level;
+ * this will be called once per row of a summary, and used as part of
+ * $view->getUrl().
+ *
+ * @param $data
+ * The query results for the row.
+ */
+ function summary_argument($data) {
+ return $data->{$this->base_alias};
+ }
+
+ /**
+ * Provides the name to use for the summary. By default this is just
+ * the name field.
+ *
+ * @param $data
+ * The query results for the row.
+ */
+ function summary_name($data) {
+ $value = $data->{$this->name_alias};
+ if (empty($value) && !empty($this->definition['empty field name'])) {
+ $value = $this->definition['empty field name'];
+ }
+ return check_plain($value);
+ }
+
+ /**
+ * Set up the query for this argument.
+ *
+ * The argument sent may be found at $this->argument.
+ */
+ public function query($group_by = FALSE) {
+ $this->ensureMyTable();
+ $this->query->add_where(0, "$this->tableAlias.$this->realField", $this->argument);
+ }
+
+ /**
+ * Get the title this argument will assign the view, given the argument.
+ *
+ * This usually needs to be overridden to provide a proper title.
+ */
+ function title() {
+ return check_plain($this->argument);
+ }
+
+ /**
+ * Called by the view object to get the title. This may be set by a
+ * validator so we don't necessarily call through to title().
+ */
+ function get_title() {
+ if (isset($this->validated_title)) {
+ return $this->validated_title;
+ }
+ else {
+ return $this->title();
+ }
+ }
+
+ /**
+ * Validate that this argument works. By default, all arguments are valid.
+ */
+ public function validateArgument($arg) {
+ // By using % in URLs, arguments could be validated twice; this eases
+ // that pain.
+ if (isset($this->argument_validated)) {
+ return $this->argument_validated;
+ }
+
+ if ($this->is_exception($arg)) {
+ return $this->argument_validated = TRUE;
+ }
+
+ $plugin = $this->get_plugin('argument_validator');
+ return $this->argument_validated = $plugin->validate_argument($arg);
+ }
+
+ /**
+ * Called by the menu system to validate an argument.
+ *
+ * This checks to see if this is a 'soft fail', which means that if the
+ * argument fails to validate, but there is an action to take anyway,
+ * then validation cannot actually fail.
+ */
+ function validate_argument($arg) {
+ $validate_info = $this->default_actions($this->options['validate']['fail']);
+ if (empty($validate_info['hard fail'])) {
+ return TRUE;
+ }
+
+ $rc = $this->validateArgument($arg);
+
+ // If the validator has changed the validate fail condition to a
+ // soft fail, deal with that:
+ $validate_info = $this->default_actions($this->options['validate']['fail']);
+ if (empty($validate_info['hard fail'])) {
+ return TRUE;
+ }
+
+ return $rc;
+ }
+
+ /**
+ * Set the input for this argument
+ *
+ * @return TRUE if it successfully validates; FALSE if it does not.
+ */
+ function set_argument($arg) {
+ $this->argument = $arg;
+ return $this->validateArgument($arg);
+ }
+
+ /**
+ * Get the value of this argument.
+ */
+ function get_value() {
+ // If we already processed this argument, we're done.
+ if (isset($this->argument)) {
+ return $this->argument;
+ }
+
+ // Otherwise, we have to pretend to process ourself to find the value.
+ $value = NULL;
+ // Find the position of this argument within the view.
+ $position = 0;
+ foreach ($this->view->argument as $id => $argument) {
+ if ($id == $this->options['id']) {
+ break;
+ }
+ $position++;
+ }
+
+ $arg = isset($this->view->args[$position]) ? $this->view->args[$position] : NULL;
+ $this->position = $position;
+
+ // Clone ourselves so that we don't break things when we're really
+ // processing the arguments.
+ $argument = clone $this;
+ if (!isset($arg) && $argument->has_default_argument()) {
+ $arg = $argument->get_default_argument();
+
+ // remember that this argument was computed, not passed on the URL.
+ $this->is_default = TRUE;
+ }
+ // Set the argument, which will also validate that the argument can be set.
+ if ($argument->set_argument($arg)) {
+ $value = $argument->argument;
+ }
+ unset($argument);
+ return $value;
+ }
+
+ /**
+ * Get the display or row plugin, if it exists.
+ */
+ function get_plugin($type = 'argument_default', $name = NULL) {
+ $options = array();
+ switch ($type) {
+ case 'argument_default':
+ $plugin_name = $this->options['default_argument_type'];
+ $options_name = 'default_argument_options';
+ break;
+ case 'argument_validator':
+ $plugin_name = $this->options['validate']['type'];
+ $options_name = 'validate_options';
+ break;
+ case 'style':
+ $plugin_name = $this->options['summary']['format'];
+ $options_name = 'summary_options';
+ }
+
+ if (!$name) {
+ $name = $plugin_name;
+ }
+
+ // we only fetch the options if we're fetching the plugin actually
+ // in use.
+ if ($name == $plugin_name) {
+ $options = $this->options[$options_name];
+ }
+
+ $plugin = views_get_plugin($type, $name);
+ if ($plugin) {
+ // Style plugins expects different parameters as argument related plugins.
+ if ($type == 'style') {
+ $plugin->init($this->view, $this->view->display_handler->display, $options);
+ }
+ else {
+ $plugin->init($this->view, $this, $options);
+ }
+ return $plugin;
+ }
+ }
+
+ /**
+ * Return a description of how the argument would normally be sorted.
+ *
+ * Subclasses should override this to specify what the default sort order of
+ * their argument is (e.g. alphabetical, numeric, date).
+ */
+ function get_sort_name() {
+ return t('Default sort', array(), array('context' => 'Sort order'));
+ }
+
+ /**
+ * Custom form radios process function.
+ *
+ * Roll out a single radios element to a list of radios, using the options
+ * array as index. While doing that, create a container element underneath
+ * each option, which contains the settings related to that option.
+ *
+ * @see form_process_radios()
+ */
+ public static function processContainerRadios($element) {
+ if (count($element['#options']) > 0) {
+ foreach ($element['#options'] as $key => $choice) {
+ $element += array($key => array());
+ // Generate the parents as the autogenerator does, so we will have a
+ // unique id for each radio button.
+ $parents_for_id = array_merge($element['#parents'], array($key));
+
+ $element[$key] += array(
+ '#type' => 'radio',
+ '#title' => $choice,
+ // The key is sanitized in drupal_attributes() during output from the
+ // theme function.
+ '#return_value' => $key,
+ '#default_value' => isset($element['#default_value']) ? $element['#default_value'] : NULL,
+ '#attributes' => $element['#attributes'],
+ '#parents' => $element['#parents'],
+ '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
+ '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
+ );
+ $element[$key . '_options'] = array(
+ '#type' => 'container',
+ '#attributes' => array('class' => array('views-admin-dependent')),
+ );
+ }
+ }
+ return $element;
+ }
+
+}
+
+/**
+ * @}
+ */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/Broken.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Broken.php
new file mode 100644
index 0000000..d08cda3
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Broken.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\Broken.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * A special handler to take the place of missing or broken handlers.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @Plugin(
+ * id = "broken"
+ * )
+ */
+class Broken extends ArgumentPluginBase {
+
+ public function adminLabel($short = FALSE) {
+ return t('Broken/missing handler');
+ }
+
+ public function defineOptions() { return array(); }
+ public function ensureMyTable() { /* No table to ensure! */ }
+ public function query($group_by = FALSE) { /* No query to run */ }
+ public function buildOptionsForm(&$form, &$form_state) {
+ $form['markup'] = array(
+ '#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
+ );
+ }
+
+ /**
+ * Determine if the handler is considered 'broken'
+ */
+ public function broken() { return TRUE; }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/Date.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Date.php
new file mode 100644
index 0000000..ce3f52f
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Date.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\Date.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Abstract argument handler for dates.
+ *
+ * Adds an option to set a default argument based on the current date.
+ *
+ * @param $arg_format
+ * The format string to use on the current time when
+ * creating a default date argument.
+ *
+ * Definitions terms:
+ * - many to one: If true, the "many to one" helper will be used.
+ * - invalid input: A string to give to the user for obviously invalid input.
+ * This is deprecated in favor of argument validators.
+ *
+ * @see Drupal\views\ManyTonOneHelper
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @Plugin(
+ * id = "date"
+ * )
+ */
+class Date extends Formula {
+
+ var $option_name = 'default_argument_date';
+ var $arg_format = 'Y-m-d';
+
+ /**
+ * Add an option to set the default value to the current date.
+ */
+ function default_argument_form(&$form, &$form_state) {
+ parent::default_argument_form($form, $form_state);
+ $form['default_argument_type']['#options'] += array('date' => t('Current date'));
+ $form['default_argument_type']['#options'] += array('node_created' => t("Current node's creation time"));
+ $form['default_argument_type']['#options'] += array('node_changed' => t("Current node's update time")); }
+
+ /**
+ * Set the empty argument value to the current date,
+ * formatted appropriately for this argument.
+ */
+ function get_default_argument($raw = FALSE) {
+ if (!$raw && $this->options['default_argument_type'] == 'date') {
+ return date($this->definition['format'], REQUEST_TIME);
+ }
+ elseif (!$raw && in_array($this->options['default_argument_type'], array('node_created', 'node_changed'))) {
+ foreach (range(1, 3) as $i) {
+ $node = menu_get_object('node', $i);
+ if (!empty($node)) {
+ continue;
+ }
+ }
+
+ if (arg(0) == 'node' && is_numeric(arg(1))) {
+ $node = node_load(arg(1));
+ }
+
+ if (empty($node)) {
+ return parent::get_default_argument();
+ }
+ elseif ($this->options['default_argument_type'] == 'node_created') {
+ return date($this->definition['format'], $node->created);
+ }
+ elseif ($this->options['default_argument_type'] == 'node_changed') {
+ return date($this->definition['format'], $node->changed);
+ }
+ }
+
+ return parent::get_default_argument($raw);
+ }
+
+ function get_sort_name() {
+ return t('Date', array(), array('context' => 'Sort order'));
+ }
+
+ /**
+ * Creates cross-database SQL date extraction.
+ *
+ * @param string $extract_type
+ * The type of value to extract from the date, like 'MONTH'.
+ *
+ * @return string
+ * An appropriate SQL string for the DB type and field type.
+ */
+ public function extractSQL($extract_type) {
+ $db_type = Database::getConnection()->databaseType();
+ $field = $this->getSQLDateField();
+
+ // Note there is no space after FROM to avoid db_rewrite problems
+ // see http://drupal.org/node/79904.
+ switch ($extract_type) {
+ case 'DATE':
+ return $field;
+ case 'YEAR':
+ return "EXTRACT(YEAR FROM($field))";
+ case 'MONTH':
+ return "EXTRACT(MONTH FROM($field))";
+ case 'DAY':
+ return "EXTRACT(DAY FROM($field))";
+ case 'HOUR':
+ return "EXTRACT(HOUR FROM($field))";
+ case 'MINUTE':
+ return "EXTRACT(MINUTE FROM($field))";
+ case 'SECOND':
+ return "EXTRACT(SECOND FROM($field))";
+ // ISO week number for date
+ case 'WEEK':
+ switch ($db_type) {
+ case 'mysql':
+ // WEEK using arg 3 in mysql should return the same value as postgres
+ // EXTRACT.
+ return "WEEK($field, 3)";
+ case 'pgsql':
+ return "EXTRACT(WEEK FROM($field))";
+ }
+ case 'DOW':
+ switch ($db_type) {
+ case 'mysql':
+ // mysql returns 1 for Sunday through 7 for Saturday php date
+ // functions and postgres use 0 for Sunday and 6 for Saturday.
+ return "INTEGER(DAYOFWEEK($field) - 1)";
+ case 'pgsql':
+ return "EXTRACT(DOW FROM($field))";
+ }
+ case 'DOY':
+ switch ($db_type) {
+ case 'mysql':
+ return "DAYOFYEAR($field)";
+ case 'pgsql':
+ return "EXTRACT(DOY FROM($field))";
+ }
+ }
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/Formula.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Formula.php
new file mode 100644
index 0000000..4c0865f
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Formula.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\Formula.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\views\ViewExecutable;
+
+/**
+ * Abstract argument handler for simple formulae.
+ *
+ * Child classes of this object should implement summary_argument, at least.
+ *
+ * Definition terms:
+ * - formula: The formula to use for this handler.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @Plugin(
+ * id = "formula"
+ * )
+ */
+class Formula extends ArgumentPluginBase {
+
+ var $formula = NULL;
+
+ /**
+ * Overrides Drupal\views\Plugin\views\argument\ArgumentPluginBase::init().
+ */
+ public function init(ViewExecutable $view, &$options) {
+ parent::init($view, $options);
+
+ if (!empty($this->definition['formula'])) {
+ $this->formula = $this->definition['formula'];
+ }
+ }
+
+ function get_formula() {
+ return str_replace('***table***', $this->tableAlias, $this->formula);
+ }
+
+ /**
+ * Build the summary query based on a formula
+ */
+ function summary_query() {
+ $this->ensureMyTable();
+ // Now that our table is secure, get our formula.
+ $formula = $this->get_formula();
+
+ // Add the field.
+ $this->base_alias = $this->name_alias = $this->query->add_field(NULL, $formula, $this->field);
+ $this->query->set_count_field(NULL, $formula, $this->field);
+
+ return $this->summary_basics(FALSE);
+ }
+
+ /**
+ * Build the query based upon the formula
+ */
+ public function query($group_by = FALSE) {
+ $this->ensureMyTable();
+ // Now that our table is secure, get our formula.
+ $placeholder = $this->placeholder();
+ $formula = $this->get_formula() .' = ' . $placeholder;
+ $placeholders = array(
+ $placeholder => $this->argument,
+ );
+ $this->query->add_where(0, $formula, $placeholders, 'formula');
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/GroupByNumeric.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/GroupByNumeric.php
new file mode 100644
index 0000000..16976d3
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/GroupByNumeric.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\GroupByNumeric.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Simple handler for arguments using group by.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @Plugin(
+ * id = "groupby_numeric"
+ * )
+ */
+class GroupByNumeric extends ArgumentPluginBase {
+
+ public function query($group_by = FALSE) {
+ $this->ensureMyTable();
+ $field = $this->getField();
+ $placeholder = $this->placeholder();
+
+ $this->query->add_having_expression(0, "$field = $placeholder", array($placeholder => $this->argument));
+ }
+
+ public function adminLabel($short = FALSE) {
+ return $this->getField(parent::adminLabel($short));
+ }
+
+ function get_sort_name() {
+ return t('Numerical', array(), array('context' => 'Sort order'));
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/ManyToOne.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/ManyToOne.php
new file mode 100644
index 0000000..b0807ad
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/ManyToOne.php
@@ -0,0 +1,200 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\ManyToOne.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\views\ViewExecutable;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\views\ManyToOneHelper;
+
+/**
+ * An argument handler for use in fields that have a many to one relationship
+ * with the table(s) to the left. This adds a bunch of options that are
+ * reasonably common with this type of relationship.
+ * Definition terms:
+ * - numeric: If true, the field will be considered numeric. Probably should
+ * always be set TRUE as views_handler_argument_string has many to one
+ * capabilities.
+ * - zero is null: If true, a 0 will be handled as empty, so for example
+ * a default argument can be provided or a summary can be shown.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @Plugin(
+ * id = "many_to_one"
+ * )
+ */
+class ManyToOne extends ArgumentPluginBase {
+
+ public function init(ViewExecutable $view, &$options) {
+ parent::init($view, $options);
+ $this->helper = new ManyToOneHelper($this);
+
+ // Ensure defaults for these, during summaries and stuff:
+ $this->operator = 'or';
+ $this->value = array();
+ }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ if (!empty($this->definition['numeric'])) {
+ $options['break_phrase'] = array('default' => FALSE, 'bool' => TRUE);
+ }
+
+ $options['add_table'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['require_value'] = array('default' => FALSE, 'bool' => TRUE);
+
+ if (isset($this->helper)) {
+ $this->helper->defineOptions($options);
+ }
+ else {
+ $helper = new ManyToOneHelper($this);
+ $helper->defineOptions($options);
+ }
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ // allow + for or, , for and
+ $form['break_phrase'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow multiple values'),
+ '#description' => t('If selected, users can enter multiple values in the form of 1+2+3 (for OR) or 1,2,3 (for AND).'),
+ '#default_value' => !empty($this->options['break_phrase']),
+ '#fieldset' => 'more',
+ );
+
+ $form['add_table'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow multiple filter values to work together'),
+ '#description' => t('If selected, multiple instances of this filter can work together, as though multiple values were supplied to the same filter. This setting is not compatible with the "Reduce duplicates" setting.'),
+ '#default_value' => !empty($this->options['add_table']),
+ '#fieldset' => 'more',
+ );
+
+ $form['require_value'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Do not display items with no value in summary'),
+ '#default_value' => !empty($this->options['require_value']),
+ '#fieldset' => 'more',
+ );
+
+ $this->helper->buildOptionsForm($form, $form_state);
+ }
+
+ /**
+ * Override ensureMyTable so we can control how this joins in.
+ * The operator actually has influence over joining.
+ */
+ public function ensureMyTable() {
+ $this->helper->ensureMyTable();
+ }
+
+ public function query($group_by = FALSE) {
+ $empty = FALSE;
+ if (isset($this->definition['zero is null']) && $this->definition['zero is null']) {
+ if (empty($this->argument)) {
+ $empty = TRUE;
+ }
+ }
+ else {
+ if (!isset($this->argument)) {
+ $empty = TRUE;
+ }
+ }
+ if ($empty) {
+ parent::ensureMyTable();
+ $this->query->add_where(0, "$this->tableAlias.$this->realField", NULL, 'IS NULL');
+ return;
+ }
+
+ if (!empty($this->options['break_phrase'])) {
+ if (!empty($this->definition['numeric'])) {
+ $this->breakPhrase($this->argument, $this);
+ }
+ else {
+ $this->breakPhraseString($this->argument, $this);
+ }
+ }
+ else {
+ $this->value = array($this->argument);
+ $this->operator = 'or';
+ }
+
+ $this->helper->add_filter();
+ }
+
+ function title() {
+ if (!$this->argument) {
+ return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
+ }
+
+ if (!empty($this->options['break_phrase'])) {
+ $this->breakPhrase($this->argument, $this);
+ }
+ else {
+ $this->value = array($this->argument);
+ $this->operator = 'or';
+ }
+
+ // @todo -- both of these should check definition for alternate keywords.
+
+ if (empty($this->value)) {
+ return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
+ }
+
+ if ($this->value === array(-1)) {
+ return !empty($this->definition['invalid input']) ? $this->definition['invalid input'] : t('Invalid input');
+ }
+
+ return implode($this->operator == 'or' ? ' + ' : ', ', $this->title_query());
+ }
+
+ function summary_query() {
+ $field = $this->table . '.' . $this->field;
+ $join = $this->getJoin();
+
+ if (!empty($this->options['require_value'])) {
+ $join->type = 'INNER';
+ }
+
+ if (empty($this->options['add_table']) || empty($this->view->many_to_one_tables[$field])) {
+ $this->tableAlias = $this->query->ensure_table($this->table, $this->relationship, $join);
+ }
+ else {
+ $this->tableAlias = $this->helper->summary_join();
+ }
+
+ // Add the field.
+ $this->base_alias = $this->query->add_field($this->tableAlias, $this->realField);
+
+ $this->summary_name_field();
+
+ return $this->summary_basics();
+ }
+
+ function summary_argument($data) {
+ $value = $data->{$this->base_alias};
+ if (empty($value)) {
+ $value = 0;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Override for specific title lookups.
+ */
+ function title_query() {
+ return $this->value;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/Null.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Null.php
new file mode 100644
index 0000000..6097b00
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Null.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\Null.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Argument handler that ignores the argument.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @Plugin(
+ * id = "null"
+ * )
+ */
+class Null extends ArgumentPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['must_not_be'] = array('default' => FALSE, 'bool' => TRUE);
+ return $options;
+ }
+
+ /**
+ * Override buildOptionsForm() so that only the relevant options
+ * are displayed to the user.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ $form['must_not_be'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Fail basic validation if any argument is given'),
+ '#default_value' => !empty($this->options['must_not_be']),
+ '#description' => t('By checking this field, you can use this to make sure views with more arguments than necessary fail validation.'),
+ '#fieldset' => 'more',
+ );
+
+ unset($form['exception']);
+ }
+
+ /**
+ * Override default_actions() to remove actions that don't
+ * make sense for a null argument.
+ */
+ function default_actions($which = NULL) {
+ if ($which) {
+ if (in_array($which, array('ignore', 'not found', 'empty', 'default'))) {
+ return parent::default_actions($which);
+ }
+ return;
+ }
+ $actions = parent::default_actions();
+ unset($actions['summary asc']);
+ unset($actions['summary desc']);
+ return $actions;
+ }
+
+ /**
+ * Override the behavior of query() to prevent the query
+ * from being changed in any way.
+ */
+ public function query($group_by = FALSE) {}
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/Numeric.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Numeric.php
new file mode 100644
index 0000000..3972b4d
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Numeric.php
@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\Numeric.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Basic argument handler for arguments that are numeric. Incorporates
+ * break_phrase.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @Plugin(
+ * id = "numeric"
+ * )
+ */
+class Numeric extends ArgumentPluginBase {
+
+ /**
+ * The operator used for the query: or|and.
+ * @var string
+ */
+ var $operator;
+
+ /**
+ * The actual value which is used for querying.
+ * @var array
+ */
+ var $value;
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['break_phrase'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['not'] = array('default' => FALSE, 'bool' => TRUE);
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ // allow + for or, , for and
+ $form['break_phrase'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow multiple values'),
+ '#description' => t('If selected, users can enter multiple values in the form of 1+2+3 (for OR) or 1,2,3 (for AND).'),
+ '#default_value' => !empty($this->options['break_phrase']),
+ '#fieldset' => 'more',
+ );
+
+ $form['not'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Exclude'),
+ '#description' => t('If selected, the numbers entered for the filter will be excluded rather than limiting the view.'),
+ '#default_value' => !empty($this->options['not']),
+ '#fieldset' => 'more',
+ );
+ }
+
+ function title() {
+ if (!$this->argument) {
+ return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
+ }
+
+ if (!empty($this->options['break_phrase'])) {
+ $this->breakPhrase($this->argument, $this);
+ }
+ else {
+ $this->value = array($this->argument);
+ $this->operator = 'or';
+ }
+
+ if (empty($this->value)) {
+ return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
+ }
+
+ if ($this->value === array(-1)) {
+ return !empty($this->definition['invalid input']) ? $this->definition['invalid input'] : t('Invalid input');
+ }
+
+ return implode($this->operator == 'or' ? ' + ' : ', ', $this->title_query());
+ }
+
+ /**
+ * Override for specific title lookups.
+ * @return array
+ * Returns all titles, if it's just one title it's an array with one entry.
+ */
+ function title_query() {
+ return $this->value;
+ }
+
+ public function query($group_by = FALSE) {
+ $this->ensureMyTable();
+
+ if (!empty($this->options['break_phrase'])) {
+ $this->breakPhrase($this->argument, $this);
+ }
+ else {
+ $this->value = array($this->argument);
+ }
+
+ $placeholder = $this->placeholder();
+ $null_check = empty($this->options['not']) ? '' : "OR $this->tableAlias.$this->realField IS NULL";
+
+ if (count($this->value) > 1) {
+ $operator = empty($this->options['not']) ? 'IN' : 'NOT IN';
+ $this->query->add_where_expression(0, "$this->tableAlias.$this->realField $operator($placeholder) $null_check", array($placeholder => $this->value));
+ }
+ else {
+ $operator = empty($this->options['not']) ? '=' : '!=';
+ $this->query->add_where_expression(0, "$this->tableAlias.$this->realField $operator $placeholder $null_check", array($placeholder => $this->argument));
+ }
+ }
+
+ function get_sort_name() {
+ return t('Numerical', array(), array('context' => 'Sort order'));
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/Standard.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Standard.php
new file mode 100644
index 0000000..d7dafb3
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/Standard.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\Standard.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Default implementation of the base argument plugin.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @Plugin(
+ * id = "standard"
+ * )
+ */
+class Standard extends ArgumentPluginBase {
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument/String.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument/String.php
new file mode 100644
index 0000000..59148de
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument/String.php
@@ -0,0 +1,289 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument\String.
+ */
+
+namespace Drupal\views\Plugin\views\argument;
+
+use Drupal\views\ViewExecutable;
+use Drupal\views\ManyToOneHelper;
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Basic argument handler to implement string arguments that may have length
+ * limits.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @Plugin(
+ * id = "string"
+ * )
+ */
+class String extends ArgumentPluginBase {
+
+ public function init(ViewExecutable $view, &$options) {
+ parent::init($view, $options);
+ if (!empty($this->definition['many to one'])) {
+ $this->helper = new ManyToOneHelper($this);
+
+ // Ensure defaults for these, during summaries and stuff:
+ $this->operator = 'or';
+ $this->value = array();
+ }
+ }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['glossary'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['limit'] = array('default' => 0);
+ $options['case'] = array('default' => 'none');
+ $options['path_case'] = array('default' => 'none');
+ $options['transform_dash'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['break_phrase'] = array('default' => FALSE, 'bool' => TRUE);
+
+ if (!empty($this->definition['many to one'])) {
+ $options['add_table'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['require_value'] = array('default' => FALSE, 'bool' => TRUE);
+ }
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $form['glossary'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Glossary mode'),
+ '#description' => t('Glossary mode applies a limit to the number of characters used in the filter value, which allows the summary view to act as a glossary.'),
+ '#default_value' => $this->options['glossary'],
+ '#fieldset' => 'more',
+ );
+
+ $form['limit'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Character limit'),
+ '#description' => t('How many characters of the filter value to filter against. If set to 1, all fields starting with the first letter in the filter value would be matched.'),
+ '#default_value' => $this->options['limit'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[glossary]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#fieldset' => 'more',
+ );
+
+ $form['case'] = array(
+ '#type' => 'select',
+ '#title' => t('Case'),
+ '#description' => t('When printing the title and summary, how to transform the case of the filter value.'),
+ '#options' => array(
+ 'none' => t('No transform'),
+ 'upper' => t('Upper case'),
+ 'lower' => t('Lower case'),
+ 'ucfirst' => t('Capitalize first letter'),
+ 'ucwords' => t('Capitalize each word'),
+ ),
+ '#default_value' => $this->options['case'],
+ '#fieldset' => 'more',
+ );
+
+ $form['path_case'] = array(
+ '#type' => 'select',
+ '#title' => t('Case in path'),
+ '#description' => t('When printing url paths, how to transform the case of the filter value. Do not use this unless with Postgres as it uses case sensitive comparisons.'),
+ '#options' => array(
+ 'none' => t('No transform'),
+ 'upper' => t('Upper case'),
+ 'lower' => t('Lower case'),
+ 'ucfirst' => t('Capitalize first letter'),
+ 'ucwords' => t('Capitalize each word'),
+ ),
+ '#default_value' => $this->options['path_case'],
+ '#fieldset' => 'more',
+ );
+
+ $form['transform_dash'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Transform spaces to dashes in URL'),
+ '#default_value' => $this->options['transform_dash'],
+ '#fieldset' => 'more',
+ );
+
+ if (!empty($this->definition['many to one'])) {
+ $form['add_table'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow multiple filter values to work together'),
+ '#description' => t('If selected, multiple instances of this filter can work together, as though multiple values were supplied to the same filter. This setting is not compatible with the "Reduce duplicates" setting.'),
+ '#default_value' => !empty($this->options['add_table']),
+ '#fieldset' => 'more',
+ );
+
+ $form['require_value'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Do not display items with no value in summary'),
+ '#default_value' => !empty($this->options['require_value']),
+ '#fieldset' => 'more',
+ );
+ }
+
+ // allow + for or, , for and
+ $form['break_phrase'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow multiple values'),
+ '#description' => t('If selected, users can enter multiple values in the form of 1+2+3 (for OR) or 1,2,3 (for AND).'),
+ '#default_value' => !empty($this->options['break_phrase']),
+ '#fieldset' => 'more',
+ );
+ }
+
+ /**
+ * Build the summary query based on a string
+ */
+ function summary_query() {
+ if (empty($this->definition['many to one'])) {
+ $this->ensureMyTable();
+ }
+ else {
+ $this->tableAlias = $this->helper->summary_join();
+ }
+
+ if (empty($this->options['glossary'])) {
+ // Add the field.
+ $this->base_alias = $this->query->add_field($this->tableAlias, $this->realField);
+ $this->query->set_count_field($this->tableAlias, $this->realField);
+ }
+ else {
+ // Add the field.
+ $formula = $this->get_formula();
+ $this->base_alias = $this->query->add_field(NULL, $formula, $this->field . '_truncated');
+ $this->query->set_count_field(NULL, $formula, $this->field, $this->field . '_truncated');
+ }
+
+ $this->summary_name_field();
+ return $this->summary_basics(FALSE);
+ }
+
+ /**
+ * Get the formula for this argument.
+ *
+ * $this->ensureMyTable() MUST have been called prior to this.
+ */
+ function get_formula() {
+ return "SUBSTRING($this->tableAlias.$this->realField, 1, " . intval($this->options['limit']) . ")";
+ }
+
+ /**
+ * Build the query based upon the formula
+ */
+ public function query($group_by = FALSE) {
+ $argument = $this->argument;
+ if (!empty($this->options['transform_dash'])) {
+ $argument = strtr($argument, '-', ' ');
+ }
+
+ if (!empty($this->options['break_phrase'])) {
+ $this->breakPhraseString($argument, $this);
+ }
+ else {
+ $this->value = array($argument);
+ $this->operator = 'or';
+ }
+
+ if (!empty($this->definition['many to one'])) {
+ if (!empty($this->options['glossary'])) {
+ $this->helper->formula = TRUE;
+ }
+ $this->helper->ensureMyTable();
+ $this->helper->add_filter();
+ return;
+ }
+
+ $this->ensureMyTable();
+ $formula = FALSE;
+ if (empty($this->options['glossary'])) {
+ $field = "$this->tableAlias.$this->realField";
+ }
+ else {
+ $formula = TRUE;
+ $field = $this->get_formula();
+ }
+
+ if (count($this->value) > 1) {
+ $operator = 'IN';
+ $argument = $this->value;
+ }
+ else {
+ $operator = '=';
+ }
+
+ if ($formula) {
+ $placeholder = $this->placeholder();
+ if ($operator == 'IN') {
+ $field .= " IN($placeholder)";
+ }
+ else {
+ $field .= ' = ' . $placeholder;
+ }
+ $placeholders = array(
+ $placeholder => $argument,
+ );
+ $this->query->add_where_expression(0, $field, $placeholders);
+ }
+ else {
+ $this->query->add_where(0, $field, $argument, $operator);
+ }
+ }
+
+ function summary_argument($data) {
+ $value = $this->caseTransform($data->{$this->base_alias}, $this->options['path_case']);
+ if (!empty($this->options['transform_dash'])) {
+ $value = strtr($value, ' ', '-');
+ }
+ return $value;
+ }
+
+ function get_sort_name() {
+ return t('Alphabetical', array(), array('context' => 'Sort order'));
+ }
+
+ function title() {
+ $this->argument = $this->caseTransform($this->argument, $this->options['case']);
+ if (!empty($this->options['transform_dash'])) {
+ $this->argument = strtr($this->argument, '-', ' ');
+ }
+
+ if (!empty($this->options['break_phrase'])) {
+ $this->breakPhraseString($this->argument, $this);
+ }
+ else {
+ $this->value = array($this->argument);
+ $this->operator = 'or';
+ }
+
+ if (empty($this->value)) {
+ return !empty($this->definition['empty field name']) ? $this->definition['empty field name'] : t('Uncategorized');
+ }
+
+ if ($this->value === array(-1)) {
+ return !empty($this->definition['invalid input']) ? $this->definition['invalid input'] : t('Invalid input');
+ }
+
+ return implode($this->operator == 'or' ? ' + ' : ', ', $this->title_query());
+ }
+
+ /**
+ * Override for specific title lookups.
+ */
+ function title_query() {
+ return drupal_map_assoc($this->value, 'check_plain');
+ }
+
+ function summary_name($data) {
+ return $this->caseTransform(parent::summary_name($data), $this->options['case']);
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/ArgumentDefaultPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/ArgumentDefaultPluginBase.php
new file mode 100644
index 0000000..c957e30
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/ArgumentDefaultPluginBase.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\argument_default;
+
+use Drupal\views\ViewExecutable;
+use Drupal\views\Plugin\views\PluginBase;
+
+/**
+ * @defgroup views_argument_default_plugins Views argument default plugins
+ * @{
+ * Allow specialized methods of filling in arguments when they aren't provided.
+ */
+
+/**
+ * The fixed argument default handler; also used as the base.
+ */
+abstract class ArgumentDefaultPluginBase extends PluginBase {
+
+ /**
+ * Return the default argument.
+ *
+ * This needs to be overridden by every default argument handler to properly do what is needed.
+ */
+ function get_argument() { }
+
+ /**
+ * Initialize this plugin with the view and the argument
+ * it is linked to.
+ */
+ public function init(ViewExecutable $view, &$argument, $options) {
+ $this->setOptionDefaults($this->options, $this->defineOptions());
+ $this->view = &$view;
+ $this->argument = &$argument;
+
+ $this->unpackOptions($this->options, $options);
+ }
+
+ /**
+ * Retrieve the options when this is a new access
+ * control plugin
+ */
+ protected function defineOptions() { return array(); }
+
+ /**
+ * Provide the default form for setting options.
+ */
+ public function buildOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Provide the default form form for validating options
+ */
+ public function validateOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Provide the default form form for submitting options
+ */
+ public function submitOptionsForm(&$form, &$form_state, &$options = array()) { }
+
+ /**
+ * Determine if the administrator has the privileges to use this
+ * plugin
+ */
+ public function access() { return TRUE; }
+
+ /**
+ * If we don't have access to the form but are showing it anyway, ensure that
+ * the form is safe and cannot be changed from user input.
+ *
+ * This is only called by child objects if specified in the buildOptionsForm(),
+ * so it will not always be used.
+ */
+ function check_access(&$form, $option_name) {
+ if (!$this->access()) {
+ $form[$option_name]['#disabled'] = TRUE;
+ $form[$option_name]['#value'] = $form[$this->option_name]['#default_value'];
+ $form[$option_name]['#description'] .= ' <strong>' . t('Note: you do not have permission to modify this. If you change the default filter type, this setting will be lost and you will NOT be able to get it back.') . '</strong>';
+ }
+ }
+
+}
+
+/**
+ * @}
+ */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Fixed.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Fixed.php
new file mode 100644
index 0000000..977a01c
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Fixed.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument_default\Fixed.
+ */
+
+namespace Drupal\views\Plugin\views\argument_default;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * The fixed argument default handler.
+ *
+ * @ingroup views_argument_default_plugins
+ *
+ * @Plugin(
+ * id = "fixed",
+ * title = @Translation("Fixed")
+ * )
+ */
+class Fixed extends ArgumentDefaultPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['argument'] = array('default' => '');
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ $form['argument'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Fixed value'),
+ '#default_value' => $this->options['argument'],
+ );
+ }
+
+ /**
+ * Return the default argument.
+ */
+ function get_argument() {
+ return $this->options['argument'];
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Php.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Php.php
new file mode 100644
index 0000000..7dc3a56
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Php.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument_default\Php.
+ */
+
+namespace Drupal\views\Plugin\views\argument_default;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Default argument plugin to provide a PHP code block.
+ *
+ * @ingroup views_argument_default_plugins
+ *
+ * @Plugin(
+ * id = "php",
+ * title = @Translation("PHP Code")
+ * )
+ */
+class Php extends ArgumentDefaultPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['code'] = array('default' => '');
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ $form['code'] = array(
+ '#type' => 'textarea',
+ '#title' => t('PHP contextual filter code'),
+ '#default_value' => $this->options['code'],
+ '#description' => t('Enter PHP code that returns a value to use for this filter. Do not use &lt;?php ?&gt;. You must return only a single value for just this filter. Some variables are available: the view object will be "$view". The argument handler will be "$argument", for example you may change the title used for substitutions for this argument by setting "argument->validated_title"".'),
+ );
+
+ // Only do this if using one simple standard form gadget
+ $this->check_access($form, 'code');
+ }
+
+ /**
+ * Only let users with PHP block visibility permissions set/modify this
+ * default plugin.
+ */
+ public function access() {
+ return user_access('use PHP for settings');
+ }
+
+ function get_argument() {
+ // set up variables to make it easier to reference during the argument.
+ $view = &$this->view;
+ $argument = &$this->argument;
+ ob_start();
+ $result = eval($this->options['code']);
+ ob_end_clean();
+ return $result;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Raw.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Raw.php
new file mode 100644
index 0000000..636e3f3
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Raw.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument_default\Raw.
+ */
+
+namespace Drupal\views\Plugin\views\argument_default;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Default argument plugin to use the raw value from the URL.
+ *
+ * @ingroup views_argument_default_plugins
+ *
+ * @Plugin(
+ * id = "raw",
+ * title = @Translation("Raw value from URL")
+ * )
+ */
+class Raw extends ArgumentDefaultPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['index'] = array('default' => '');
+ $options['use_alias'] = array('default' => FALSE, 'bool' => TRUE);
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ // Using range(1, 10) will create an array keyed 0-9, which allows arg() to
+ // properly function since it is also zero-based.
+ $form['index'] = array(
+ '#type' => 'select',
+ '#title' => t('Path component'),
+ '#default_value' => $this->options['index'],
+ '#options' => range(1, 10),
+ '#description' => t('The numbering starts from 1, e.g. on the page admin/structure/types, the 3rd path component is "types".'),
+ );
+ $form['use_alias'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Use path alias'),
+ '#default_value' => $this->options['use_alias'],
+ '#description' => t('Use path alias instead of internal path.'),
+ );
+ }
+
+ function get_argument() {
+ $path = NULL;
+ if ($this->options['use_alias']) {
+ $path = drupal_get_path_alias();
+ }
+ if ($arg = arg($this->options['index'], $path)) {
+ return $arg;
+ }
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php
new file mode 100644
index 0000000..9d9d92c
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument_validator\ArgumentValidatorPluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\argument_validator;
+
+use Drupal\views\ViewExecutable;
+use Drupal\views\Plugin\views\PluginBase;
+
+/**
+ * @defgroup views_argument_validate_plugins Views argument validate plugins
+ * @{
+ * Allow specialized methods of validating arguments.
+ */
+
+/**
+ * Base argument validator plugin to provide basic functionality.
+ */
+abstract class ArgumentValidatorPluginBase extends PluginBase {
+
+ /**
+ * Initialize this plugin with the view and the argument
+ * it is linked to.
+ */
+ public function init(ViewExecutable $view, &$argument, $options) {
+ $this->setOptionDefaults($this->options, $this->defineOptions());
+ $this->view = &$view;
+ $this->argument = &$argument;
+
+ $this->unpackOptions($this->options, $options);
+ }
+
+ /**
+ * Retrieve the options when this is a new access
+ * control plugin
+ */
+ protected function defineOptions() { return array(); }
+
+ /**
+ * Provide the default form for setting options.
+ */
+ public function buildOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Provide the default form form for validating options
+ */
+ public function validateOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Provide the default form form for submitting options
+ */
+ public function submitOptionsForm(&$form, &$form_state, &$options = array()) { }
+
+ /**
+ * Determine if the administrator has the privileges to use this plugin
+ */
+ public function access() { return TRUE; }
+
+ /**
+ * If we don't have access to the form but are showing it anyway, ensure that
+ * the form is safe and cannot be changed from user input.
+ *
+ * This is only called by child objects if specified in the buildOptionsForm(),
+ * so it will not always be used.
+ */
+ function check_access(&$form, $option_name) {
+ if (!$this->access()) {
+ $form[$option_name]['#disabled'] = TRUE;
+ $form[$option_name]['#value'] = $form[$this->option_name]['#default_value'];
+ $form[$option_name]['#description'] .= ' <strong>' . t('Note: you do not have permission to modify this. If you change the default filter type, this setting will be lost and you will NOT be able to get it back.') . '</strong>';
+ }
+ }
+
+ function validate_argument($arg) { return TRUE; }
+
+ /**
+ * Process the summary arguments for displaying.
+ *
+ * Some plugins alter the argument so it uses something else interal.
+ * For example the user validation set's the argument to the uid,
+ * for a faster query. But there are use cases where you want to use
+ * the old value again, for example the summary.
+ */
+ function process_summary_arguments(&$args) { }
+
+}
+
+/**
+ * @}
+ */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/None.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/None.php
new file mode 100644
index 0000000..f15fc11
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/None.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument_validator\None.
+ */
+
+namespace Drupal\views\Plugin\views\argument_validator;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Do not validate the argument.
+ *
+ * @ingroup views_argument_validate_plugins
+ *
+ * @Plugin(
+ * id = "none",
+ * title = @Translation(" - Basic validation - ")
+ * )
+ */
+class None extends ArgumentValidatorPluginBase {
+
+ function validate_argument($argument) {
+ if (!empty($this->argument->options['must_not_be'])) {
+ return !isset($argument);
+ }
+
+ if (!isset($argument) || $argument === '') {
+ return FALSE;
+ }
+
+ if (!empty($this->argument->definition['numeric']) && !isset($this->argument->options['break_phrase']) && !is_numeric($arg)) {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Numeric.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Numeric.php
new file mode 100644
index 0000000..fae745d
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Numeric.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument_validator\Numeric.
+ */
+
+namespace Drupal\views\Plugin\views\argument_validator;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Validate whether an argument is numeric or not.
+ *
+ * @ingroup views_argument_validate_plugins
+ *
+ * @Plugin(
+ * id = "numeric",
+ * title = @Translation("Numeric")
+ * )
+ */
+class Numeric extends ArgumentValidatorPluginBase {
+
+ function validate_argument($argument) {
+ return is_numeric($argument);
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Php.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Php.php
new file mode 100644
index 0000000..798533f
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Php.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\argument_validator\Php.
+ */
+
+namespace Drupal\views\Plugin\views\argument_validator;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Provide PHP code to validate whether or not an argument is ok.
+ *
+ * @ingroup views_argument_validate_plugins
+ *
+ * @Plugin(
+ * id = "php",
+ * title = @Translation("PHP Code")
+ * )
+ */
+class Php extends ArgumentValidatorPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['code'] = array('default' => '');
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ $form['code'] = array(
+ '#type' => 'textarea',
+ '#title' => t('PHP validate code'),
+ '#default_value' => $this->options['code'],
+ '#description' => t('Enter PHP code that returns TRUE or FALSE. No return is the same as FALSE, so be SURE to return something if you do not want to declare the argument invalid. Do not use &lt;?php ?&gt;. The argument to validate will be "$argument" and the view will be "$view". You may change the argument by setting "$handler->argument". You may change the title used for substitutions for this argument by setting "$handler->validated_title".'),
+ );
+
+ $this->check_access($form, 'code');
+ }
+
+ /**
+ * Only let users with PHP block visibility permissions set/modify this
+ * validate plugin.
+ */
+ public function access() {
+ return user_access('use PHP for settings');
+ }
+
+ function validate_argument($argument) {
+ // set up variables to make it easier to reference during the argument.
+ $view = &$this->view;
+ $handler = &$this->argument;
+
+ ob_start();
+ $result = eval($this->options['code']);
+ ob_end_clean();
+ return $result;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php
new file mode 100644
index 0000000..400fd0c
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php
@@ -0,0 +1,361 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\cache\CachePluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\cache;
+
+use Drupal\views\ViewExecutable;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\views\Plugin\views\PluginBase;
+use Drupal\Core\Database\Query\Select;
+
+/**
+ * @defgroup views_cache_plugins Views cache plugins
+ * @{
+ * The base plugin to handler caching of a view.
+ *
+ * Cache plugins can handle both caching of just the database result and
+ * the rendered output of the view.
+ */
+
+/**
+ * The base plugin to handle caching.
+ */
+abstract class CachePluginBase extends PluginBase {
+
+ /**
+ * Contains all data that should be written/read from cache.
+ */
+ var $storage = array();
+
+ /**
+ * What table to store data in.
+ */
+ var $table = 'views_results';
+
+ /**
+ * Stores the cache ID used for the results cache.
+ *
+ * The cache ID is stored in generateResultsKey() got executed.
+ *
+ * @var string
+ *
+ * @see Drupal\views\Plugin\views\cache\CachePluginBase::generateResultsKey()
+ */
+ protected $resultsKey;
+
+ /**
+ * Stores the cache ID used for the output cache, once generateOutputKey() got
+ * executed.
+ *
+ * @var string
+ *
+ * @see Drupal\views\Plugin\views\cache\CachePluginBase::generateOutputKey()
+ */
+ protected $outputKey;
+
+ /**
+ * Initialize the plugin.
+ *
+ * @param $view
+ * The view object.
+ * @param $display
+ * The display handler.
+ */
+ public function init(ViewExecutable $view, &$display, $options = NULL) {
+ $this->setOptionDefaults($this->options, $this->defineOptions());
+ $this->view = &$view;
+ $this->displayHandler = &$display;
+
+ $this->unpackOptions($this->options, $options);
+ }
+
+ /**
+ * Returns the outputKey property.
+ *
+ * @return string
+ * The outputKey property.
+ */
+ public function getOutputKey() {
+ return $this->outputKey;
+ }
+
+ /**
+ * Returns the resultsKey property.
+ *
+ * @return string
+ * The resultsKey property.
+ */
+ public function getResultsKey() {
+ return $this->resultsKey;
+ }
+
+ /**
+ * Return a string to display as the clickable title for the
+ * access control.
+ */
+ public function summaryTitle() {
+ return t('Unknown');
+ }
+
+ /**
+ * Determine the expiration time of the cache type, or NULL if no expire.
+ *
+ * Plugins must override this to implement expiration.
+ *
+ * @param $type
+ * The cache type, either 'query', 'result' or 'output'.
+ */
+ function cache_expire($type) { }
+
+ /**
+ * Determine expiration time in the cache table of the cache type
+ * or CACHE_PERMANENT if item shouldn't be removed automatically from cache.
+ *
+ * Plugins must override this to implement expiration in the cache table.
+ *
+ * @param $type
+ * The cache type, either 'query', 'result' or 'output'.
+ */
+ function cache_set_expire($type) {
+ return CacheBackendInterface::CACHE_PERMANENT;
+ }
+
+
+ /**
+ * Save data to the cache.
+ *
+ * A plugin should override this to provide specialized caching behavior.
+ */
+ function cache_set($type) {
+ switch ($type) {
+ case 'query':
+ // Not supported currently, but this is certainly where we'd put it.
+ break;
+ case 'results':
+ $data = array(
+ 'result' => $this->view->result,
+ 'total_rows' => isset($this->view->total_rows) ? $this->view->total_rows : 0,
+ 'current_page' => $this->view->getCurrentPage(),
+ );
+ cache($this->table)->set($this->generateResultsKey(), $data, $this->cache_set_expire($type));
+ break;
+ case 'output':
+ $this->gather_headers();
+ $this->storage['output'] = $this->view->display_handler->output;
+ cache($this->table)->set($this->generateOutputKey(), $this->storage, $this->cache_set_expire($type));
+ break;
+ }
+ }
+
+
+ /**
+ * Retrieve data from the cache.
+ *
+ * A plugin should override this to provide specialized caching behavior.
+ */
+ function cache_get($type) {
+ $cutoff = $this->cache_expire($type);
+ switch ($type) {
+ case 'query':
+ // Not supported currently, but this is certainly where we'd put it.
+ return FALSE;
+ case 'results':
+ // Values to set: $view->result, $view->total_rows, $view->execute_time,
+ // $view->current_page.
+ if ($cache = cache($this->table)->get($this->generateResultsKey())) {
+ if (!$cutoff || $cache->created > $cutoff) {
+ $this->view->result = $cache->data['result'];
+ $this->view->total_rows = $cache->data['total_rows'];
+ $this->view->setCurrentPage($cache->data['current_page']);
+ $this->view->execute_time = 0;
+ return TRUE;
+ }
+ }
+ return FALSE;
+ case 'output':
+ if ($cache = cache($this->table)->get($this->generateOutputKey())) {
+ if (!$cutoff || $cache->created > $cutoff) {
+ $this->storage = $cache->data;
+ $this->view->display_handler->output = $cache->data['output'];
+ $this->restore_headers();
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ }
+
+ /**
+ * Clear out cached data for a view.
+ *
+ * We're just going to nuke anything related to the view, regardless of display,
+ * to be sure that we catch everything. Maybe that's a bad idea.
+ */
+ function cache_flush() {
+ cache($this->table)->invalidateTags(array($this->view->storage->name => TRUE));
+ }
+
+ /**
+ * Post process any rendered data.
+ *
+ * This can be valuable to be able to cache a view and still have some level of
+ * dynamic output. In an ideal world, the actual output will include HTML
+ * comment based tokens, and then the post process can replace those tokens.
+ *
+ * Example usage. If it is known that the view is a node view and that the
+ * primary field will be a nid, you can do something like this:
+ *
+ * <!--post-FIELD-NID-->
+ *
+ * And then in the post render, create an array with the text that should
+ * go there:
+ *
+ * strtr($output, array('<!--post-FIELD-1-->', 'output for FIELD of nid 1');
+ *
+ * All of the cached result data will be available in $view->result, as well,
+ * so all ids used in the query should be discoverable.
+ */
+ function post_render(&$output) { }
+
+ /**
+ * Start caching javascript, css and other out of band info.
+ *
+ * This takes a snapshot of the current system state so that we don't
+ * duplicate it. Later on, when gather_headers() is run, this information
+ * will be removed so that we don't hold onto it.
+ */
+ function cache_start() {
+ $this->storage['head'] = drupal_add_html_head();
+ $this->storage['css'] = drupal_add_css();
+ $this->storage['js'] = drupal_add_js();
+ }
+
+ /**
+ * Gather out of band data, compare it to what we started with and store the difference.
+ */
+ function gather_headers() {
+ // Simple replacement for head
+ if (isset($this->storage['head'])) {
+ $this->storage['head'] = str_replace($this->storage['head'], '', drupal_add_html_head());
+ }
+ else {
+ $this->storage['head'] = '';
+ }
+
+ // Slightly less simple for CSS:
+ $css = drupal_add_css();
+ $css_start = isset($this->storage['css']) ? $this->storage['css'] : array();
+ $this->storage['css'] = array_diff_assoc($css, $css_start);
+
+ // Get javascript after/before views renders.
+ $js = drupal_add_js();
+ $js_start = isset($this->storage['js']) ? $this->storage['js'] : array();
+ // If there are any differences between the old and the new javascript then
+ // store them to be added later.
+ $this->storage['js'] = array_diff_assoc($js, $js_start);
+
+ // Special case the settings key and get the difference of the data.
+ $settings = isset($js['settings']['data']) ? $js['settings']['data'] : array();
+ $settings_start = isset($js_start['settings']['data']) ? $js_start['settings']['data'] : array();
+ $this->storage['js']['settings'] = array_diff_assoc($settings, $settings_start);
+ }
+
+ /**
+ * Restore out of band data saved to cache. Copied from Panels.
+ */
+ function restore_headers() {
+ if (!empty($this->storage['head'])) {
+ drupal_add_html_head($this->storage['head']);
+ }
+ if (!empty($this->storage['css'])) {
+ foreach ($this->storage['css'] as $args) {
+ drupal_add_css($args['data'], $args);
+ }
+ }
+ if (!empty($this->storage['js'])) {
+ foreach ($this->storage['js'] as $key => $args) {
+ if ($key !== 'settings') {
+ drupal_add_js($args['data'], $args);
+ }
+ else {
+ foreach ($args as $setting) {
+ drupal_add_js($setting, 'setting');
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Calculates and sets a cache ID used for the result cache.
+ *
+ * @return string
+ * The generated cache ID.
+ */
+ public function generateResultsKey() {
+ global $user;
+
+ if (!isset($this->resultsKey)) {
+ $build_info = $this->view->build_info;
+
+ foreach (array('query', 'count_query') as $index) {
+ // If the default query back-end is used generate SQL query strings from
+ // the query objects.
+ if ($build_info[$index] instanceof Select) {
+ $query = clone $build_info[$index];
+ $query->preExecute();
+ $build_info[$index] = (string)$query;
+ }
+ }
+ $key_data = array(
+ 'build_info' => $build_info,
+ 'roles' => array_keys($user->roles),
+ 'super-user' => $user->uid == 1, // special caching for super user.
+ 'langcode' => language(LANGUAGE_TYPE_INTERFACE)->langcode,
+ 'base_url' => $GLOBALS['base_url'],
+ );
+ foreach (array('exposed_info', 'page', 'sort', 'order', 'items_per_page', 'offset') as $key) {
+ if (isset($_GET[$key])) {
+ $key_data[$key] = $_GET[$key];
+ }
+ }
+
+ $this->resultsKey = $this->view->storage->name . ':' . $this->displayHandler->display['id'] . ':results:' . md5(serialize($key_data));
+ }
+
+ return $this->resultsKey;
+ }
+
+ /**
+ * Calculates and sets a cache ID used for the output cache.
+ *
+ * @return string
+ * The generated cache ID.
+ */
+ public function generateOutputKey() {
+ global $user;
+ if (!isset($this->outputKey)) {
+ $key_data = array(
+ 'result' => $this->view->result,
+ 'roles' => array_keys($user->roles),
+ 'super-user' => $user->uid == 1, // special caching for super user.
+ 'theme' => $GLOBALS['theme'],
+ 'langcode' => language(LANGUAGE_TYPE_INTERFACE)->langcode,
+ 'base_url' => $GLOBALS['base_url'],
+ );
+
+ $this->outputKey = $this->view->storage->name . ':' . $this->displayHandler->display['id'] . ':output:' . md5(serialize($key_data));
+ }
+
+ return $this->outputKey;
+ }
+
+}
+
+/**
+ * @}
+ */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/cache/None.php b/core/modules/views/lib/Drupal/views/Plugin/views/cache/None.php
new file mode 100644
index 0000000..89e1703
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/cache/None.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\cache\None.
+ */
+
+namespace Drupal\views\Plugin\views\cache;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Caching plugin that provides no caching at all.
+ *
+ * @ingroup views_cache_plugins
+ *
+ * @Plugin(
+ * id = "none",
+ * title = @Translation("None"),
+ * help = @Translation("No caching of Views data.")
+ * )
+ */
+class None extends CachePluginBase {
+
+ function cache_start() { /* do nothing */ }
+
+ public function summaryTitle() {
+ return t('None');
+ }
+
+ function cache_get($type) {
+ return FALSE;
+ }
+
+ function cache_set($type) { }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/cache/Time.php b/core/modules/views/lib/Drupal/views/Plugin/views/cache/Time.php
new file mode 100644
index 0000000..aae760d
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/cache/Time.php
@@ -0,0 +1,131 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\cache\Time.
+ */
+
+namespace Drupal\views\Plugin\views\cache;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Simple caching of query results for Views displays.
+ *
+ * @ingroup views_cache_plugins
+ *
+ * @Plugin(
+ * id = "time",
+ * title = @Translation("Time-based"),
+ * help = @Translation("Simple time-based caching of data.")
+ * )
+ */
+class Time extends CachePluginBase {
+
+ /**
+ * Overrides Drupal\views\Plugin\Plugin::$usesOptions.
+ */
+ protected $usesOptions = TRUE;
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['results_lifespan'] = array('default' => 3600);
+ $options['results_lifespan_custom'] = array('default' => 0);
+ $options['output_lifespan'] = array('default' => 3600);
+ $options['output_lifespan_custom'] = array('default' => 0);
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ $options = array(60, 300, 1800, 3600, 21600, 518400);
+ $options = drupal_map_assoc($options, 'format_interval');
+ $options = array(-1 => t('Never cache')) + $options + array('custom' => t('Custom'));
+
+ $form['results_lifespan'] = array(
+ '#type' => 'select',
+ '#title' => t('Query results'),
+ '#description' => t('The length of time raw query results should be cached.'),
+ '#options' => $options,
+ '#default_value' => $this->options['results_lifespan'],
+ );
+ $form['results_lifespan_custom'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Seconds'),
+ '#size' => '25',
+ '#maxlength' => '30',
+ '#description' => t('Length of time in seconds raw query results should be cached.'),
+ '#default_value' => $this->options['results_lifespan_custom'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="cache_options[results_lifespan]"]' => array('value' => 'custom'),
+ ),
+ ),
+ );
+ $form['output_lifespan'] = array(
+ '#type' => 'select',
+ '#title' => t('Rendered output'),
+ '#description' => t('The length of time rendered HTML output should be cached.'),
+ '#options' => $options,
+ '#default_value' => $this->options['output_lifespan'],
+ );
+ $form['output_lifespan_custom'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Seconds'),
+ '#size' => '25',
+ '#maxlength' => '30',
+ '#description' => t('Length of time in seconds rendered HTML output should be cached.'),
+ '#default_value' => $this->options['output_lifespan_custom'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="cache_options[output_lifespan]"]' => array('value' => 'custom'),
+ ),
+ ),
+ );
+ }
+
+ public function validateOptionsForm(&$form, &$form_state) {
+ $custom_fields = array('output_lifespan', 'results_lifespan');
+ foreach ($custom_fields as $field) {
+ if ($form_state['values']['cache_options'][$field] == 'custom' && !is_numeric($form_state['values']['cache_options'][$field . '_custom'])) {
+ form_error($form[$field .'_custom'], t('Custom time values must be numeric.'));
+ }
+ }
+ }
+
+ public function summaryTitle() {
+ $results_lifespan = $this->get_lifespan('results');
+ $output_lifespan = $this->get_lifespan('output');
+ return format_interval($results_lifespan, 1) . '/' . format_interval($output_lifespan, 1);
+ }
+
+ function get_lifespan($type) {
+ $lifespan = $this->options[$type . '_lifespan'] == 'custom' ? $this->options[$type . '_lifespan_custom'] : $this->options[$type . '_lifespan'];
+ return $lifespan;
+ }
+
+ function cache_expire($type) {
+ $lifespan = $this->get_lifespan($type);
+ if ($lifespan) {
+ $cutoff = REQUEST_TIME - $lifespan;
+ return $cutoff;
+ }
+ else {
+ return FALSE;
+ }
+ }
+
+ function cache_set_expire($type) {
+ $lifespan = $this->get_lifespan($type);
+ if ($lifespan) {
+ return time() + $lifespan;
+ }
+ else {
+ return CacheBackendInterface::CACHE_PERMANENT;
+ }
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Attachment.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Attachment.php
new file mode 100644
index 0000000..a0f6a2f
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Attachment.php
@@ -0,0 +1,299 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\display\Attachment.
+ */
+
+namespace Drupal\views\Plugin\views\display;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * The plugin that handles an attachment display.
+ *
+ * Attachment displays are secondary displays that are 'attached' to a primary
+ * display. Effectively they are a simple way to get multiple views within
+ * the same view. They can share some information.
+ *
+ * @ingroup views_display_plugins
+ *
+ * @Plugin(
+ * id = "attachment",
+ * title = @Translation("Attachment"),
+ * help = @Translation("Attachments added to other displays to achieve multiple views in the same view."),
+ * theme = "views_view",
+ * contextual_links_locations = {""}
+ * )
+ */
+class Attachment extends DisplayPluginBase {
+
+ /**
+ * Whether the display allows the use of a pager or not.
+ *
+ * @var bool
+ */
+ protected $usesPager = FALSE;
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['displays'] = array('default' => array());
+ $options['attachment_position'] = array('default' => 'before');
+ $options['inherit_arguments'] = array('default' => TRUE, 'bool' => TRUE);
+ $options['inherit_exposed_filters'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['inherit_pager'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['render_pager'] = array('default' => FALSE, 'bool' => TRUE);
+
+ return $options;
+ }
+
+ public function execute() {
+ return $this->view->render($this->display['id']);
+ }
+
+ public function attachmentPositions($position = NULL) {
+ $positions = array(
+ 'before' => t('Before'),
+ 'after' => t('After'),
+ 'both' => t('Both'),
+ );
+
+ if ($position) {
+ return $positions[$position];
+ }
+
+ return $positions;
+ }
+
+ /**
+ * Provide the summary for attachment options in the views UI.
+ *
+ * This output is returned as an array.
+ */
+ public function optionsSummary(&$categories, &$options) {
+ // It is very important to call the parent function here:
+ parent::optionsSummary($categories, $options);
+
+ $categories['attachment'] = array(
+ 'title' => t('Attachment settings'),
+ 'column' => 'second',
+ 'build' => array(
+ '#weight' => -10,
+ ),
+ );
+
+ $displays = array_filter($this->getOption('displays'));
+ if (count($displays) > 1) {
+ $attach_to = t('Multiple displays');
+ }
+ elseif (count($displays) == 1) {
+ $display = array_shift($displays);
+ if (!empty($this->view->display[$display])) {
+ $attach_to = check_plain($this->view->display[$display]['display_title']);
+ }
+ }
+
+ if (!isset($attach_to)) {
+ $attach_to = t('Not defined');
+ }
+
+ $options['displays'] = array(
+ 'category' => 'attachment',
+ 'title' => t('Attach to'),
+ 'value' => $attach_to,
+ );
+
+ $options['attachment_position'] = array(
+ 'category' => 'attachment',
+ 'title' => t('Attachment position'),
+ 'value' => $this->attachmentPositions($this->getOption('attachment_position')),
+ );
+
+ $options['inherit_arguments'] = array(
+ 'category' => 'attachment',
+ 'title' => t('Inherit contextual filters'),
+ 'value' => $this->getOption('inherit_arguments') ? t('Yes') : t('No'),
+ );
+
+ $options['inherit_exposed_filters'] = array(
+ 'category' => 'attachment',
+ 'title' => t('Inherit exposed filters'),
+ 'value' => $this->getOption('inherit_exposed_filters') ? t('Yes') : t('No'),
+ );
+
+ $options['inherit_pager'] = array(
+ 'category' => 'pager',
+ 'title' => t('Inherit pager'),
+ 'value' => $this->getOption('inherit_pager') ? t('Yes') : t('No'),
+ );
+
+ $options['render_pager'] = array(
+ 'category' => 'pager',
+ 'title' => t('Render pager'),
+ 'value' => $this->getOption('render_pager') ? t('Yes') : t('No'),
+ );
+
+ }
+
+ /**
+ * Provide the default form for setting options.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ // It is very important to call the parent function here:
+ parent::buildOptionsForm($form, $form_state);
+
+ switch ($form_state['section']) {
+ case 'inherit_arguments':
+ $form['#title'] .= t('Inherit contextual filters');
+ $form['inherit_arguments'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Inherit'),
+ '#description' => t('Should this display inherit its contextual filter values from the parent display to which it is attached?'),
+ '#default_value' => $this->getOption('inherit_arguments'),
+ );
+ break;
+ case 'inherit_exposed_filters':
+ $form['#title'] .= t('Inherit exposed filters');
+ $form['inherit_exposed_filters'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Inherit'),
+ '#description' => t('Should this display inherit its exposed filter values from the parent display to which it is attached?'),
+ '#default_value' => $this->getOption('inherit_exposed_filters'),
+ );
+ break;
+ case 'inherit_pager':
+ $form['#title'] .= t('Inherit pager');
+ $form['inherit_pager'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Inherit'),
+ '#description' => t('Should this display inherit its paging values from the parent display to which it is attached?'),
+ '#default_value' => $this->getOption('inherit_pager'),
+ );
+ break;
+ case 'render_pager':
+ $form['#title'] .= t('Render pager');
+ $form['render_pager'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Render'),
+ '#description' => t('Should this display render the pager values? This is only meaningful if inheriting a pager.'),
+ '#default_value' => $this->getOption('render_pager'),
+ );
+ break;
+ case 'attachment_position':
+ $form['#title'] .= t('Position');
+ $form['attachment_position'] = array(
+ '#type' => 'radios',
+ '#description' => t('Attach before or after the parent display?'),
+ '#options' => $this->attachmentPositions(),
+ '#default_value' => $this->getOption('attachment_position'),
+ );
+ break;
+ case 'displays':
+ $form['#title'] .= t('Attach to');
+ $displays = array();
+ foreach ($this->view->display as $display_id => $display) {
+ if (!empty($this->view->displayHandlers[$display_id]) && $this->view->displayHandlers[$display_id]->acceptAttachments()) {
+ $displays[$display_id] = $display['display_title'];
+ }
+ }
+ $form['displays'] = array(
+ '#type' => 'checkboxes',
+ '#description' => t('Select which display or displays this should attach to.'),
+ '#options' => $displays,
+ '#default_value' => $this->getOption('displays'),
+ );
+ break;
+ }
+ }
+
+ /**
+ * Perform any necessary changes to the form values prior to storage.
+ * There is no need for this function to actually store the data.
+ */
+ public function submitOptionsForm(&$form, &$form_state) {
+ // It is very important to call the parent function here:
+ parent::submitOptionsForm($form, $form_state);
+ switch ($form_state['section']) {
+ case 'inherit_arguments':
+ case 'inherit_pager':
+ case 'render_pager':
+ case 'inherit_exposed_filters':
+ case 'attachment_position':
+ case 'displays':
+ $this->setOption($form_state['section'], $form_state['values'][$form_state['section']]);
+ break;
+ }
+ }
+
+ /**
+ * Attach to another view.
+ */
+ public function attachTo($display_id) {
+ $displays = $this->getOption('displays');
+
+ if (empty($displays[$display_id])) {
+ return;
+ }
+
+ if (!$this->access()) {
+ return;
+ }
+
+ // Get a fresh view because our current one has a lot of stuff on it because it's
+ // already been executed.
+ $view = $this->view->cloneView();
+
+ $args = $this->getOption('inherit_arguments') ? $this->view->args : array();
+ $view->setArguments($args);
+ $view->setDisplay($this->display['id']);
+ if ($this->getOption('inherit_pager')) {
+ $view->display_handler->usesPager = $this->view->display[$display_id]->handler->usesPager();
+ $view->display_handler->setOption('pager', $this->view->display[$display_id]->handler->getOption('pager'));
+ }
+
+ $attachment = $view->executeDisplay($this->display['id'], $args);
+
+ switch ($this->getOption('attachment_position')) {
+ case 'before':
+ $this->view->attachment_before .= $attachment;
+ break;
+ case 'after':
+ $this->view->attachment_after .= $attachment;
+ break;
+ case 'both':
+ $this->view->attachment_before .= $attachment;
+ $this->view->attachment_after .= $attachment;
+ break;
+ }
+
+ $view->destroy();
+ }
+
+ /**
+ * Attachment displays only use exposed widgets if
+ * they are set to inherit the exposed filter settings
+ * of their parent display.
+ */
+ public function usesExposed() {
+ if (!empty($this->options['inherit_exposed_filters']) && parent::usesExposed()) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * If an attachment is set to inherit the exposed filter
+ * settings from its parent display, then don't render and
+ * display a second set of exposed filter widgets.
+ */
+ public function displaysExposed() {
+ return $this->options['inherit_exposed_filters'] ? FALSE : TRUE;
+ }
+
+ public function renderPager() {
+ return $this->usesPager() && $this->getOption('render_pager');
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DefaultDisplay.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DefaultDisplay.php
new file mode 100644
index 0000000..c739973
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DefaultDisplay.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\display\DefaultDisplay.
+ */
+
+namespace Drupal\views\Plugin\views\display;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * A plugin to handle defaults on a view.
+ *
+ * @ingroup views_display_plugins
+ *
+ * @Plugin(
+ * id = "default",
+ * title = @Translation("Master"),
+ * help = @Translation("Default settings for this view."),
+ * theme = "views_view",
+ * no_ui = TRUE
+ * )
+ */
+class DefaultDisplay extends DisplayPluginBase {
+
+ /**
+ * Whether the display allows attachments.
+ *
+ * @var bool
+ */
+ protected $usesAttachments = TRUE;
+
+ /**
+ * Determine if this display is the 'default' display which contains
+ * fallback settings
+ */
+ public function isDefaultDisplay() { return TRUE; }
+
+ /**
+ * The default execute handler fully renders the view.
+ *
+ * For the simplest use:
+ * @code
+ * $output = $view->executeDisplay('default', $args);
+ * @endcode
+ *
+ * For more complex usages, a view can be partially built:
+ * @code
+ * $view->setArguments($args);
+ * $view->build('default'); // Build the query
+ * $view->preExecute(); // Pre-execute the query.
+ * $view->execute(); // Run the query
+ * $output = $view->render(); // Render the view
+ * @endcode
+ *
+ * If short circuited at any point, look in $view->build_info for
+ * information about the query. After execute, look in $view->result
+ * for the array of objects returned from db_query.
+ *
+ * You can also do:
+ * @code
+ * $view->setArguments($args);
+ * $output = $view->render('default'); // Render the view
+ * @endcode
+ *
+ * This illustrates that render is smart enough to call build and execute
+ * if these items have not already been accomplished.
+ *
+ * Note that execute also must accomplish other tasks, such
+ * as setting page titles, breadcrumbs, and generating exposed filter
+ * data if necessary.
+ */
+ public function execute() {
+ return $this->view->render($this->display['id']);
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
new file mode 100644
index 0000000..ed22ea5
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -0,0 +1,2717 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\display\DisplayPluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\display;
+
+use Drupal\views\ViewExecutable;
+use Drupal\views\Plugin\views\PluginBase;
+
+/**
+ * @defgroup views_display_plugins Views display plugins
+ * @{
+ * Display plugins control how Views interact with the rest of Drupal.
+ *
+ * They can handle creating Views from a Drupal page hook; they can
+ * handle creating Views from a Drupal block hook. They can also
+ * handle creating Views from an external module source.
+ */
+
+/**
+ * The default display plugin handler. Display plugins handle options and
+ * basic mechanisms for different output methods.
+ */
+abstract class DisplayPluginBase extends PluginBase {
+
+ /**
+ * The top object of a view.
+ *
+ * @var Drupal\views\ViewExecutable
+ */
+ var $view = NULL;
+
+ var $handlers = array();
+
+ /**
+ * An array of instantiated plugins used in this display.
+ *
+ * @var array
+ */
+ protected $plugins = array();
+
+ /**
+ * Stores all available display extenders.
+ */
+ var $extender = array();
+
+ /**
+ * Overrides Drupal\views\Plugin\Plugin::$usesOptions.
+ */
+ protected $usesOptions = TRUE;
+
+ /**
+ * Stores the rendered output of the display.
+ *
+ * @see View::render
+ * @var string
+ */
+ public $output = NULL;
+
+ /**
+ * Whether the display allows the use of AJAX or not.
+ *
+ * @var bool
+ */
+ protected $usesAJAX = TRUE;
+
+ /**
+ * Whether the display allows the use of a pager or not.
+ *
+ * @var bool
+ */
+ protected $usesPager = TRUE;
+
+ /**
+ * Whether the display allows the use of a 'more' link or not.
+ *
+ * @var bool
+ */
+ protected $usesMore = TRUE;
+
+ /**
+ * Whether the display allows attachments.
+ *
+ * @var bool
+ * TRUE if the display can use attachments, or FALSE otherwise.
+ */
+ protected $usesAttachments = FALSE;
+
+ public function init(ViewExecutable $view, &$display, $options = NULL) {
+ $this->setOptionDefaults($this->options, $this->defineOptions());
+ $this->view = &$view;
+ $this->display = &$display;
+
+ // Load extenders as soon as possible.
+ $this->extender = array();
+ $extenders = views_get_enabled_display_extenders();
+ if (!empty($extenders)) {
+ foreach ($extenders as $extender) {
+ $plugin = views_get_plugin('display_extender', $extender);
+ if ($plugin) {
+ $plugin->init($this->view, $this);
+ $this->extender[$extender] = $plugin;
+ }
+ }
+ }
+
+ // Track changes that the user should know about.
+ $changed = FALSE;
+
+ // Make some modifications:
+ if (!isset($options) && isset($display['display_options'])) {
+ $options = $display['display_options'];
+ }
+
+ if ($this->isDefaultDisplay() && isset($options['defaults'])) {
+ unset($options['defaults']);
+ }
+
+ views_include('cache');
+ // Cache for unpackOptions, but not if we are in the ui.
+ static $unpack_options = array();
+ if (empty($view->editing)) {
+ $cid = 'unpackOptions:' . md5(serialize(array($this->options, $options)));
+ if (empty($unpack_options[$cid])) {
+ $cache = views_cache_get($cid, TRUE);
+ if (!empty($cache->data)) {
+ $this->options = $cache->data;
+ }
+ else {
+ $this->unpackOptions($this->options, $options);
+ views_cache_set($cid, $this->options, TRUE);
+ }
+ $unpack_options[$cid] = $this->options;
+ }
+ else {
+ $this->options = $unpack_options[$cid];
+ }
+ }
+ else {
+ $this->unpackOptions($this->options, $options);
+ }
+
+ // Convert the field_language and field_language_add_to_query settings.
+ $field_language = $this->getOption('field_language');
+ $field_language_add_to_query = $this->getOption('field_language_add_to_query');
+ if (isset($field_langcode)) {
+ $this->setOption('field_langcode', $field_language);
+ $this->setOption('field_langcode_add_to_query', $field_language_add_to_query);
+ $changed = TRUE;
+ }
+
+ // Mark the view as changed so the user has a chance to save it.
+ if ($changed) {
+ $this->view->changed = TRUE;
+ }
+ }
+
+ public function destroy() {
+ parent::destroy();
+
+ foreach ($this->handlers as $type => $handlers) {
+ foreach ($handlers as $id => $handler) {
+ if (is_object($handler)) {
+ $this->handlers[$type][$id]->destroy();
+ }
+ }
+ }
+
+ if (isset($this->default_display)) {
+ unset($this->default_display);
+ }
+
+ foreach ($this->extender as $extender) {
+ $extender->destroy();
+ }
+ }
+
+ /**
+ * Determine if this display is the 'default' display which contains
+ * fallback settings
+ */
+ public function isDefaultDisplay() { return FALSE; }
+
+ /**
+ * Determine if this display uses exposed filters, so the view
+ * will know whether or not to build them.
+ */
+ public function usesExposed() {
+ if (!isset($this->has_exposed)) {
+ foreach ($this->handlers as $type => $value) {
+ foreach ($this->view->$type as $id => $handler) {
+ if ($handler->canExpose() && $handler->isExposed()) {
+ // one is all we need; if we find it, return true.
+ $this->has_exposed = TRUE;
+ return TRUE;
+ }
+ }
+ }
+ $pager = $this->getPlugin('pager');
+ if (isset($pager) && $pager->uses_exposed()) {
+ $this->has_exposed = TRUE;
+ return TRUE;
+ }
+ $this->has_exposed = FALSE;
+ }
+
+ return $this->has_exposed;
+ }
+
+ /**
+ * Determine if this display should display the exposed
+ * filters widgets, so the view will know whether or not
+ * to render them.
+ *
+ * Regardless of what this function
+ * returns, exposed filters will not be used nor
+ * displayed unless usesExposed() returns TRUE.
+ */
+ public function displaysExposed() {
+ return TRUE;
+ }
+
+ /**
+ * Whether the display allows the use of AJAX or not.
+ *
+ * @return bool
+ */
+ public function usesAJAX() {
+ return $this->usesAJAX;
+ }
+
+ /**
+ * Whether the display is actually using AJAX or not.
+ *
+ * @return bool
+ */
+ public function isAJAXEnabled() {
+ if ($this->usesAJAX()) {
+ return $this->getOption('use_ajax');
+ }
+ return FALSE;
+ }
+
+ /**
+ * Whether the display is enabled.
+ *
+ * @return bool
+ * Returns TRUE if the display is marked as enabled, else FALSE.
+ */
+ public function isEnabled() {
+ return (bool) $this->getOption('enabled');
+ }
+
+ /**
+ * Whether the display allows the use of a pager or not.
+ *
+ * @return bool
+ */
+
+ public function usesPager() {
+ return $this->usesPager;
+ }
+
+ /**
+ * Whether the display is using a pager or not.
+ *
+ * @return bool
+ */
+ public function isPagerEnabled() {
+ if ($this->usesPager()) {
+ $pager = $this->getPlugin('pager');
+ if ($pager) {
+ return $pager->use_pager();
+ }
+ }
+ return FALSE;
+ }
+
+ /**
+ * Whether the display allows the use of a 'more' link or not.
+ *
+ * @return bool
+ */
+ public function usesMore() {
+ return $this->usesMore;
+ }
+
+ /**
+ * Whether the display is using the 'more' link or not.
+ *
+ * @return bool
+ */
+ public function isMoreEnabled() {
+ if ($this->usesMore()) {
+ return $this->getOption('use_more');
+ }
+ return FALSE;
+ }
+
+ /**
+ * Does the display have groupby enabled?
+ */
+ public function useGroupBy() {
+ return $this->getOption('group_by');
+ }
+
+ /**
+ * Should the enabled display more link be shown when no more items?
+ */
+ public function useMoreAlways() {
+ if ($this->usesMore()) {
+ return $this->getOption('useMoreAlways');
+ }
+ return FALSE;
+ }
+
+ /**
+ * Does the display have custom link text?
+ */
+ public function useMoreText() {
+ if ($this->usesMore()) {
+ return $this->getOption('useMoreText');
+ }
+ return FALSE;
+ }
+
+ /**
+ * Determines whether this display can use attachments.
+ *
+ * @return bool
+ */
+ public function acceptAttachments() {
+ if (!$this->usesAttachments()) {
+ return FALSE;
+ }
+ if (!empty($this->view->argument) && $this->getOption('hide_attachment_summary')) {
+ foreach ($this->view->argument as $argument_id => $argument) {
+ if ($argument->needsStylePlugin() && empty($argument->argument_validated)) {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+ }
+
+ /**
+ * Returns whether the display can use attachments.
+ *
+ * @return bool
+ */
+ public function usesAttachments() {
+ return $this->usesAttachments;
+ }
+
+ /**
+ * Allow displays to attach to other views.
+ */
+ public function attachTo($display_id) { }
+
+ /**
+ * Static member function to list which sections are defaultable
+ * and what items each section contains.
+ */
+ public function defaultableSections($section = NULL) {
+ $sections = array(
+ 'access' => array('access'),
+ 'cache' => array('cache'),
+ 'title' => array('title'),
+ 'css_class' => array('css_class'),
+ 'use_ajax' => array('use_ajax'),
+ 'hide_attachment_summary' => array('hide_attachment_summary'),
+ 'hide_admin_links' => array('hide_admin_links'),
+ 'group_by' => array('group_by'),
+ 'query' => array('query'),
+ 'use_more' => array('use_more', 'use_more_always', 'use_more_text'),
+ 'use_more_always' => array('use_more', 'use_more_always', 'use_more_text'),
+ 'use_more_text' => array('use_more', 'use_more_always', 'use_more_text'),
+ 'link_display' => array('link_display', 'link_url'),
+
+ // Force these to cascade properly.
+ 'style' => array('style', 'row'),
+ 'row' => array('style', 'row'),
+
+ 'pager' => array('pager', 'pager_options'),
+ 'pager_options' => array('pager', 'pager_options'),
+
+ 'exposed_form' => array('exposed_form', 'exposed_form_options'),
+ 'exposed_form_options' => array('exposed_form', 'exposed_form_options'),
+
+ // These guys are special
+ 'header' => array('header'),
+ 'footer' => array('footer'),
+ 'empty' => array('empty'),
+ 'relationships' => array('relationships'),
+ 'fields' => array('fields'),
+ 'sorts' => array('sorts'),
+ 'arguments' => array('arguments'),
+ 'filters' => array('filters', 'filter_groups'),
+ 'filter_groups' => array('filters', 'filter_groups'),
+ );
+
+ // If the display cannot use a pager, then we cannot default it.
+ if (!$this->usesPager()) {
+ unset($sections['pager']);
+ unset($sections['items_per_page']);
+ }
+
+ foreach ($this->extender as $extender) {
+ $extender->defaultableSections($sections, $section);
+ }
+
+ if ($section) {
+ if (!empty($sections[$section])) {
+ return $sections[$section];
+ }
+ }
+ else {
+ return $sections;
+ }
+ }
+
+ protected function defineOptions() {
+ $options = array(
+ 'defaults' => array(
+ 'default' => array(
+ 'access' => TRUE,
+ 'cache' => TRUE,
+ 'query' => TRUE,
+ 'title' => TRUE,
+ 'css_class' => TRUE,
+
+ 'display_description' => FALSE,
+ 'use_ajax' => TRUE,
+ 'hide_attachment_summary' => TRUE,
+ 'hide_admin_links' => FALSE,
+ 'pager' => TRUE,
+ 'use_more' => TRUE,
+ 'use_more_always' => TRUE,
+ 'use_more_text' => TRUE,
+ 'exposed_form' => TRUE,
+
+ 'link_display' => TRUE,
+ 'link_url' => '',
+ 'group_by' => TRUE,
+
+ 'style' => TRUE,
+ 'row' => TRUE,
+
+ 'header' => TRUE,
+ 'footer' => TRUE,
+ 'empty' => TRUE,
+
+ 'relationships' => TRUE,
+ 'fields' => TRUE,
+ 'sorts' => TRUE,
+ 'arguments' => TRUE,
+ 'filters' => TRUE,
+ 'filter_groups' => TRUE,
+ ),
+ ),
+
+ 'title' => array(
+ 'default' => '',
+ 'translatable' => TRUE,
+ ),
+ 'enabled' => array(
+ 'default' => TRUE,
+ 'translatable' => FALSE,
+ 'bool' => TRUE,
+ ),
+ 'display_comment' => array(
+ 'default' => '',
+ ),
+ 'css_class' => array(
+ 'default' => '',
+ 'translatable' => FALSE,
+ ),
+ 'display_description' => array(
+ 'default' => '',
+ 'translatable' => TRUE,
+ ),
+ 'use_ajax' => array(
+ 'default' => FALSE,
+ 'bool' => TRUE,
+ ),
+ 'hide_attachment_summary' => array(
+ 'default' => FALSE,
+ 'bool' => TRUE,
+ ),
+ 'hide_admin_links' => array(
+ 'default' => FALSE,
+ 'bool' => TRUE,
+ ),
+ 'use_more' => array(
+ 'default' => FALSE,
+ 'bool' => TRUE,
+ ),
+ 'use_more_always' => array(
+ 'default' => FALSE,
+ 'bool' => TRUE,
+ ),
+ 'use_more_text' => array(
+ 'default' => 'more',
+ 'translatable' => TRUE,
+ ),
+ 'link_display' => array(
+ 'default' => '',
+ ),
+ 'link_url' => array(
+ 'default' => '',
+ ),
+ 'group_by' => array(
+ 'default' => FALSE,
+ 'bool' => TRUE,
+ ),
+ 'field_langcode' => array(
+ 'default' => '***CURRENT_LANGUAGE***',
+ ),
+ 'field_langcode_add_to_query' => array(
+ 'default' => TRUE,
+ 'bool' => TRUE,
+ ),
+
+ // These types are all plugins that can have individual settings
+ // and therefore need special handling.
+ 'access' => array(
+ 'contains' => array(
+ 'type' => array('default' => 'none'),
+ 'options' => array('default' => array()),
+ ),
+ ),
+ 'cache' => array(
+ 'contains' => array(
+ 'type' => array('default' => 'none'),
+ 'options' => array('default' => array()),
+ ),
+ ),
+ 'query' => array(
+ 'contains' => array(
+ 'type' => array('default' => 'views_query'),
+ 'options' => array('default' => array()),
+ ),
+ ),
+ 'exposed_form' => array(
+ 'contains' => array(
+ 'type' => array('default' => 'basic'),
+ 'options' => array('default' => array()),
+ ),
+ ),
+ 'pager' => array(
+ 'contains' => array(
+ 'type' => array('default' => 'full'),
+ 'options' => array('default' => array()),
+ ),
+ ),
+ 'style' => array(
+ 'contains' => array(
+ 'type' => array('default' => 'default'),
+ 'options' => array('default' => array()),
+ ),
+ ),
+ 'row' => array(
+ 'contains' => array(
+ 'type' => array('default' => 'fields'),
+ 'options' => array('default' => array()),
+ ),
+ ),
+
+ 'exposed_block' => array(
+ 'default' => FALSE,
+ ),
+
+ 'header' => array(
+ 'default' => array(),
+ ),
+ 'footer' => array(
+ 'default' => array(),
+ ),
+ 'empty' => array(
+ 'default' => array(),
+ ),
+
+ // We want these to export last.
+ // These are the 5 handler types.
+ 'relationships' => array(
+ 'default' => array(),
+ ),
+ 'fields' => array(
+ 'default' => array(),
+ ),
+ 'sorts' => array(
+ 'default' => array(),
+ ),
+ 'arguments' => array(
+ 'default' => array(),
+ ),
+ 'filter_groups' => array(
+ 'contains' => array(
+ 'operator' => array('default' => 'AND'),
+ 'groups' => array('default' => array(1 => 'AND')),
+ ),
+ ),
+ 'filters' => array(
+ 'default' => array(),
+ ),
+ );
+
+ if (!$this->usesPager()) {
+ $options['defaults']['default']['use_pager'] = FALSE;
+ $options['defaults']['default']['items_per_page'] = FALSE;
+ $options['defaults']['default']['offset'] = FALSE;
+ $options['defaults']['default']['pager'] = FALSE;
+ $options['pager']['contains']['type']['default'] = 'some';
+ }
+
+ if ($this->isDefaultDisplay()) {
+ unset($options['defaults']);
+ }
+
+ foreach ($this->extender as $extender) {
+ $extender->defineOptionsAlter($options);
+ }
+
+ return $options;
+ }
+
+ /**
+ * Check to see if the display has a 'path' field.
+ *
+ * This is a pure function and not just a setting on the definition
+ * because some displays (such as a panel pane) may have a path based
+ * upon configuration.
+ *
+ * By default, displays do not have a path.
+ */
+ public function hasPath() { return FALSE; }
+
+ /**
+ * Check to see if the display has some need to link to another display.
+ *
+ * For the most part, displays without a path will use a link display. However,
+ * sometimes displays that have a path might also need to link to another display.
+ * This is true for feeds.
+ */
+ public function usesLinkDisplay() { return !$this->hasPath(); }
+
+ /**
+ * Check to see if the display can put the exposed formin a block.
+ *
+ * By default, displays that do not have a path cannot disconnect
+ * the exposed form and put it in a block, because the form has no
+ * place to go and Views really wants the forms to go to a specific
+ * page.
+ */
+ public function usesExposedFormInBlock() { return $this->hasPath(); }
+
+ /**
+ * Check to see which display to use when creating links within
+ * a view using this display.
+ */
+ public function getLinkDisplay() {
+ $display_id = $this->getOption('link_display');
+ // If unknown, pick the first one.
+ if (empty($display_id) || empty($this->view->displayHandlers[$display_id])) {
+ foreach ($this->view->displayHandlers as $display_id => $display) {
+ if (!empty($display) && $display->hasPath()) {
+ return $display_id;
+ }
+ }
+ }
+ else {
+ return $display_id;
+ }
+ // fall-through returns NULL
+ }
+
+ /**
+ * Return the base path to use for this display.
+ *
+ * This can be overridden for displays that do strange things
+ * with the path.
+ */
+ public function getPath() {
+ if ($this->hasPath()) {
+ return $this->getOption('path');
+ }
+
+ $display_id = $this->getLinkDisplay();
+ if ($display_id && !empty($this->view->displayHandlers[$display_id]) && is_object($this->view->displayHandlers[$display_id])) {
+ return $this->view->displayHandlers[$display_id]->getPath();
+ }
+ }
+
+ public function getUrl() {
+ return $this->view->getUrl();
+ }
+
+ /**
+ * Check to see if the display needs a breadcrumb
+ *
+ * By default, displays do not need breadcrumbs
+ */
+ public function usesBreadcrumb() { return FALSE; }
+
+ /**
+ * Determine if a given option is set to use the default display or the
+ * current display
+ *
+ * @return
+ * TRUE for the default display
+ */
+ public function isDefaulted($option) {
+ return !$this->isDefaultDisplay() && !empty($this->default_display) && !empty($this->options['defaults'][$option]);
+ }
+
+ /**
+ * Intelligently get an option either from this display or from the
+ * default display, if directed to do so.
+ */
+ public function getOption($option) {
+ if ($this->isDefaulted($option)) {
+ return $this->default_display->getOption($option);
+ }
+
+ if (array_key_exists($option, $this->options)) {
+ return $this->options[$option];
+ }
+ }
+
+ /**
+ * Determine if the display's style uses fields.
+ *
+ * @return bool
+ */
+ public function usesFields() {
+ return $this->getPlugin('style')->usesFields();
+ }
+
+ /**
+ * Get the instance of a plugin, for example style or row.
+ *
+ * @param string $type
+ * The type of the plugin.
+ *
+ * @return Drupal\views\Plugin\views\PluginBase
+ */
+ public function getPlugin($type) {
+ // Look up the plugin name to use for this instance.
+ $options = $this->getOption($type);
+ $name = $options['type'];
+
+ // Query plugins allow specifying a specific query class per base table.
+ if ($type == 'query') {
+ $views_data = views_fetch_data($this->view->storage->base_table);
+ $name = !empty($views_data['table']['base']['query class']) ? $views_data['table']['base']['query class'] : 'views_query';
+ }
+
+ // Plugin instances are stored on the display for re-use.
+ if (!isset($this->plugins[$type][$name])) {
+ $plugin = drupal_container()->get("plugin.manager.views.$type")->createInstance($name);
+
+ // Initialize the plugin. Query has a unique method signature.
+ if ($type == 'query') {
+ $plugin->init($this->view->storage->base_table, $this->view->storage->base_field, $options['options']);
+ }
+ else {
+ $plugin->init($this->view, $this, $options['options']);
+ }
+
+ $this->plugins[$type][$name] = $plugin;
+ }
+
+ return $this->plugins[$type][$name];
+ }
+
+ /**
+ * Get the handler object for a single handler.
+ */
+ public function &getHandler($type, $id) {
+ if (!isset($this->handlers[$type])) {
+ $this->getHandlers($type);
+ }
+
+ if (isset($this->handlers[$type][$id])) {
+ return $this->handlers[$type][$id];
+ }
+
+ // So we can return a reference.
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * Get a full array of handlers for $type. This caches them.
+ */
+ public function getHandlers($type) {
+ if (!isset($this->handlers[$type])) {
+ $this->handlers[$type] = array();
+ $types = ViewExecutable::viewsHandlerTypes();
+ $plural = $types[$type]['plural'];
+
+ foreach ($this->getOption($plural) as $id => $info) {
+ // If this is during form submission and there are temporary options
+ // which can only appear if the view is in the edit cache, use those
+ // options instead. This is used for AJAX multi-step stuff.
+ if (isset($_POST['form_id']) && isset($this->view->temporary_options[$type][$id])) {
+ $info = $this->view->temporary_options[$type][$id];
+ }
+
+ if ($info['id'] != $id) {
+ $info['id'] = $id;
+ }
+
+ // If aggregation is on, the group type might override the actual
+ // handler that is in use. This piece of code checks that and,
+ // if necessary, sets the override handler.
+ $override = NULL;
+ if ($this->useGroupBy() && !empty($info['group_type'])) {
+ if (empty($this->view->query)) {
+ $this->view->initQuery();
+ }
+ $aggregate = $this->view->query->get_aggregation_info();
+ if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
+ $override = $aggregate[$info['group_type']]['handler'][$type];
+ }
+ }
+
+ if (!empty($types[$type]['type'])) {
+ $handler_type = $types[$type]['type'];
+ }
+ else {
+ $handler_type = $type;
+ }
+
+ $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
+ if ($handler) {
+ // Special override for area types so they know where they come from.
+ if ($handler_type == 'area') {
+ $handler->handler_type = $type;
+ }
+
+ $handler->init($this->view, $info);
+ $this->handlers[$type][$id] = &$handler;
+ }
+
+ // Prevent reference problems.
+ unset($handler);
+ }
+ }
+
+ return $this->handlers[$type];
+ }
+
+ /**
+ * Retrieve a list of fields for the current display with the
+ * relationship associated if it exists.
+ *
+ * @param $groupable_only
+ * Return only an array of field labels from handler that return TRUE
+ * from use_string_group_by method.
+ */
+ public function getFieldLabels() {
+ // Use func_get_arg so the function signature isn't amended
+ // but we can still pass TRUE into the function to filter
+ // by groupable handlers.
+ $args = func_get_args();
+ $groupable_only = isset($args[0]) ? $args[0] : FALSE;
+
+ $options = array();
+ foreach ($this->getHandlers('relationship') as $relationship => $handler) {
+ if ($label = $handler->label()) {
+ $relationships[$relationship] = $label;
+ }
+ else {
+ $relationships[$relationship] = $handler->adminLabel();
+ }
+ }
+
+ foreach ($this->getHandlers('field') as $id => $handler) {
+ if ($groupable_only && !$handler->use_string_group_by()) {
+ // Continue to next handler if it's not groupable.
+ continue;
+ }
+ if ($label = $handler->label()) {
+ $options[$id] = $label;
+ }
+ else {
+ $options[$id] = $handler->adminLabel();
+ }
+ if (!empty($handler->options['relationship']) && !empty($relationships[$handler->options['relationship']])) {
+ $options[$id] = '(' . $relationships[$handler->options['relationship']] . ') ' . $options[$id];
+ }
+ }
+ return $options;
+ }
+
+ /**
+ * Intelligently set an option either from this display or from the
+ * default display, if directed to do so.
+ */
+ public function setOption($option, $value) {
+ if ($this->isDefaulted($option)) {
+ return $this->default_display->setOption($option, $value);
+ }
+
+ // Set this in two places: On the handler where we'll notice it
+ // but also on the display object so it gets saved. This should
+ // only be a temporary fix.
+ $this->display['display_options'][$option] = $value;
+ return $this->options[$option] = $value;
+ }
+
+ /**
+ * Set an option and force it to be an override.
+ */
+ public function overrideOption($option, $value) {
+ $this->setOverride($option, FALSE);
+ $this->setOption($option, $value);
+ }
+
+ /**
+ * Because forms may be split up into sections, this provides
+ * an easy URL to exactly the right section. Don't override this.
+ */
+ public function optionLink($text, $section, $class = '', $title = '') {
+ views_add_js('ajax');
+ if (!empty($class)) {
+ $text = '<span>' . $text . '</span>';
+ }
+
+ if (!trim($text)) {
+ $text = t('Broken field');
+ }
+
+ if (empty($title)) {
+ $title = $text;
+ }
+
+ return l($text, 'admin/structure/views/nojs/display/' . $this->view->storage->name . '/' . $this->display['id'] . '/' . $section, array('attributes' => array('class' => 'views-ajax-link ' . $class, 'title' => $title, 'id' => drupal_html_id('views-' . $this->display['id'] . '-' . $section)), 'html' => TRUE));
+ }
+
+ /**
+ * Returns to tokens for arguments.
+ *
+ * This function is similar to views_handler_field::get_render_tokens()
+ * but without fields tokens.
+ */
+ public function getArgumentsTokens() {
+ $tokens = array();
+ if (!empty($this->view->build_info['substitutions'])) {
+ $tokens = $this->view->build_info['substitutions'];
+ }
+ $count = 0;
+ foreach ($this->view->display_handler->getHandlers('argument') as $arg => $handler) {
+ $token = '%' . ++$count;
+ if (!isset($tokens[$token])) {
+ $tokens[$token] = '';
+ }
+
+ // Use strip tags as there should never be HTML in the path.
+ // However, we need to preserve special characters like " that
+ // were removed by check_plain().
+ $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(decode_entities($this->view->args[$count - 1])) : '';
+ }
+
+ return $tokens;
+ }
+
+ /**
+ * Provide the default summary for options in the views UI.
+ *
+ * This output is returned as an array.
+ */
+ public function optionsSummary(&$categories, &$options) {
+ $categories = array(
+ 'title' => array(
+ 'title' => t('Title'),
+ 'column' => 'first',
+ ),
+ 'format' => array(
+ 'title' => t('Format'),
+ 'column' => 'first',
+ ),
+ 'filters' => array(
+ 'title' => t('Filters'),
+ 'column' => 'first',
+ ),
+ 'fields' => array(
+ 'title' => t('Fields'),
+ 'column' => 'first',
+ ),
+ 'pager' => array(
+ 'title' => t('Pager'),
+ 'column' => 'second',
+ ),
+ 'exposed' => array(
+ 'title' => t('Exposed form'),
+ 'column' => 'third',
+ 'build' => array(
+ '#weight' => 1,
+ ),
+ ),
+ 'access' => array(
+ 'title' => '',
+ 'column' => 'second',
+ 'build' => array(
+ '#weight' => -5,
+ ),
+ ),
+ 'other' => array(
+ 'title' => t('Other'),
+ 'column' => 'third',
+ 'build' => array(
+ '#weight' => 2,
+ ),
+ ),
+ );
+
+ if ($this->display['id'] != 'default') {
+ $options['display_id'] = array(
+ 'category' => 'other',
+ 'title' => t('Machine Name'),
+ 'value' => !empty($this->display['new_id']) ? check_plain($this->display['new_id']) : check_plain($this->display['id']),
+ 'desc' => t('Change the machine name of this display.'),
+ );
+ }
+
+ $display_comment = check_plain(drupal_substr($this->getOption('display_comment'), 0, 10));
+ $options['display_comment'] = array(
+ 'category' => 'other',
+ 'title' => t('Comment'),
+ 'value' => !empty($display_comment) ? $display_comment : t('No comment'),
+ 'desc' => t('Comment or document this display.'),
+ );
+
+ $title = strip_tags($this->getOption('title'));
+ if (!$title) {
+ $title = t('None');
+ }
+
+ $options['title'] = array(
+ 'category' => 'title',
+ 'title' => t('Title'),
+ 'value' => $title,
+ 'desc' => t('Change the title that this display will use.'),
+ );
+
+ $style_plugin_instance = $this->getPlugin('style');
+ $style_summary = empty($style_plugin_instance->definition['title']) ? t('Missing style plugin') : $style_plugin_instance->summaryTitle();
+ $style_title = empty($style_plugin_instance->definition['title']) ? t('Missing style plugin') : $style_plugin_instance->pluginTitle();
+
+ $style = '';
+
+ $options['style'] = array(
+ 'category' => 'format',
+ 'title' => t('Format'),
+ 'value' => $style_title,
+ 'setting' => $style_summary,
+ 'desc' => t('Change the way content is formatted.'),
+ );
+
+ // This adds a 'Settings' link to the style_options setting if the style has options.
+ if ($style_plugin_instance->usesOptions()) {
+ $options['style']['links']['style_options'] = t('Change settings for this format');
+ }
+
+ if ($style_plugin_instance->usesRowPlugin()) {
+ $row_plugin_instance = $this->getPlugin('row');
+ $row_summary = empty($row_plugin_instance->definition['title']) ? t('Missing style plugin') : $row_plugin_instance->summaryTitle();
+ $row_title = empty($row_plugin_instance->definition['title']) ? t('Missing style plugin') : $row_plugin_instance->pluginTitle();
+
+ $options['row'] = array(
+ 'category' => 'format',
+ 'title' => t('Show'),
+ 'value' => $row_title,
+ 'setting' => $row_summary,
+ 'desc' => t('Change the way each row in the view is styled.'),
+ );
+ // This adds a 'Settings' link to the row_options setting if the row style has options.
+ if ($row_plugin_instance->usesOptions()) {
+ $options['row']['links']['row_options'] = t('Change settings for this style');
+ }
+ }
+ if ($this->usesAJAX()) {
+ $options['use_ajax'] = array(
+ 'category' => 'other',
+ 'title' => t('Use AJAX'),
+ 'value' => $this->getOption('use_ajax') ? t('Yes') : t('No'),
+ 'desc' => t('Change whether or not this display will use AJAX.'),
+ );
+ }
+ if ($this->usesAttachments()) {
+ $options['hide_attachment_summary'] = array(
+ 'category' => 'other',
+ 'title' => t('Hide attachments in summary'),
+ 'value' => $this->getOption('hide_attachment_summary') ? t('Yes') : t('No'),
+ 'desc' => t('Change whether or not to display attachments when displaying a contextual filter summary.'),
+ );
+ }
+ if (!isset($this->definition['contextual links locations']) || !empty($this->definition['contextual links locations'])) {
+ $options['hide_admin_links'] = array(
+ 'category' => 'other',
+ 'title' => t('Hide contextual links'),
+ 'value' => $this->getOption('hide_admin_links') ? t('Yes') : t('No'),
+ 'desc' => t('Change whether or not to display contextual links for this view.'),
+ );
+ }
+
+ $pager_plugin = $this->getPlugin('pager');
+ if (!$pager_plugin) {
+ // default to the no pager plugin.
+ $pager_plugin = views_get_plugin('pager', 'none');
+ }
+
+ $pager_str = $pager_plugin->summaryTitle();
+
+ $options['pager'] = array(
+ 'category' => 'pager',
+ 'title' => t('Use pager'),
+ 'value' => $pager_plugin->pluginTitle(),
+ 'setting' => $pager_str,
+ 'desc' => t("Change this display's pager setting."),
+ );
+
+ // If pagers aren't allowed, change the text of the item:
+ if (!$this->usesPager()) {
+ $options['pager']['title'] = t('Items to display');
+ }
+
+ if ($pager_plugin->usesOptions()) {
+ $options['pager']['links']['pager_options'] = t('Change settings for this pager type.');
+ }
+
+ if ($this->usesMore()) {
+ $options['use_more'] = array(
+ 'category' => 'pager',
+ 'title' => t('More link'),
+ 'value' => $this->getOption('use_more') ? t('Yes') : t('No'),
+ 'desc' => t('Specify whether this display will provide a "more" link.'),
+ );
+ }
+
+ $this->view->initQuery();
+ if ($this->view->query->get_aggregation_info()) {
+ $options['group_by'] = array(
+ 'category' => 'other',
+ 'title' => t('Use aggregation'),
+ 'value' => $this->getOption('group_by') ? t('Yes') : t('No'),
+ 'desc' => t('Allow grouping and aggregation (calculation) of fields.'),
+ );
+ }
+
+ $options['query'] = array(
+ 'category' => 'other',
+ 'title' => t('Query settings'),
+ 'value' => t('Settings'),
+ 'desc' => t('Allow to set some advanced settings for the query plugin'),
+ );
+
+ $languages = array(
+ '***CURRENT_LANGUAGE***' => t("Current user's language"),
+ '***DEFAULT_LANGUAGE***' => t("Default site language"),
+ LANGUAGE_NOT_SPECIFIED => t('Language neutral'),
+ );
+ if (module_exists('language')) {
+ $languages = array_merge($languages, language_list());
+ }
+ $options['field_langcode'] = array(
+ 'category' => 'other',
+ 'title' => t('Field Language'),
+ 'value' => $languages[$this->getOption('field_langcode')],
+ 'desc' => t('All fields which support translations will be displayed in the selected language.'),
+ );
+
+ $access_plugin = $this->getPlugin('access');
+ if (!$access_plugin) {
+ // default to the no access control plugin.
+ $access_plugin = views_get_plugin('access', 'none');
+ }
+
+ $access_str = $access_plugin->summaryTitle();
+
+ $options['access'] = array(
+ 'category' => 'access',
+ 'title' => t('Access'),
+ 'value' => $access_plugin->pluginTitle(),
+ 'setting' => $access_str,
+ 'desc' => t('Specify access control type for this display.'),
+ );
+
+ if ($access_plugin->usesOptions()) {
+ $options['access']['links']['access_options'] = t('Change settings for this access type.');
+ }
+
+ $cache_plugin = $this->getPlugin('cache');
+ if (!$cache_plugin) {
+ // default to the no cache control plugin.
+ $cache_plugin = views_get_plugin('cache', 'none');
+ }
+
+ $cache_str = $cache_plugin->summaryTitle();
+
+ $options['cache'] = array(
+ 'category' => 'other',
+ 'title' => t('Caching'),
+ 'value' => $cache_plugin->pluginTitle(),
+ 'setting' => $cache_str,
+ 'desc' => t('Specify caching type for this display.'),
+ );
+
+ if ($cache_plugin->usesOptions()) {
+ $options['cache']['links']['cache_options'] = t('Change settings for this caching type.');
+ }
+
+ if ($access_plugin->usesOptions()) {
+ $options['access']['links']['access_options'] = t('Change settings for this access type.');
+ }
+
+ if ($this->usesLinkDisplay()) {
+ $display_id = $this->getLinkDisplay();
+ $link_display = empty($this->view->display[$display_id]) ? t('None') : check_plain($this->view->display[$display_id]['display_title']);
+ $link_display = $this->getOption('link_display') == 'custom_url' ? t('Custom URL') : $link_display;
+ $options['link_display'] = array(
+ 'category' => 'other',
+ 'title' => t('Link display'),
+ 'value' => $link_display,
+ 'desc' => t('Specify which display or custom url this display will link to.'),
+ );
+ }
+
+ if ($this->usesExposedFormInBlock()) {
+ $options['exposed_block'] = array(
+ 'category' => 'exposed',
+ 'title' => t('Exposed form in block'),
+ 'value' => $this->getOption('exposed_block') ? t('Yes') : t('No'),
+ 'desc' => t('Allow the exposed form to appear in a block instead of the view.'),
+ );
+ }
+
+ $exposed_form_plugin = $this->getPlugin('exposed_form');
+ if (!$exposed_form_plugin) {
+ // default to the no cache control plugin.
+ $exposed_form_plugin = views_get_plugin('exposed_form', 'basic');
+ }
+
+ $exposed_form_str = $exposed_form_plugin->summaryTitle();
+
+ $options['exposed_form'] = array(
+ 'category' => 'exposed',
+ 'title' => t('Exposed form style'),
+ 'value' => $exposed_form_plugin->pluginTitle(),
+ 'setting' => $exposed_form_str,
+ 'desc' => t('Select the kind of exposed filter to use.'),
+ );
+
+ if ($exposed_form_plugin->usesOptions()) {
+ $options['exposed_form']['links']['exposed_form_options'] = t('Exposed form settings for this exposed form style.');
+ }
+
+ $css_class = check_plain(trim($this->getOption('css_class')));
+ if (!$css_class) {
+ $css_class = t('None');
+ }
+
+ $options['css_class'] = array(
+ 'category' => 'other',
+ 'title' => t('CSS class'),
+ 'value' => $css_class,
+ 'desc' => t('Change the CSS class name(s) that will be added to this display.'),
+ );
+
+ $options['analyze-theme'] = array(
+ 'category' => 'other',
+ 'title' => t('Theme'),
+ 'value' => t('Information'),
+ 'desc' => t('Get information on how to theme this display'),
+ );
+
+ foreach ($this->extender as $extender) {
+ $extender->optionsSummary($categories, $options);
+ }
+ }
+
+ /**
+ * Provide the default form for setting options.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ if ($this->defaultableSections($form_state['section'])) {
+ views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
+ }
+ $form['#title'] = check_plain($this->display['display_title']) . ': ';
+
+ // Set the 'section' to hilite on the form.
+ // If it's the item we're looking at is pulling from the default display,
+ // reflect that. Don't use is_defaulted since we want it to show up even
+ // on the default display.
+ if (!empty($this->options['defaults'][$form_state['section']])) {
+ $form['#section'] = 'default-' . $form_state['section'];
+ }
+ else {
+ $form['#section'] = $this->display['id'] . '-' . $form_state['section'];
+ }
+
+ switch ($form_state['section']) {
+ case 'display_id':
+ $form['#title'] .= t('The machine name of this display');
+ $form['display_id'] = array(
+ '#type' => 'textfield',
+ '#description' => t('This is machine name of the display.'),
+ '#default_value' => !empty($this->display['new_id']) ? $this->display['new_id'] : $this->display['id'],
+ '#required' => TRUE,
+ '#size' => 64,
+ );
+ break;
+ case 'display_title':
+ $form['#title'] .= t('The name and the description of this display');
+ $form['display_title'] = array(
+ '#title' => t('Name'),
+ '#type' => 'textfield',
+ '#description' => t('This name will appear only in the administrative interface for the View.'),
+ '#default_value' => $this->display['display_title'],
+ );
+ $form['display_description'] = array(
+ '#title' => t('Description'),
+ '#type' => 'textfield',
+ '#description' => t('This description will appear only in the administrative interface for the View.'),
+ '#default_value' => $this->getOption('display_description'),
+ );
+ break;
+ case 'display_comment':
+ $form['#title'] .= t("This display's comments");
+ $form['display_comment'] = array(
+ '#type' => 'textarea',
+ '#description' => t('This value will be seen and used only within the Views UI and can be used to document this display. You can use this to provide notes for other or future maintainers of your site about how or why this display is configured.'),
+ '#default_value' => $this->getOption('display_comment'),
+ );
+ break;
+ case 'title':
+ $form['#title'] .= t('The title of this view');
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#description' => t('This title will be displayed with the view, wherever titles are normally displayed; i.e, as the page title, block title, etc.'),
+ '#default_value' => $this->getOption('title'),
+ );
+ break;
+ case 'css_class':
+ $form['#title'] .= t('CSS class');
+ $form['css_class'] = array(
+ '#type' => 'textfield',
+ '#description' => t('The CSS class names will be added to the view. This enables you to use specific CSS code for each view. You may define multiples classes separated by spaces.'),
+ '#default_value' => $this->getOption('css_class'),
+ );
+ break;
+ case 'use_ajax':
+ $form['#title'] .= t('Use AJAX when available to load this view');
+ $form['description'] = array(
+ '#markup' => '<div class="description form-item">' . t('If set, this view will use an AJAX mechanism for paging, table sorting and exposed filters. This means the entire page will not refresh. It is not recommended that you use this if this view is the main content of the page as it will prevent deep linking to specific pages, but it is very useful for side content.') . '</div>',
+ );
+ $form['use_ajax'] = array(
+ '#type' => 'radios',
+ '#options' => array(1 => t('Yes'), 0 => t('No')),
+ '#default_value' => $this->getOption('use_ajax') ? 1 : 0,
+ );
+ break;
+ case 'hide_attachment_summary':
+ $form['#title'] .= t('Hide attachments when displaying a contextual filter summary');
+ $form['hide_attachment_summary'] = array(
+ '#type' => 'radios',
+ '#options' => array(1 => t('Yes'), 0 => t('No')),
+ '#default_value' => $this->getOption('hide_attachment_summary') ? 1 : 0,
+ );
+ break;
+ case 'hide_admin_links':
+ $form['#title'] .= t('Hide contextual links on this view.');
+ $form['hide_admin_links'] = array(
+ '#type' => 'radios',
+ '#options' => array(1 => t('Yes'), 0 => t('No')),
+ '#default_value' => $this->getOption('hide_admin_links') ? 1 : 0,
+ );
+ break;
+ case 'use_more':
+ $form['#title'] .= t('Add a more link to the bottom of the display.');
+ $form['use_more'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Create more link'),
+ '#description' => t("This will add a more link to the bottom of this view, which will link to the page view. If you have more than one page view, the link will point to the display specified in 'Link display' section under advanced. You can override the url at the link display setting."),
+ '#default_value' => $this->getOption('use_more'),
+ );
+ $form['use_more_always'] = array(
+ '#type' => 'checkbox',
+ '#title' => t("Display 'more' link only if there is more content"),
+ '#description' => t("Leave this unchecked to display the 'more' link even if there are no more items to display."),
+ '#default_value' => !$this->getOption('use_more_always'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="use_more"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+ $form['use_more_text'] = array(
+ '#type' => 'textfield',
+ '#title' => t('More link text'),
+ '#description' => t("The text to display for the more link."),
+ '#default_value' => $this->getOption('useMoreText'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="use_more"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+ break;
+ case 'group_by':
+ $form['#title'] .= t('Allow grouping and aggregation (calculation) of fields.');
+ $form['group_by'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Aggregate'),
+ '#description' => t('If enabled, some fields may become unavailable. All fields that are selected for grouping will be collapsed to one record per distinct value. Other fields which are selected for aggregation will have the function run on them. For example, you can group nodes on title and count the number of nids in order to get a list of duplicate titles.'),
+ '#default_value' => $this->getOption('group_by'),
+ );
+ break;
+ case 'access':
+ $form['#title'] .= t('Access restrictions');
+ $form['access'] = array(
+ '#prefix' => '<div class="clearfix">',
+ '#suffix' => '</div>',
+ '#tree' => TRUE,
+ );
+
+ $access = $this->getOption('access');
+ $form['access']['type'] = array(
+ '#type' => 'radios',
+ '#options' => views_fetch_plugin_names('access', NULL, array($this->view->storage->base_table)),
+ '#default_value' => $access['type'],
+ );
+
+ $access_plugin = $this->getPlugin('access');
+ if ($access_plugin->usesOptions()) {
+ $form['markup'] = array(
+ '#prefix' => '<div class="form-item description">',
+ '#markup' => t('You may also adjust the !settings for the currently selected access restriction.', array('!settings' => $this->optionLink(t('settings'), 'access_options'))),
+ '#suffix' => '</div>',
+ );
+ }
+
+ break;
+ case 'access_options':
+ $plugin = $this->getPlugin('access');
+ $form['#title'] .= t('Access options');
+ if ($plugin) {
+ $form['access_options'] = array(
+ '#tree' => TRUE,
+ );
+ $plugin->buildOptionsForm($form['access_options'], $form_state);
+ }
+ break;
+ case 'cache':
+ $form['#title'] .= t('Caching');
+ $form['cache'] = array(
+ '#prefix' => '<div class="clearfix">',
+ '#suffix' => '</div>',
+ '#tree' => TRUE,
+ );
+
+ $cache = $this->getOption('cache');
+ $form['cache']['type'] = array(
+ '#type' => 'radios',
+ '#options' => views_fetch_plugin_names('cache', NULL, array($this->view->storage->base_table)),
+ '#default_value' => $cache['type'],
+ );
+
+ $cache_plugin = $this->getPlugin('cache');
+ if ($cache_plugin->usesOptions()) {
+ $form['markup'] = array(
+ '#prefix' => '<div class="form-item description">',
+ '#suffix' => '</div>',
+ '#markup' => t('You may also adjust the !settings for the currently selected cache mechanism.', array('!settings' => $this->optionLink(t('settings'), 'cache_options'))),
+ );
+ }
+ break;
+ case 'cache_options':
+ $plugin = $this->getPlugin('cache');
+ $form['#title'] .= t('Caching options');
+ if ($plugin) {
+ $form['cache_options'] = array(
+ '#tree' => TRUE,
+ );
+ $plugin->buildOptionsForm($form['cache_options'], $form_state);
+ }
+ break;
+ case 'query':
+ $query_options = $this->getOption('query');
+ $plugin_name = $query_options['type'];
+
+ $form['#title'] .= t('Query options');
+ $this->view->initQuery();
+ if ($this->view->query) {
+ $form['query'] = array(
+ '#tree' => TRUE,
+ 'type' => array(
+ '#type' => 'value',
+ '#value' => $plugin_name,
+ ),
+ 'options' => array(
+ '#tree' => TRUE,
+ ),
+ );
+
+ $this->view->query->buildOptionsForm($form['query']['options'], $form_state);
+ }
+ break;
+ case 'field_language':
+ $form['#title'] .= t('Field Language');
+
+ $entities = entity_get_info();
+ $entity_tables = array();
+ $has_translation_handlers = FALSE;
+ foreach ($entities as $type => $entity_info) {
+ $entity_tables[] = $entity_info['base table'];
+
+ if (!empty($entity_info['translation'])) {
+ $has_translation_handlers = TRUE;
+ }
+ }
+
+ // Doesn't make sense to show a field setting here if we aren't querying
+ // an entity base table. Also, we make sure that there's at least one
+ // entity type with a translation handler attached.
+ if (in_array($this->view->storage->base_table, $entity_tables) && $has_translation_handlers) {
+ $languages = array(
+ '***CURRENT_LANGUAGE***' => t("Current user's language"),
+ '***DEFAULT_LANGUAGE***' => t("Default site language"),
+ LANGUAGE_NOT_SPECIFIED => t('Language neutral'),
+ );
+ $languages = array_merge($languages, views_language_list());
+
+ $form['field_langcode'] = array(
+ '#type' => 'select',
+ '#title' => t('Field Language'),
+ '#description' => t('All fields which support translations will be displayed in the selected language.'),
+ '#options' => $languages,
+ '#default_value' => $this->getOption('field_langcode'),
+ );
+ $form['field_langcode_add_to_query'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('When needed, add the field language condition to the query'),
+ '#default_value' => $this->getOption('field_langcode_add_to_query'),
+ );
+ }
+ else {
+ $form['field_language']['#markup'] = t("You don't have translatable entity types.");
+ }
+ break;
+ case 'style':
+ $form['#title'] .= t('How should this view be styled');
+ $style_plugin = $this->getPlugin('style');
+ $form['style'] = array(
+ '#type' => 'radios',
+ '#options' => views_fetch_plugin_names('style', $this->getStyleType(), array($this->view->storage->base_table)),
+ '#default_value' => $style_plugin->definition['id'],
+ '#description' => t('If the style you choose has settings, be sure to click the settings button that will appear next to it in the View summary.'),
+ );
+
+ if ($style_plugin->usesOptions()) {
+ $form['markup'] = array(
+ '#prefix' => '<div class="form-item description">',
+ '#suffix' => '</div>',
+ '#markup' => t('You may also adjust the !settings for the currently selected style.', array('!settings' => $this->optionLink(t('settings'), 'style_options'))),
+ );
+ }
+
+ break;
+ case 'style_options':
+ $form['#title'] .= t('Style options');
+ $style = TRUE;
+ $style_plugin = $this->getOption('style');
+ $name = $style_plugin['type'];
+
+ case 'row_options':
+ if (!isset($name)) {
+ $row_plugin = $this->getOption('row');
+ $name = $row_plugin['type'];
+ }
+ // if row, $style will be empty.
+ if (empty($style)) {
+ $form['#title'] .= t('Row style options');
+ }
+ $plugin = $this->getPlugin(empty($style) ? 'row' : 'style', $name);
+ if ($plugin) {
+ $form[$form_state['section']] = array(
+ '#tree' => TRUE,
+ );
+ $plugin->buildOptionsForm($form[$form_state['section']], $form_state);
+ }
+ break;
+ case 'row':
+ $form['#title'] .= t('How should each row in this view be styled');
+ $row_plugin_instance = $this->getPlugin('row');
+ $form['row'] = array(
+ '#type' => 'radios',
+ '#options' => views_fetch_plugin_names('row', $this->getStyleType(), array($this->view->storage->base_table)),
+ '#default_value' => $row_plugin_instance->definition['id'],
+ );
+
+ if ($row_plugin_instance->usesOptions()) {
+ $form['markup'] = array(
+ '#prefix' => '<div class="form-item description">',
+ '#suffix' => '</div>',
+ '#markup' => t('You may also adjust the !settings for the currently selected row style.', array('!settings' => $this->optionLink(t('settings'), 'row_options'))),
+ );
+ }
+
+ break;
+ case 'link_display':
+ $form['#title'] .= t('Which display to use for path');
+ foreach ($this->view->storage->display as $display_id => $display) {
+ if ($this->view->displayHandlers[$display_id]->hasPath()) {
+ $options[$display_id] = $display['display_title'];
+ }
+ }
+ $options['custom_url'] = t('Custom URL');
+ if (count($options)) {
+ $form['link_display'] = array(
+ '#type' => 'radios',
+ '#options' => $options,
+ '#description' => t("Which display to use to get this display's path for things like summary links, rss feed links, more links, etc."),
+ '#default_value' => $this->getOption('link_display'),
+ );
+ }
+
+ $options = array();
+ $count = 0; // This lets us prepare the key as we want it printed.
+ foreach ($this->view->display_handler->getHandlers('argument') as $arg => $handler) {
+ $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->adminLabel()));
+ $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->adminLabel()));
+ }
+
+ // Default text.
+ // We have some options, so make a list.
+ $output = '';
+ if (!empty($options)) {
+ $output = t('<p>The following tokens are available for this link.</p>');
+ foreach (array_keys($options) as $type) {
+ if (!empty($options[$type])) {
+ $items = array();
+ foreach ($options[$type] as $key => $value) {
+ $items[] = $key . ' == ' . $value;
+ }
+ $output .= theme('item_list',
+ array(
+ 'items' => $items,
+ 'type' => $type
+ ));
+ }
+ }
+ }
+
+ $form['link_url'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Custom URL'),
+ '#default_value' => $this->getOption('link_url'),
+ '#description' => t('A Drupal path or external URL the more link will point to. Note that this will override the link display setting above.') . $output,
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="link_display"]' => array('value' => 'custom_url'),
+ ),
+ ),
+ );
+ break;
+ case 'analyze-theme':
+ $form['#title'] .= t('Theming information');
+ if ($theme = drupal_container()->get('request')->request->get('theme')) {
+ $this->theme = $theme;
+ }
+ elseif (empty($this->theme)) {
+ $this->theme = variable_get('theme_default', 'bartik');
+ }
+
+ if (isset($GLOBALS['theme']) && $GLOBALS['theme'] == $this->theme) {
+ $this->theme_registry = theme_get_registry();
+ $theme_engine = $GLOBALS['theme_engine'];
+ }
+ else {
+ $themes = list_themes();
+ $theme = $themes[$this->theme];
+
+ // Find all our ancestor themes and put them in an array.
+ $base_theme = array();
+ $ancestor = $this->theme;
+ while ($ancestor && isset($themes[$ancestor]->base_theme)) {
+ $ancestor = $themes[$ancestor]->base_theme;
+ $base_theme[] = $themes[$ancestor];
+ }
+
+ // The base themes should be initialized in the right order.
+ $base_theme = array_reverse($base_theme);
+
+ // This code is copied directly from _drupal_theme_initialize()
+ $theme_engine = NULL;
+
+ // Initialize the theme.
+ if (isset($theme->engine)) {
+ // Include the engine.
+ include_once DRUPAL_ROOT . '/' . $theme->owner;
+
+ $theme_engine = $theme->engine;
+ if (function_exists($theme_engine . '_init')) {
+ foreach ($base_theme as $base) {
+ call_user_func($theme_engine . '_init', $base);
+ }
+ call_user_func($theme_engine . '_init', $theme);
+ }
+ }
+ else {
+ // include non-engine theme files
+ foreach ($base_theme as $base) {
+ // Include the theme file or the engine.
+ if (!empty($base->owner)) {
+ include_once DRUPAL_ROOT . '/' . $base->owner;
+ }
+ }
+ // and our theme gets one too.
+ if (!empty($theme->owner)) {
+ include_once DRUPAL_ROOT . '/' . $theme->owner;
+ }
+ }
+ $this->theme_registry = _theme_load_registry($theme, $base_theme, $theme_engine);
+ }
+
+ // If there's a theme engine involved, we also need to know its extension
+ // so we can give the proper filename.
+ $this->theme_extension = '.tpl.php';
+ if (isset($theme_engine)) {
+ $extension_function = $theme_engine . '_extension';
+ if (function_exists($extension_function)) {
+ $this->theme_extension = $extension_function();
+ }
+ }
+
+ $funcs = array();
+ // Get theme functions for the display. Note that some displays may
+ // not have themes. The 'feed' display, for example, completely
+ // delegates to the style.
+ if (!empty($this->definition['theme'])) {
+ $funcs[] = $this->optionLink(t('Display output'), 'analyze-theme-display') . ': ' . $this->formatThemes($this->themeFunctions());
+ $themes = $this->additionalThemeFunctions();
+ if ($themes) {
+ foreach ($themes as $theme) {
+ $funcs[] = $this->optionLink(t('Alternative display output'), 'analyze-theme-display') . ': ' . $this->formatThemes($theme);
+ }
+ }
+ }
+
+ $plugin = $this->getPlugin('style');
+ if ($plugin) {
+ $funcs[] = $this->optionLink(t('Style output'), 'analyze-theme-style') . ': ' . $this->formatThemes($plugin->themeFunctions(), $plugin->additionalThemeFunctions());
+ $themes = $plugin->additionalThemeFunctions();
+ if ($themes) {
+ foreach ($themes as $theme) {
+ $funcs[] = $this->optionLink(t('Alternative style'), 'analyze-theme-style') . ': ' . $this->formatThemes($theme);
+ }
+ }
+
+ if ($plugin->usesRowPlugin()) {
+ $row_plugin = $this->getPlugin('row');
+ if ($row_plugin) {
+ $funcs[] = $this->optionLink(t('Row style output'), 'analyze-theme-row') . ': ' . $this->formatThemes($row_plugin->themeFunctions());
+ $themes = $row_plugin->additionalThemeFunctions();
+ if ($themes) {
+ foreach ($themes as $theme) {
+ $funcs[] = $this->optionLink(t('Alternative row style'), 'analyze-theme-row') . ': ' . $this->formatThemes($theme);
+ }
+ }
+ }
+ }
+
+ if ($plugin->usesFields()) {
+ foreach ($this->getHandlers('field') as $id => $handler) {
+ $funcs[] = $this->optionLink(t('Field @field (ID: @id)', array('@field' => $handler->adminLabel(), '@id' => $id)), 'analyze-theme-field') . ': ' . $this->formatThemes($handler->themeFunctions());
+ }
+ }
+ }
+
+ $form['important'] = array(
+ '#markup' => '<div class="form-item description"><p>' . t('This section lists all possible templates for the display plugin and for the style plugins, ordered roughly from the least specific to the most specific. The active template for each plugin -- which is the most specific template found on the system -- is highlighted in bold.') . '</p></div>',
+ );
+
+ if (isset($this->view->display_handler->new_id)) {
+ $form['important']['new_id'] = array(
+ '#prefix' => '<div class="description">',
+ '#suffix' => '</div>',
+ '#value' => t("<strong>Important!</strong> You have changed the display's machine name. Anything that attached to this display specifically, such as theming, may stop working until it is updated. To see theme suggestions for it, you need to save the view."),
+ );
+ }
+
+ foreach (list_themes() as $key => $theme) {
+ if (!empty($theme->info['hidden'])) {
+ continue;
+ }
+ $options[$key] = $theme->info['name'];
+ }
+
+ $form['box'] = array(
+ '#prefix' => '<div class="container-inline">',
+ '#suffix' => '</div>',
+ );
+ $form['box']['theme'] = array(
+ '#type' => 'select',
+ '#options' => $options,
+ '#default_value' => $this->theme,
+ );
+
+ $form['box']['change'] = array(
+ '#type' => 'submit',
+ '#value' => t('Change theme'),
+ '#submit' => array('views_ui_edit_display_form_change_theme'),
+ );
+
+ $form['analysis'] = array(
+ '#markup' => '<div class="form-item">' . theme('item_list', array('items' => $funcs)) . '</div>',
+ );
+
+ $form['rescan_button'] = array(
+ '#prefix' => '<div class="form-item">',
+ '#suffix' => '</div>',
+ );
+ $form['rescan_button']['button'] = array(
+ '#type' => 'submit',
+ '#value' => t('Rescan template files'),
+ '#submit' => array('views_ui_config_item_form_rescan'),
+ );
+ $form['rescan_button']['markup'] = array(
+ '#markup' => '<div class="description">' . t("<strong>Important!</strong> When adding, removing, or renaming template files, it is necessary to make Drupal aware of the changes by making it rescan the files on your system. By clicking this button you clear Drupal's theme registry and thereby trigger this rescanning process. The highlighted templates above will then reflect the new state of your system.") . '</div>',
+ );
+
+ $form_state['ok_button'] = TRUE;
+ break;
+ case 'analyze-theme-display':
+ $form['#title'] .= t('Theming information (display)');
+ $output = '<p>' . t('Back to !info.', array('!info' => $this->optionLink(t('theming information'), 'analyze-theme'))) . '</p>';
+
+ if (empty($this->definition['theme'])) {
+ $output .= t('This display has no theming information');
+ }
+ else {
+ $output .= '<p>' . t('This is the default theme template used for this display.') . '</p>';
+ $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($this->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
+ }
+
+ if (!empty($this->definition['additional themes'])) {
+ foreach ($this->definition['additional themes'] as $theme => $type) {
+ $output .= '<p>' . t('This is an alternative template for this display.') . '</p>';
+ $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
+ }
+ }
+
+ $form['analysis'] = array(
+ '#markup' => '<div class="form-item">' . $output . '</div>',
+ );
+
+ $form_state['ok_button'] = TRUE;
+ break;
+ case 'analyze-theme-style':
+ $form['#title'] .= t('Theming information (style)');
+ $output = '<p>' . t('Back to !info.', array('!info' => $this->optionLink(t('theming information'), 'analyze-theme'))) . '</p>';
+
+ $plugin = $this->getPlugin('style');
+
+ if (empty($plugin->definition['theme'])) {
+ $output .= t('This display has no style theming information');
+ }
+ else {
+ $output .= '<p>' . t('This is the default theme template used for this style.') . '</p>';
+ $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
+ }
+
+ if (!empty($plugin->definition['additional themes'])) {
+ foreach ($plugin->definition['additional themes'] as $theme => $type) {
+ $output .= '<p>' . t('This is an alternative template for this style.') . '</p>';
+ $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
+ }
+ }
+
+ $form['analysis'] = array(
+ '#markup' => '<div class="form-item">' . $output . '</div>',
+ );
+
+ $form_state['ok_button'] = TRUE;
+ break;
+ case 'analyze-theme-row':
+ $form['#title'] .= t('Theming information (row style)');
+ $output = '<p>' . t('Back to !info.', array('!info' => $this->optionLink(t('theming information'), 'analyze-theme'))) . '</p>';
+
+ $plugin = $this->getPlugin('row');
+
+ if (empty($plugin->definition['theme'])) {
+ $output .= t('This display has no row style theming information');
+ }
+ else {
+ $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
+ $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
+ }
+
+ if (!empty($plugin->definition['additional themes'])) {
+ foreach ($plugin->definition['additional themes'] as $theme => $type) {
+ $output .= '<p>' . t('This is an alternative template for this row style.') . '</p>';
+ $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
+ }
+ }
+
+ $form['analysis'] = array(
+ '#markup' => '<div class="form-item">' . $output . '</div>',
+ );
+
+ $form_state['ok_button'] = TRUE;
+ break;
+ case 'analyze-theme-field':
+ $form['#title'] .= t('Theming information (row style)');
+ $output = '<p>' . t('Back to !info.', array('!info' => $this->optionLink(t('theming information'), 'analyze-theme'))) . '</p>';
+
+ $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
+
+ // Field templates aren't registered the normal way...and they're always
+ // this one, anyhow.
+ $output .= '<pre>' . check_plain(file_get_contents(drupal_get_path('module', 'views') . '/theme/views-view-field.tpl.php')) . '</pre>';
+
+ $form['analysis'] = array(
+ '#markup' => '<div class="form-item">' . $output . '</div>',
+ );
+ $form_state['ok_button'] = TRUE;
+ break;
+
+ case 'exposed_block':
+ $form['#title'] .= t('Put the exposed form in a block');
+ $form['description'] = array(
+ '#markup' => '<div class="description form-item">' . t('If set, any exposed widgets will not appear with this view. Instead, a block will be made available to the Drupal block administration system, and the exposed form will appear there. Note that this block must be enabled manually, Views will not enable it for you.') . '</div>',
+ );
+ $form['exposed_block'] = array(
+ '#type' => 'radios',
+ '#options' => array(1 => t('Yes'), 0 => t('No')),
+ '#default_value' => $this->getOption('exposed_block') ? 1 : 0,
+ );
+ break;
+ case 'exposed_form':
+ $form['#title'] .= t('Exposed Form');
+ $form['exposed_form'] = array(
+ '#prefix' => '<div class="clearfix">',
+ '#suffix' => '</div>',
+ '#tree' => TRUE,
+ );
+
+ $exposed_form = $this->getOption('exposed_form');
+ $form['exposed_form']['type'] = array(
+ '#type' => 'radios',
+ '#options' => views_fetch_plugin_names('exposed_form', NULL, array($this->view->storage->base_table)),
+ '#default_value' => $exposed_form['type'],
+ );
+
+ $exposed_form_plugin = $this->getPlugin('exposed_form');
+ if ($exposed_form_plugin->usesOptions()) {
+ $form['markup'] = array(
+ '#prefix' => '<div class="form-item description">',
+ '#suffix' => '</div>',
+ '#markup' => t('You may also adjust the !settings for the currently selected style.', array('!settings' => $this->optionLink(t('settings'), 'exposed_form_options'))),
+ );
+ }
+ break;
+ case 'exposed_form_options':
+ $plugin = $this->getPlugin('exposed_form');
+ $form['#title'] .= t('Exposed form options');
+ if ($plugin) {
+ $form['exposed_form_options'] = array(
+ '#tree' => TRUE,
+ );
+ $plugin->buildOptionsForm($form['exposed_form_options'], $form_state);
+ }
+ break;
+ case 'pager':
+ $form['#title'] .= t('Select which pager, if any, to use for this view');
+ $form['pager'] = array(
+ '#prefix' => '<div class="clearfix">',
+ '#suffix' => '</div>',
+ '#tree' => TRUE,
+ );
+
+ $pager = $this->getOption('pager');
+ $form['pager']['type'] = array(
+ '#type' => 'radios',
+ '#options' => views_fetch_plugin_names('pager', !$this->usesPager() ? 'basic' : NULL, array($this->view->storage->base_table)),
+ '#default_value' => $pager['type'],
+ );
+
+ $pager_plugin = $this->getPlugin('pager');
+ if ($pager_plugin->usesOptions()) {
+ $form['markup'] = array(
+ '#prefix' => '<div class="form-item description">',
+ '#suffix' => '</div>',
+ '#markup' => t('You may also adjust the !settings for the currently selected pager.', array('!settings' => $this->optionLink(t('settings'), 'pager_options'))),
+ );
+ }
+
+ break;
+ case 'pager_options':
+ $plugin = $this->getPlugin('pager');
+ $form['#title'] .= t('Pager options');
+ if ($plugin) {
+ $form['pager_options'] = array(
+ '#tree' => TRUE,
+ );
+ $plugin->buildOptionsForm($form['pager_options'], $form_state);
+ }
+ break;
+ }
+
+ foreach ($this->extender as $extender) {
+ $extender->buildOptionsForm($form, $form_state);
+ }
+ }
+
+ /**
+ * Format a list of theme templates for output by the theme info helper.
+ */
+ protected function formatThemes($themes) {
+ $registry = $this->theme_registry;
+ $extension = $this->theme_extension;
+
+ $output = '';
+ $picked = FALSE;
+ foreach ($themes as $theme) {
+ $template = strtr($theme, '_', '-') . $extension;
+ if (!$picked && !empty($registry[$theme])) {
+ $template_path = isset($registry[$theme]['path']) ? $registry[$theme]['path'] . '/' : './';
+ if (file_exists($template_path . $template)) {
+ $hint = t('File found in folder @template-path', array('@template-path' => $template_path));
+ $template = '<strong title="'. $hint .'">' . $template . '</strong>';
+ }
+ else {
+ $template = '<strong class="error">' . $template . ' ' . t('(File not found, in folder @template-path)', array('@template-path' => $template_path)) . '</strong>';
+ }
+ $picked = TRUE;
+ }
+ $fixed[] = $template;
+ }
+
+ return theme('item_list', array('items' => array_reverse($fixed)));
+ }
+
+ /**
+ * Validate the options form.
+ */
+ public function validateOptionsForm(&$form, &$form_state) {
+ switch ($form_state['section']) {
+ case 'display_title':
+ if (empty($form_state['values']['display_title'])) {
+ form_error($form['display_title'], t('Display title may not be empty.'));
+ }
+ break;
+ case 'css_class':
+ $css_class = $form_state['values']['css_class'];
+ if (preg_match('/[^a-zA-Z0-9-_ ]/', $css_class)) {
+ form_error($form['css_class'], t('CSS classes must be alphanumeric or dashes only.'));
+ }
+ break;
+ case 'display_id':
+ if ($form_state['values']['display_id']) {
+ if (preg_match('/[^a-z0-9_]/', $form_state['values']['display_id'])) {
+ form_error($form['display_id'], t('Display name must be letters, numbers, or underscores only.'));
+ }
+
+ foreach ($this->view->display as $id => $display) {
+ if ($id != $this->view->current_display && ($form_state['values']['display_id'] == $id || (isset($display->new_id) && $form_state['values']['display_id'] == $display->new_id))) {
+ form_error($form['display_id'], t('Display id should be unique.'));
+ }
+ }
+ }
+ break;
+ case 'style_options':
+ $style = TRUE;
+ case 'row_options':
+ // if row, $style will be empty.
+ $plugin = $this->getPlugin(empty($style) ? 'row' : 'style');
+ if ($plugin) {
+ $plugin->validateOptionsForm($form[$form_state['section']], $form_state);
+ }
+ break;
+ case 'access_options':
+ $plugin = $this->getPlugin('access');
+ if ($plugin) {
+ $plugin->validateOptionsForm($form['access_options'], $form_state);
+ }
+ break;
+ case 'query':
+ if ($this->view->query) {
+ $this->view->query->validateOptionsForm($form['query'], $form_state);
+ }
+ break;
+ case 'cache_options':
+ $plugin = $this->getPlugin('cache');
+ if ($plugin) {
+ $plugin->validateOptionsForm($form['cache_options'], $form_state);
+ }
+ break;
+ case 'exposed_form_options':
+ $plugin = $this->getPlugin('exposed_form');
+ if ($plugin) {
+ $plugin->validateOptionsForm($form['exposed_form_options'], $form_state);
+ }
+ break;
+ case 'pager_options':
+ $plugin = $this->getPlugin('pager');
+ if ($plugin) {
+ $plugin->validateOptionsForm($form['pager_options'], $form_state);
+ }
+ break;
+ }
+
+ foreach ($this->extender as $extender) {
+ $extender->validateOptionsForm($form, $form_state);
+ }
+ }
+
+ /**
+ * Perform any necessary changes to the form values prior to storage.
+ * There is no need for this function to actually store the data.
+ */
+ public function submitOptionsForm(&$form, &$form_state) {
+ // Not sure I like this being here, but it seems (?) like a logical place.
+ $cache_plugin = $this->getPlugin('cache');
+ if ($cache_plugin) {
+ $cache_plugin->cache_flush();
+ }
+
+ $section = $form_state['section'];
+ switch ($section) {
+ case 'display_id':
+ if (isset($form_state['values']['display_id'])) {
+ $this->display['new_id'] = $form_state['values']['display_id'];
+ }
+ break;
+ case 'display_title':
+ $this->display['display_title'] = $form_state['values']['display_title'];
+ $this->setOption('display_description', $form_state['values']['display_description']);
+ break;
+ case 'access':
+ $access = $this->getOption('access');
+ if ($access['type'] != $form_state['values']['access']['type']) {
+ $plugin = views_get_plugin('access', $form_state['values']['access']['type']);
+ if ($plugin) {
+ $access = array('type' => $form_state['values']['access']['type']);
+ $this->setOption('access', $access);
+ if ($plugin->usesOptions()) {
+ $this->view->addFormToStack('display', $this->display['id'], array('access_options'));
+ }
+ }
+ }
+
+ break;
+ case 'access_options':
+ $plugin = $this->getPlugin('access');
+ if ($plugin) {
+ $access = $this->getOption('access');
+ $plugin->submitOptionsForm($form['access_options'], $form_state);
+ $access['options'] = $form_state['values'][$section];
+ $this->setOption('access', $access);
+ }
+ break;
+ case 'cache':
+ $cache = $this->getOption('cache');
+ if ($cache['type'] != $form_state['values']['cache']['type']) {
+ $plugin = views_get_plugin('cache', $form_state['values']['cache']['type']);
+ if ($plugin) {
+ $cache = array('type' => $form_state['values']['cache']['type']);
+ $this->setOption('cache', $cache);
+ if ($plugin->usesOptions()) {
+ $this->view->addFormToStack('display', $this->display['id'], array('cache_options'));
+ }
+ }
+ }
+
+ break;
+ case 'cache_options':
+ $plugin = $this->getPlugin('cache');
+ if ($plugin) {
+ $cache = $this->getOption('cache');
+ $plugin->submitOptionsForm($form['cache_options'], $form_state);
+ $cache['options'] = $form_state['values'][$section];
+ $this->setOption('cache', $cache);
+ }
+ break;
+ case 'query':
+ $plugin = $this->get_plugin('query');
+ if ($plugin) {
+ $plugin->submitOptionsForm($form['query']['options'], $form_state);
+ $this->setOption('query', $form_state['values'][$section]);
+ }
+ break;
+
+ case 'link_display':
+ $this->setOption('link_url', $form_state['values']['link_url']);
+ case 'title':
+ case 'css_class':
+ case 'display_comment':
+ $this->setOption($section, $form_state['values'][$section]);
+ break;
+ case 'field_language':
+ $this->setOption('field_langcode', $form_state['values']['field_langcode']);
+ $this->setOption('field_langcode_add_to_query', $form_state['values']['field_langcode_add_to_query']);
+ break;
+ case 'use_ajax':
+ case 'hide_attachment_summary':
+ case 'hide_admin_links':
+ $this->setOption($section, (bool)$form_state['values'][$section]);
+ break;
+ case 'use_more':
+ $this->setOption($section, intval($form_state['values'][$section]));
+ $this->setOption('use_more_always', !intval($form_state['values']['use_more_always']));
+ $this->setOption('use_more_text', $form_state['values']['use_more_text']);
+ case 'distinct':
+ $this->setOption($section, $form_state['values'][$section]);
+ break;
+ case 'group_by':
+ $this->setOption($section, $form_state['values'][$section]);
+ break;
+ case 'row':
+ // This if prevents resetting options to default if they don't change
+ // the plugin.
+ $row = $this->getOption('row');
+ if ($row['type'] != $form_state['values'][$section]) {
+ $plugin = views_get_plugin('row', $form_state['values'][$section]);
+ if ($plugin) {
+ $row = array('type' => $form_state['values'][$section]);
+ $this->setOption($section, $row);
+
+ // send ajax form to options page if we use it.
+ if ($plugin->usesOptions()) {
+ $this->view->addFormToStack('display', $this->display['id'], array('row_options'));
+ }
+ }
+ }
+ break;
+ case 'style':
+ // This if prevents resetting options to default if they don't change
+ // the plugin.
+ $style = $this->getOption('style');
+ if ($style['type'] != $form_state['values'][$section]) {
+ $plugin = views_get_plugin('style', $form_state['values'][$section]);
+ if ($plugin) {
+ $row = array('type' => $form_state['values'][$section]);
+ $this->setOption($section, $row);
+ // send ajax form to options page if we use it.
+ if ($plugin->usesOptions()) {
+ $this->view->addFormToStack('display', $this->display['id'], array('style_options'));
+ }
+ }
+ }
+ break;
+ case 'style_options':
+ $plugin = $this->getPlugin('style');
+ if ($plugin) {
+ $style = $this->getOption('style');
+ $plugin->submitOptionsForm($form['style_options'], $form_state);
+ $style['options'] = $form_state['values'][$section];
+ $this->setOption('style', $style);
+ }
+ break;
+ case 'row_options':
+ $plugin = $this->getPlugin('row');
+ if ($plugin) {
+ $row = $this->getOption('row');
+ $plugin->submitOptionsForm($form['row_options'], $form_state);
+ $row['options'] = $form_state['values'][$section];
+ $this->setOption('row', $row);
+ }
+ break;
+ case 'exposed_block':
+ $this->setOption($section, (bool) $form_state['values'][$section]);
+ break;
+ case 'exposed_form':
+ $exposed_form = $this->getOption('exposed_form');
+ if ($exposed_form['type'] != $form_state['values']['exposed_form']['type']) {
+ $plugin = views_get_plugin('exposed_form', $form_state['values']['exposed_form']['type']);
+ if ($plugin) {
+ $exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array());
+ $this->setOption('exposed_form', $exposed_form);
+ if ($plugin->usesOptions()) {
+ $this->view->addFormToStack('display', $this->display['id'], array('exposed_form_options'));
+ }
+ }
+ }
+
+ break;
+ case 'exposed_form_options':
+ $plugin = $this->getPlugin('exposed_form');
+ if ($plugin) {
+ $exposed_form = $this->getOption('exposed_form');
+ $plugin->submitOptionsForm($form['exposed_form_options'], $form_state);
+ $exposed_form['options'] = $form_state['values'][$section];
+ $this->setOption('exposed_form', $exposed_form);
+ }
+ break;
+ case 'pager':
+ $pager = $this->getOption('pager');
+ if ($pager['type'] != $form_state['values']['pager']['type']) {
+ $plugin = views_get_plugin('pager', $form_state['values']['pager']['type']);
+ if ($plugin) {
+ // Because pagers have very similar options, let's allow pagers to
+ // try to carry the options over.
+ $plugin->init($this->view, $this->display, $pager['options']);
+
+ $pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
+ $this->setOption('pager', $pager);
+ if ($plugin->usesOptions()) {
+ $this->view->addFormToStack('display', $this->display['id'], array('pager_options'));
+ }
+ }
+ }
+
+ break;
+ case 'pager_options':
+ $plugin = $this->getPlugin('pager');
+ if ($plugin) {
+ $pager = $this->getOption('pager');
+ $plugin->submitOptionsForm($form['pager_options'], $form_state);
+ $pager['options'] = $form_state['values'][$section];
+ $this->setOption('pager', $pager);
+ }
+ break;
+ }
+
+ foreach ($this->extender as $extender) {
+ $extender->submitOptionsForm($form, $form_state);
+ }
+ }
+
+ /**
+ * If override/revert was clicked, perform the proper toggle.
+ */
+ public function optionsOverride($form, &$form_state) {
+ $this->setOverride($form_state['section']);
+ }
+
+ /**
+ * Flip the override setting for the given section.
+ *
+ * @param string $section
+ * Which option should be marked as overridden, for example "filters".
+ * @param bool $new_state
+ * Select the new state of the option.
+ * - TRUE: Revert to default.
+ * - FALSE: Mark it as overridden.
+ */
+ public function setOverride($section, $new_state = NULL) {
+ $options = $this->defaultableSections($section);
+ if (!$options) {
+ return;
+ }
+
+ if (!isset($new_state)) {
+ $new_state = empty($this->options['defaults'][$section]);
+ }
+
+ // For each option that is part of this group, fix our settings.
+ foreach ($options as $option) {
+ if ($new_state) {
+ // Revert to defaults.
+ unset($this->options[$option]);
+ unset($this->display['display_options'][$option]);
+ }
+ else {
+ // copy existing values into our display.
+ $this->options[$option] = $this->getOption($option);
+ $this->display['display_options'][$option] = $this->options[$option];
+ }
+ $this->options['defaults'][$option] = $new_state;
+ $this->display['display_options']['defaults'][$option] = $new_state;
+ }
+ }
+
+ /**
+ * Inject anything into the query that the display handler needs.
+ */
+ public function query() {
+ foreach ($this->extender as $extender) {
+ $extender->query();
+ }
+ }
+
+ /**
+ * Not all display plugins will support filtering
+ *
+ * @todo this doesn't seems to be used
+ */
+ public function renderFilters() { }
+
+ /**
+ * Not all display plugins will suppert pager rendering.
+ */
+ public function renderPager() {
+ return TRUE;
+ }
+
+ /**
+ * Render the 'more' link
+ */
+ public function renderMoreLink() {
+ if ($this->usesMore() && ($this->useMoreAlways() || (!empty($this->view->pager) && $this->view->pager->has_more_records()))) {
+ $path = $this->getPath();
+
+ if ($this->getOption('link_display') == 'custom_url' && $override_path = $this->getOption('link_url')) {
+ $tokens = $this->getArgumentsTokens();
+ $path = strtr($override_path, $tokens);
+ }
+
+ if ($path) {
+ if (empty($override_path)) {
+ $path = $this->view->getUrl(NULL, $path);
+ }
+ $url_options = array();
+ if (!empty($this->view->exposed_raw_input)) {
+ $url_options['query'] = $this->view->exposed_raw_input;
+ }
+ $theme = views_theme_functions('views_more', $this->view, $this->view->display_handler->display);
+ $path = check_url(url($path, $url_options));
+
+ return theme($theme, array('more_url' => $path, 'link_text' => check_plain($this->useMoreText()), 'view' => $this->view));
+ }
+ }
+ }
+
+
+ /**
+ * Legacy functions.
+ */
+
+ /**
+ * Render the header of the view.
+ */
+ public function renderHeader() {
+ $empty = empty($this->view->result);
+ return $this->renderArea('header', $empty);
+ }
+
+ /**
+ * Render the footer of the view.
+ */
+ public function renderFooter() {
+ $empty = empty($this->view->result);
+ return $this->renderArea('footer', $empty);
+ }
+
+ public function renderEmpty() {
+ return $this->renderArea('empty', TRUE);
+ }
+
+ /**
+ * If this display creates a block, implement one of these.
+ */
+ public function hookBlockList($delta = 0, $edit = array()) { return array(); }
+
+ /**
+ * If this display creates a page with a menu item, implement it here.
+ */
+ public function hookMenu() { return array(); }
+
+ /**
+ * Render this display.
+ */
+ public function render() {
+ return theme($this->themeFunctions(), array('view' => $this->view));
+ }
+
+ public function renderArea($area, $empty = FALSE) {
+ $return = '';
+ foreach ($this->getHandlers($area) as $area) {
+ $return .= $area->render($empty);
+ }
+ return $return;
+ }
+
+
+ /**
+ * Determine if the user has access to this display of the view.
+ */
+ public function access($account = NULL) {
+ if (!isset($account)) {
+ global $user;
+ $account = $user;
+ }
+
+ // Full override.
+ if (user_access('access all views', $account)) {
+ return TRUE;
+ }
+
+ $plugin = $this->getPlugin('access');
+ if ($plugin) {
+ return $plugin->access($account);
+ }
+
+ // fallback to all access if no plugin.
+ return TRUE;
+ }
+
+ /**
+ * Set up any variables on the view prior to execution. These are separated
+ * from execute because they are extremely common and unlikely to be
+ * overridden on an individual display.
+ */
+ public function preExecute() {
+ $this->view->setUseAJAX($this->isAJAXEnabled());
+ if ($this->usesMore() && !$this->useMoreAlways()) {
+ $this->view->get_total_rows = TRUE;
+ }
+ $this->view->initHandlers();
+ if ($this->usesExposed()) {
+ $exposed_form = $this->getPlugin('exposed_form');
+ $exposed_form->pre_execute();
+ }
+
+ foreach ($this->extender as $extender) {
+ $extender->pre_execute();
+ }
+
+ if ($this->getOption('hide_admin_links')) {
+ $this->view->hide_admin_links = TRUE;
+ }
+ }
+
+ /**
+ * When used externally, this is how a view gets run and returns
+ * data in the format required.
+ *
+ * The base class cannot be executed.
+ */
+ public function execute() { }
+
+ /**
+ * Fully render the display for the purposes of a live preview or
+ * some other AJAXy reason.
+ */
+ function preview() { return $this->view->render(); }
+
+ /**
+ * Displays can require a certain type of style plugin. By default, they will
+ * be 'normal'.
+ */
+ protected function getStyleType() { return 'normal'; }
+
+ /**
+ * Make sure the display and all associated handlers are valid.
+ *
+ * @return
+ * Empty array if the display is valid; an array of error strings if it is not.
+ */
+ public function validate() {
+ $errors = array();
+ // Make sure displays that use fields HAVE fields.
+ if ($this->usesFields()) {
+ $fields = FALSE;
+ foreach ($this->getHandlers('field') as $field) {
+ if (empty($field->options['exclude'])) {
+ $fields = TRUE;
+ }
+ }
+
+ if (!$fields) {
+ $errors[] = t('Display "@display" uses fields but there are none defined for it or all are excluded.', array('@display' => $this->display['display_title']));
+ }
+ }
+
+ if ($this->hasPath() && !$this->getOption('path')) {
+ $errors[] = t('Display "@display" uses a path but the path is undefined.', array('@display' => $this->display['display_title']));
+ }
+
+ // Validate style plugin
+ $style = $this->getPlugin('style');
+ if (empty($style)) {
+ $errors[] = t('Display "@display" has an invalid style plugin.', array('@display' => $this->display['display_title']));
+ }
+ else {
+ $result = $style->validate();
+ if (!empty($result) && is_array($result)) {
+ $errors = array_merge($errors, $result);
+ }
+ }
+
+ // Validate query plugin.
+ $query = $this->getPlugin('query');
+ $result = $query->validate();
+ if (!empty($result) && is_array($result)) {
+ $errors = array_merge($errors, $result);
+ }
+
+ // Validate handlers
+ foreach (ViewExecutable::viewsHandlerTypes() as $type => $info) {
+ foreach ($this->getHandlers($type) as $handler) {
+ $result = $handler->validate();
+ if (!empty($result) && is_array($result)) {
+ $errors = array_merge($errors, $result);
+ }
+ }
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Check if the provided identifier is unique.
+ *
+ * @param string $id
+ * The id of the handler which is checked.
+ * @param string $identifier
+ * The actual get identifier configured in the exposed settings.
+ *
+ * @return bool
+ * Returns whether the identifier is unique on all handlers.
+ *
+ */
+ public function isIdentifierUnique($id, $identifier) {
+ foreach (ViewExecutable::viewsHandlerTypes() as $type => $info) {
+ foreach ($this->getHandlers($type) as $key => $handler) {
+ if ($handler->canExpose() && $handler->isExposed()) {
+ if ($handler->isAGroup()) {
+ if ($id != $key && $identifier == $handler->options['group_info']['identifier']) {
+ return FALSE;
+ }
+ }
+ else {
+ if ($id != $key && $identifier == $handler->options['expose']['identifier']) {
+ return FALSE;
+ }
+ }
+ }
+ }
+ }
+ return TRUE;
+ }
+
+ /**
+ * Provide the block system with any exposed widget blocks for this display.
+ */
+ public function getSpecialBlocks() {
+ $blocks = array();
+
+ if ($this->usesExposedFormInBlock()) {
+ $delta = '-exp-' . $this->view->storage->name . '-' . $this->display['id'];
+ $desc = t('Exposed form: @view-@display_id', array('@view' => $this->view->storage->name, '@display_id' => $this->display['id']));
+
+ $blocks[$delta] = array(
+ 'info' => $desc,
+ 'cache' => DRUPAL_NO_CACHE,
+ );
+ }
+
+ return $blocks;
+ }
+
+ /**
+ * Render any special blocks provided for this display.
+ */
+ public function viewSpecialBlocks($type) {
+ if ($type == '-exp') {
+ // avoid interfering with the admin forms.
+ if (arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'views') {
+ return;
+ }
+ $this->view->initHandlers();
+
+ if ($this->usesExposed() && $this->getOption('exposed_block')) {
+ $exposed_form = $this->getPlugin('exposed_form');
+ return array(
+ 'content' => $exposed_form->render_exposed_form(TRUE),
+ );
+ }
+ }
+ }
+
+ /**
+ * Provide some helpful text for the arguments.
+ * The result should contain of an array with
+ * - filter value present: The title of the fieldset in the argument
+ * where you can configure what should be done with a given argument.
+ * - filter value not present: The tiel of the fieldset in the argument
+ * where you can configure what should be done if the argument does not
+ * exist.
+ * - description: A description about how arguments comes to the display.
+ * For example blocks don't get it from url.
+ */
+ public function getArgumentText() {
+ return array(
+ 'filter value not present' => t('When the filter value is <em>NOT</em> available'),
+ 'filter value present' => t('When the filter value <em>IS</em> available or a default is provided'),
+ 'description' => t("This display does not have a source for contextual filters, so no contextual filter value will be available unless you select 'Provide default'."),
+ );
+ }
+
+ /**
+ * Provide some helpful text for pagers.
+ *
+ * The result should contain of an array within
+ * - items per page title
+ */
+ public function getPagerText() {
+ return array(
+ 'items per page title' => t('Items to display'),
+ 'items per page description' => t('The number of items to display. Enter 0 for no limit.')
+ );
+ }
+
+}
+
+/**
+ * @}
+ */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Embed.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Embed.php
new file mode 100644
index 0000000..bb35995
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Embed.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\display\Embed.
+ */
+
+namespace Drupal\views\Plugin\views\display;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * The plugin that handles an embed display.
+ *
+ * @ingroup views_display_plugins
+ *
+ * @todo: Wait until annotations/plugins support access mehtods.
+ * no_ui => !config('views.settings')->get('ui.show.display_embed'),
+ *
+ * @Plugin(
+ * id = "embed",
+ * title = @Translation("Embed"),
+ * help = @Translation("Provide a display which can be embedded using the views api."),
+ * theme = "views_view",
+ * uses_hook_menu = FALSE
+ * )
+ */
+class Embed extends DisplayPluginBase {
+
+ // This display plugin does nothing apart from exist.
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Feed.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Feed.php
new file mode 100644
index 0000000..eea41a3
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Feed.php
@@ -0,0 +1,260 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\display\Feed.
+ */
+
+namespace Drupal\views\Plugin\views\display;
+
+use Drupal\views\ViewExecutable;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * The plugin that handles a feed, such as RSS or atom.
+ *
+ * For the most part, feeds are page displays but with some subtle differences.
+ *
+ * @ingroup views_display_plugins
+ *
+ * @Plugin(
+ * id = "feed",
+ * title = @Translation("Feed"),
+ * help = @Translation("Display the view as a feed, such as an RSS feed."),
+ * uses_hook_menu = TRUE,
+ * admin = @Translation("Feed")
+ * )
+ */
+class Feed extends Page {
+
+ /**
+ * Whether the display allows the use of AJAX or not.
+ *
+ * @var bool
+ */
+ protected $usesAJAX = FALSE;
+
+ /**
+ * Whether the display allows the use of a pager or not.
+ *
+ * @var bool
+ */
+ protected $usesPager = FALSE;
+
+ public function init(ViewExecutable $view, &$display, $options = NULL) {
+ parent::init($view, $display, $options);
+
+ // Set the default row style. Ideally this would be part of the option
+ // definition, but in this case it's dependent on the view's base table,
+ // which we don't know until init().
+ $row_plugins = views_fetch_plugin_names('row', $this->getStyleType(), array($view->storage->base_table));
+ $default_row_plugin = key($row_plugins);
+ if (empty($this->options['row']['type'])) {
+ $this->options['row']['type'] = $default_row_plugin;
+ }
+ }
+
+ public function usesBreadcrumb() { return FALSE; }
+ protected function getStyleType() { return 'feed'; }
+
+ /**
+ * Feeds do not go through the normal page theming mechanism. Instead, they
+ * go through their own little theme function and then return NULL so that
+ * Drupal believes that the page has already rendered itself...which it has.
+ */
+ public function execute() {
+ $output = $this->view->render();
+ if (empty($output)) {
+ throw new NotFoundHttpException();
+ }
+
+ $response = $this->view->getResponse();
+ $response->setContent($output);
+ return $response;
+ }
+
+ public function preview() {
+ if (!empty($this->view->live_preview)) {
+ return '<pre>' . check_plain($this->view->render()) . '</pre>';
+ }
+ return $this->view->render();
+ }
+
+ /**
+ * Instead of going through the standard views_view.tpl.php, delegate this
+ * to the style handler.
+ */
+ public function render() {
+ return $this->view->style_plugin->render($this->view->result);
+ }
+
+ public function defaultableSections($section = NULL) {
+ if (in_array($section, array('style', 'row'))) {
+ return FALSE;
+ }
+
+ $sections = parent::defaultableSections($section);
+
+ // Tell views our sitename_title option belongs in the title section.
+ if ($section == 'title') {
+ $sections[] = 'sitename_title';
+ }
+ elseif (!$section) {
+ $sections['title'][] = 'sitename_title';
+ }
+ return $sections;
+ }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['displays'] = array('default' => array());
+
+ // Overrides for standard stuff:
+ $options['style']['contains']['type']['default'] = 'rss';
+ $options['style']['contains']['options']['default'] = array('description' => '');
+ $options['sitename_title']['default'] = FALSE;
+ $options['row']['contains']['type']['default'] = '';
+ $options['defaults']['default']['style'] = FALSE;
+ $options['defaults']['default']['row'] = FALSE;
+
+ return $options;
+ }
+
+ public function optionsSummary(&$categories, &$options) {
+ // It is very important to call the parent function here:
+ parent::optionsSummary($categories, $options);
+
+ // Since we're childing off the 'page' type, we'll still *call* our
+ // category 'page' but let's override it so it says feed settings.
+ $categories['page'] = array(
+ 'title' => t('Feed settings'),
+ 'column' => 'second',
+ 'build' => array(
+ '#weight' => -10,
+ ),
+ );
+
+ if ($this->getOption('sitename_title')) {
+ $options['title']['value'] = t('Using the site name');
+ }
+
+ // I don't think we want to give feeds menus directly.
+ unset($options['menu']);
+
+ $displays = array_filter($this->getOption('displays'));
+ if (count($displays) > 1) {
+ $attach_to = t('Multiple displays');
+ }
+ elseif (count($displays) == 1) {
+ $display = array_shift($displays);
+ if (!empty($this->view->storage->display[$display])) {
+ $attach_to = check_plain($this->view->storage->display[$display]['display_title']);
+ }
+ }
+
+ if (!isset($attach_to)) {
+ $attach_to = t('None');
+ }
+
+ $options['displays'] = array(
+ 'category' => 'page',
+ 'title' => t('Attach to'),
+ 'value' => $attach_to,
+ );
+ }
+
+ /**
+ * Provide the default form for setting options.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ // It is very important to call the parent function here.
+ parent::buildOptionsForm($form, $form_state);
+
+ switch ($form_state['section']) {
+ case 'title':
+ $title = $form['title'];
+ // A little juggling to move the 'title' field beyond our checkbox.
+ unset($form['title']);
+ $form['sitename_title'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Use the site name for the title'),
+ '#default_value' => $this->getOption('sitename_title'),
+ );
+ $form['title'] = $title;
+ $form['title']['#states'] = array(
+ 'visible' => array(
+ ':input[name="sitename_title"]' => array('checked' => FALSE),
+ ),
+ );
+ break;
+ case 'displays':
+ $form['#title'] .= t('Attach to');
+ $displays = array();
+ foreach ($this->view->storage->display as $display_id => $display) {
+ // @todo The display plugin should have display_title and id as well.
+ if (!empty($this->view->displayHandlers[$display_id]) && $this->view->displayHandlers[$display_id]->acceptAttachments()) {
+ $displays[$display_id] = $display['display_title'];
+ }
+ }
+ $form['displays'] = array(
+ '#type' => 'checkboxes',
+ '#description' => t('The feed icon will be available only to the selected displays.'),
+ '#options' => $displays,
+ '#default_value' => $this->getOption('displays'),
+ );
+ break;
+ case 'path':
+ $form['path']['#description'] = t('This view will be displayed by visiting this path on your site. It is recommended that the path be something like "path/%/%/feed" or "path/%/%/rss.xml", putting one % in the path for each contextual filter you have defined in the view.');
+ }
+ }
+
+ /**
+ * Perform any necessary changes to the form values prior to storage.
+ * There is no need for this function to actually store the data.
+ */
+ public function submitOptionsForm(&$form, &$form_state) {
+ // It is very important to call the parent function here:
+ parent::submitOptionsForm($form, $form_state);
+ switch ($form_state['section']) {
+ case 'title':
+ $this->setOption('sitename_title', $form_state['values']['sitename_title']);
+ break;
+ case 'displays':
+ $this->setOption($form_state['section'], $form_state['values'][$form_state['section']]);
+ break;
+ }
+ }
+
+ /**
+ * Attach to another view.
+ */
+ public function attachTo($display_id) {
+ $displays = $this->getOption('displays');
+ if (empty($displays[$display_id])) {
+ return;
+ }
+
+ // Defer to the feed style; it may put in meta information, and/or
+ // attach a feed icon.
+ $plugin = $this->getPlugin('style');
+ if ($plugin) {
+ $clone = $this->view->cloneView();
+ $clone->setDisplay($this->display['id']);
+ $clone->buildTitle();
+ $plugin->attach_to($display_id, $this->getPath(), $clone->getTitle());
+
+ // Clean up
+ $clone->destroy();
+ unset($clone);
+ }
+ }
+
+ public function usesLinkDisplay() {
+ return TRUE;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php
new file mode 100644
index 0000000..9bc4346
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/Page.php
@@ -0,0 +1,654 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\display\Page.
+ */
+
+namespace Drupal\views\Plugin\views\display;
+
+use Drupal\Core\Annotation\Plugin;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * The plugin that handles a full page.
+ *
+ * @ingroup views_display_plugins
+ *
+ * @Plugin(
+ * id = "page",
+ * title = @Translation("Page"),
+ * help = @Translation("Display the view as a page, with a URL and menu links."),
+ * uses_hook_menu = TRUE,
+ * contextual_links_locations = {"page"},
+ * theme = "views_view",
+ * admin = @Translation("Page")
+ * )
+ */
+class Page extends DisplayPluginBase {
+
+ /**
+ * Whether the display allows attachments.
+ *
+ * @var bool
+ */
+ protected $usesAttachments = TRUE;
+
+ /**
+ * The page display has a path.
+ */
+ public function hasPath() { return TRUE; }
+ public function usesBreadcrumb() { return TRUE; }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['path'] = array('default' => '');
+ $options['menu'] = array(
+ 'contains' => array(
+ 'type' => array('default' => 'none'),
+ // Do not translate menu and title as menu system will.
+ 'title' => array('default' => '', 'translatable' => FALSE),
+ 'description' => array('default' => '', 'translatable' => FALSE),
+ 'weight' => array('default' => 0),
+ 'name' => array('default' => variable_get('menu_default_node_menu', 'navigation')),
+ 'context' => array('default' => ''),
+ ),
+ );
+ $options['tab_options'] = array(
+ 'contains' => array(
+ 'type' => array('default' => 'none'),
+ // Do not translate menu and title as menu system will.
+ 'title' => array('default' => '', 'translatable' => FALSE),
+ 'description' => array('default' => '', 'translatable' => FALSE),
+ 'weight' => array('default' => 0),
+ 'name' => array('default' => 'navigation'),
+ ),
+ );
+
+ return $options;
+ }
+
+ /**
+ * Add this display's path information to Drupal's menu system.
+ */
+ public function executeHookMenu($callbacks) {
+ $items = array();
+ // Replace % with the link to our standard views argument loader
+ // views_arg_load -- which lives in views.module
+
+ $bits = explode('/', $this->getOption('path'));
+ $page_arguments = array($this->view->storage->name, $this->display['id']);
+ $this->view->initHandlers();
+ $view_arguments = $this->view->argument;
+
+ // Replace % with %views_arg for menu autoloading and add to the
+ // page arguments so the argument actually comes through.
+ foreach ($bits as $pos => $bit) {
+ if ($bit == '%') {
+ $argument = array_shift($view_arguments);
+ if (!empty($argument->options['specify_validation']) && $argument->options['validate']['type'] != 'none') {
+ $bits[$pos] = '%views_arg';
+ }
+ $page_arguments[] = $pos;
+ }
+ }
+
+ $path = implode('/', $bits);
+
+ $access_plugin = $this->getPlugin('access');
+ if (!isset($access_plugin)) {
+ $access_plugin = views_get_plugin('access', 'none');
+ }
+
+ // Get access callback might return an array of the callback + the dynamic arguments.
+ $access_plugin_callback = $access_plugin->get_access_callback();
+
+ if (is_array($access_plugin_callback)) {
+ $access_arguments = array();
+
+ // Find the plugin arguments.
+ $access_plugin_method = array_shift($access_plugin_callback);
+ $access_plugin_arguments = array_shift($access_plugin_callback);
+ if (!is_array($access_plugin_arguments)) {
+ $access_plugin_arguments = array();
+ }
+
+ $access_arguments[0] = array($access_plugin_method, &$access_plugin_arguments);
+
+ // Move the plugin arguments to the access arguments array.
+ $i = 1;
+ foreach ($access_plugin_arguments as $key => $value) {
+ if (is_int($value)) {
+ $access_arguments[$i] = $value;
+ $access_plugin_arguments[$key] = $i;
+ $i++;
+ }
+ }
+ }
+ else {
+ $access_arguments = array($access_plugin_callback);
+ }
+
+ if ($path) {
+ $items[$path] = array(
+ // default views page entry
+ 'page callback' => 'views_page',
+ 'page arguments' => $page_arguments,
+ // Default access check (per display)
+ 'access callback' => 'views_access',
+ 'access arguments' => $access_arguments,
+ // Identify URL embedded arguments and correlate them to a handler
+ 'load arguments' => array($this->view->storage->name, $this->display['id'], '%index'),
+ );
+ $menu = $this->getOption('menu');
+ if (empty($menu)) {
+ $menu = array('type' => 'none');
+ }
+ // Set the title and description if we have one.
+ if ($menu['type'] != 'none') {
+ $items[$path]['title'] = $menu['title'];
+ $items[$path]['description'] = $menu['description'];
+ }
+
+ if (isset($menu['weight'])) {
+ $items[$path]['weight'] = intval($menu['weight']);
+ }
+
+ switch ($menu['type']) {
+ case 'none':
+ default:
+ $items[$path]['type'] = MENU_CALLBACK;
+ break;
+ case 'normal':
+ $items[$path]['type'] = MENU_NORMAL_ITEM;
+ // Insert item into the proper menu
+ $items[$path]['menu_name'] = $menu['name'];
+ break;
+ case 'tab':
+ $items[$path]['type'] = MENU_LOCAL_TASK;
+ break;
+ case 'default tab':
+ $items[$path]['type'] = MENU_DEFAULT_LOCAL_TASK;
+ break;
+ }
+
+ // Add context for contextual links.
+ // @see menu_contextual_links()
+ if (!empty($menu['context'])) {
+ $items[$path]['context'] = MENU_CONTEXT_INLINE;
+ }
+
+ // If this is a 'default' tab, check to see if we have to create teh
+ // parent menu item.
+ if ($menu['type'] == 'default tab') {
+ $tab_options = $this->getOption('tab_options');
+ if (!empty($tab_options['type']) && $tab_options['type'] != 'none') {
+ $bits = explode('/', $path);
+ // Remove the last piece.
+ $bit = array_pop($bits);
+
+ // we can't do this if they tried to make the last path bit variable.
+ // @todo: We can validate this.
+ if ($bit != '%views_arg' && !empty($bits)) {
+ $default_path = implode('/', $bits);
+ $items[$default_path] = array(
+ // default views page entry
+ 'page callback' => 'views_page',
+ 'page arguments' => $page_arguments,
+ // Default access check (per display)
+ 'access callback' => 'views_access',
+ 'access arguments' => $access_arguments,
+ // Identify URL embedded arguments and correlate them to a handler
+ 'load arguments' => array($this->view->storage->name, $this->display['id'], '%index'),
+ 'title' => $tab_options['title'],
+ 'description' => $tab_options['description'],
+ 'menu_name' => $tab_options['name'],
+ );
+ switch ($tab_options['type']) {
+ default:
+ case 'normal':
+ $items[$default_path]['type'] = MENU_NORMAL_ITEM;
+ break;
+ case 'tab':
+ $items[$default_path]['type'] = MENU_LOCAL_TASK;
+ break;
+ }
+ if (isset($tab_options['weight'])) {
+ $items[$default_path]['weight'] = intval($tab_options['weight']);
+ }
+ }
+ }
+ }
+ }
+
+ return $items;
+ }
+
+ /**
+ * The display page handler returns a normal view, but it also does
+ * a drupal_set_title for the page, and does a views_set_page_view
+ * on the view.
+ */
+ public function execute() {
+ // Let the world know that this is the page view we're using.
+ views_set_page_view($this->view);
+
+ // Prior to this being called, the $view should already be set to this
+ // display, and arguments should be set on the view.
+ $this->view->build();
+ if (!empty($this->view->build_info['fail'])) {
+ throw new NotFoundHttpException();
+ }
+
+ if (!empty($this->view->build_info['denied'])) {
+ throw new AccessDeniedHttpException();
+ }
+
+ $this->view->getBreadcrumb(TRUE);
+
+
+ // And now render the view.
+ $render = $this->view->render();
+
+ // First execute the view so it's possible to get tokens for the title.
+ // And the title, which is much easier.
+ drupal_set_title(filter_xss_admin($this->view->getTitle()), PASS_THROUGH);
+ return $render;
+ }
+
+ /**
+ * Provide the summary for page options in the views UI.
+ *
+ * This output is returned as an array.
+ */
+ public function optionsSummary(&$categories, &$options) {
+ // It is very important to call the parent function here:
+ parent::optionsSummary($categories, $options);
+
+ $categories['page'] = array(
+ 'title' => t('Page settings'),
+ 'column' => 'second',
+ 'build' => array(
+ '#weight' => -10,
+ ),
+ );
+
+ $path = strip_tags('/' . $this->getOption('path'));
+ if (empty($path)) {
+ $path = t('None');
+ }
+
+ $options['path'] = array(
+ 'category' => 'page',
+ 'title' => t('Path'),
+ 'value' => views_ui_truncate($path, 24),
+ );
+
+ $menu = $this->getOption('menu');
+ if (!is_array($menu)) {
+ $menu = array('type' => 'none');
+ }
+ switch ($menu['type']) {
+ case 'none':
+ default:
+ $menu_str = t('No menu');
+ break;
+ case 'normal':
+ $menu_str = t('Normal: @title', array('@title' => $menu['title']));
+ break;
+ case 'tab':
+ case 'default tab':
+ $menu_str = t('Tab: @title', array('@title' => $menu['title']));
+ break;
+ }
+
+ $options['menu'] = array(
+ 'category' => 'page',
+ 'title' => t('Menu'),
+ 'value' => views_ui_truncate($menu_str, 24),
+ );
+
+ // This adds a 'Settings' link to the style_options setting if the style has options.
+ if ($menu['type'] == 'default tab') {
+ $options['menu']['setting'] = t('Parent menu item');
+ $options['menu']['links']['tab_options'] = t('Change settings for the parent menu');
+ }
+ }
+
+ /**
+ * Provide the default form for setting options.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ // It is very important to call the parent function here:
+ parent::buildOptionsForm($form, $form_state);
+
+ switch ($form_state['section']) {
+ case 'path':
+ $form['#title'] .= t('The menu path or URL of this view');
+ $form['path'] = array(
+ '#type' => 'textfield',
+ '#description' => t('This view will be displayed by visiting this path on your site. You may use "%" in your URL to represent values that will be used for contextual filters: For example, "node/%/feed".'),
+ '#default_value' => $this->getOption('path'),
+ '#field_prefix' => '<span dir="ltr">' . url(NULL, array('absolute' => TRUE)),
+ '#field_suffix' => '</span>&lrm;',
+ '#attributes' => array('dir' => 'ltr'),
+ );
+ break;
+ case 'menu':
+ $form['#title'] .= t('Menu item entry');
+ $form['menu'] = array(
+ '#prefix' => '<div class="clearfix">',
+ '#suffix' => '</div>',
+ '#tree' => TRUE,
+ );
+ $menu = $this->getOption('menu');
+ if (empty($menu)) {
+ $menu = array('type' => 'none', 'title' => '', 'weight' => 0);
+ }
+ $form['menu']['type'] = array(
+ '#prefix' => '<div class="views-left-30">',
+ '#suffix' => '</div>',
+ '#title' => t('Type'),
+ '#type' => 'radios',
+ '#options' => array(
+ 'none' => t('No menu entry'),
+ 'normal' => t('Normal menu entry'),
+ 'tab' => t('Menu tab'),
+ 'default tab' => t('Default menu tab')
+ ),
+ '#default_value' => $menu['type'],
+ );
+
+ $form['menu']['title'] = array(
+ '#prefix' => '<div class="views-left-50">',
+ '#title' => t('Title'),
+ '#type' => 'textfield',
+ '#default_value' => $menu['title'],
+ '#description' => t('If set to normal or tab, enter the text to use for the menu item.'),
+ '#states' => array(
+ 'visible' => array(
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'normal'),
+ ),
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'tab'),
+ ),
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'default tab'),
+ ),
+ ),
+ ),
+ );
+ $form['menu']['description'] = array(
+ '#title' => t('Description'),
+ '#type' => 'textfield',
+ '#default_value' => $menu['description'],
+ '#description' => t("If set to normal or tab, enter the text to use for the menu item's description."),
+ '#states' => array(
+ 'visible' => array(
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'normal'),
+ ),
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'tab'),
+ ),
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'default tab'),
+ ),
+ ),
+ ),
+ );
+
+ // Only display the menu selector if menu module is enabled.
+ if (module_exists('menu')) {
+ $form['menu']['name'] = array(
+ '#title' => t('Menu'),
+ '#type' => 'select',
+ '#options' => menu_get_menus(),
+ '#default_value' => $menu['name'],
+ '#description' => t('Insert item into an available menu.'),
+ '#states' => array(
+ 'visible' => array(
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'normal'),
+ ),
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'tab'),
+ ),
+ ),
+ ),
+ );
+ }
+ else {
+ $form['menu']['name'] = array(
+ '#type' => 'value',
+ '#value' => $menu['name'],
+ );
+ $form['menu']['markup'] = array(
+ '#markup' => t('Menu selection requires the activation of menu module.'),
+ );
+ }
+ $form['menu']['weight'] = array(
+ '#title' => t('Weight'),
+ '#type' => 'textfield',
+ '#default_value' => isset($menu['weight']) ? $menu['weight'] : 0,
+ '#description' => t('The lower the weight the higher/further left it will appear.'),
+ '#states' => array(
+ 'visible' => array(
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'normal'),
+ ),
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'tab'),
+ ),
+ array(
+ ':input[name="menu[type]"]' => array('value' => 'default tab'),
+ ),
+ ),
+ ),
+ );
+ $form['menu']['context'] = array(
+ '#title' => t('Context'),
+ '#suffix' => '</div>',
+ '#type' => 'checkbox',
+ '#default_value' => !empty($menu['context']),
+ '#description' => t('Displays the link in contextual links'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="menu[type]"]' => array('value' => 'tab'),
+ ),
+ ),
+ );
+ break;
+ case 'tab_options':
+ $form['#title'] .= t('Default tab options');
+ $tab_options = $this->getOption('tab_options');
+ if (empty($tab_options)) {
+ $tab_options = array('type' => 'none', 'title' => '', 'weight' => 0);
+ }
+
+ $form['tab_markup'] = array(
+ '#markup' => '<div class="form-item description">' . t('When providing a menu item as a tab, Drupal needs to know what the parent menu item of that tab will be. Sometimes the parent will already exist, but other times you will need to have one created. The path of a parent item will always be the same path with the last part left off. i.e, if the path to this view is <em>foo/bar/baz</em>, the parent path would be <em>foo/bar</em>.') . '</div>',
+ );
+
+ $form['tab_options'] = array(
+ '#prefix' => '<div class="clearfix">',
+ '#suffix' => '</div>',
+ '#tree' => TRUE,
+ );
+ $form['tab_options']['type'] = array(
+ '#prefix' => '<div class="views-left-25">',
+ '#suffix' => '</div>',
+ '#title' => t('Parent menu item'),
+ '#type' => 'radios',
+ '#options' => array('none' => t('Already exists'), 'normal' => t('Normal menu item'), 'tab' => t('Menu tab')),
+ '#default_value' => $tab_options['type'],
+ );
+ $form['tab_options']['title'] = array(
+ '#prefix' => '<div class="views-left-75">',
+ '#title' => t('Title'),
+ '#type' => 'textfield',
+ '#default_value' => $tab_options['title'],
+ '#description' => t('If creating a parent menu item, enter the title of the item.'),
+ '#states' => array(
+ 'visible' => array(
+ array(
+ ':input[name="tab_options[type]"]' => array('value' => 'normal'),
+ ),
+ array(
+ ':input[name="tab_options[type]"]' => array('value' => 'tab'),
+ ),
+ ),
+ ),
+ );
+ $form['tab_options']['description'] = array(
+ '#title' => t('Description'),
+ '#type' => 'textfield',
+ '#default_value' => $tab_options['description'],
+ '#description' => t('If creating a parent menu item, enter the description of the item.'),
+ '#states' => array(
+ 'visible' => array(
+ array(
+ ':input[name="tab_options[type]"]' => array('value' => 'normal'),
+ ),
+ array(
+ ':input[name="tab_options[type]"]' => array('value' => 'tab'),
+ ),
+ ),
+ ),
+ );
+ // Only display the menu selector if menu module is enabled.
+ if (module_exists('menu')) {
+ $form['tab_options']['name'] = array(
+ '#title' => t('Menu'),
+ '#type' => 'select',
+ '#options' => menu_get_menus(),
+ '#default_value' => $tab_options['name'],
+ '#description' => t('Insert item into an available menu.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="tab_options[type]"]' => array('value' => 'normal'),
+ ),
+ ),
+ );
+ }
+ else {
+ $form['tab_options']['name'] = array(
+ '#type' => 'value',
+ '#value' => $tab_options['name'],
+ );
+ $form['tab_options']['markup'] = array(
+ '#markup' => t('Menu selection requires the activation of menu module.'),
+ );
+ }
+ $form['tab_options']['weight'] = array(
+ '#suffix' => '</div>',
+ '#title' => t('Tab weight'),
+ '#type' => 'textfield',
+ '#default_value' => $tab_options['weight'],
+ '#size' => 5,
+ '#description' => t('If the parent menu item is a tab, enter the weight of the tab. The lower the number, the more to the left it will be.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="tab_options[type]"]' => array('value' => 'tab'),
+ ),
+ ),
+ );
+ break;
+ }
+ }
+
+ public function validateOptionsForm(&$form, &$form_state) {
+ // It is very important to call the parent function here:
+ parent::validateOptionsForm($form, $form_state);
+ switch ($form_state['section']) {
+ case 'path':
+ if (strpos($form_state['values']['path'], '$arg') !== FALSE) {
+ form_error($form['path'], t('"$arg" is no longer supported. Use % instead.'));
+ }
+
+ if (strpos($form_state['values']['path'], '%') === 0) {
+ form_error($form['path'], t('"%" may not be used for the first segment of a path.'));
+ }
+
+ // automatically remove '/' and trailing whitespace from path.
+ $form_state['values']['path'] = trim($form_state['values']['path'], '/ ');
+ break;
+ case 'menu':
+ $path = $this->getOption('path');
+ if ($form_state['values']['menu']['type'] == 'normal' && strpos($path, '%') !== FALSE) {
+ form_error($form['menu']['type'], t('Views cannot create normal menu items for paths with a % in them.'));
+ }
+
+ if ($form_state['values']['menu']['type'] == 'default tab' || $form_state['values']['menu']['type'] == 'tab') {
+ $bits = explode('/', $path);
+ $last = array_pop($bits);
+ if ($last == '%') {
+ form_error($form['menu']['type'], t('A display whose path ends with a % cannot be a tab.'));
+ }
+ }
+
+ if ($form_state['values']['menu']['type'] != 'none' && empty($form_state['values']['menu']['title'])) {
+ form_error($form['menu']['title'], t('Title is required for this menu type.'));
+ }
+ break;
+ }
+ }
+
+ public function submitOptionsForm(&$form, &$form_state) {
+ // It is very important to call the parent function here:
+ parent::submitOptionsForm($form, $form_state);
+ switch ($form_state['section']) {
+ case 'path':
+ $this->setOption('path', $form_state['values']['path']);
+ break;
+ case 'menu':
+ $this->setOption('menu', $form_state['values']['menu']);
+ // send ajax form to options page if we use it.
+ if ($form_state['values']['menu']['type'] == 'default tab') {
+ $this->view->addFormToStack('display', $this->display['id'], array('tab_options'));
+ }
+ break;
+ case 'tab_options':
+ $this->setOption('tab_options', $form_state['values']['tab_options']);
+ break;
+ }
+ }
+
+ public function validate() {
+ $errors = parent::validate();
+
+ $menu = $this->getOption('menu');
+ if (!empty($menu['type']) && $menu['type'] != 'none' && empty($menu['title'])) {
+ $errors[] = t('Display @display is set to use a menu but the menu link text is not set.', array('@display' => $this->display['display_title']));
+ }
+
+ if ($menu['type'] == 'default tab') {
+ $tab_options = $this->getOption('tab_options');
+ if (!empty($tab_options['type']) && $tab_options['type'] != 'none' && empty($tab_options['title'])) {
+ $errors[] = t('Display @display is set to use a parent menu but the parent menu link text is not set.', array('@display' => $this->display['display_title']));
+ }
+ }
+
+ return $errors;
+ }
+
+ public function getArgumentText() {
+ return array(
+ 'filter value not present' => t('When the filter value is <em>NOT</em> in the URL'),
+ 'filter value present' => t('When the filter value <em>IS</em> in the URL or a default is provided'),
+ 'description' => t('The contextual filter values is provided by the URL.'),
+ );
+ }
+
+ public function getPagerText() {
+ return array(
+ 'items per page title' => t('Items per page'),
+ 'items per page description' => t('The number of items to display per page. Enter 0 for no limit.')
+ );
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display_extender/DefaultDisplayExtender.php b/core/modules/views/lib/Drupal/views/Plugin/views/display_extender/DefaultDisplayExtender.php
new file mode 100644
index 0000000..b8faf98
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display_extender/DefaultDisplayExtender.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\display_extender\DefaultDisplayExtender.
+ */
+
+namespace Drupal\views\Plugin\views\display_extender;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @todo
+ *
+ * @Plugin(
+ * id = "default",
+ * title = @Translation("Empty display extender"),
+ * help = @Translation("Default settings for this view."),
+ * enabled = FALSE,
+ * no_ui = TRUE
+ * )
+ */
+class DefaultDisplayExtender extends DisplayExtenderPluginBase {
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display_extender/DisplayExtenderPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display_extender/DisplayExtenderPluginBase.php
new file mode 100644
index 0000000..0647022
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display_extender/DisplayExtenderPluginBase.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\display_extender;
+
+use Drupal\views\ViewExecutable;
+use Drupal\views\Plugin\views\PluginBase;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * @todo.
+ *
+ * @ingroup views_display_plugins
+ */
+abstract class DisplayExtenderPluginBase extends PluginBase {
+
+ public function init(ViewExecutable $view, &$display) {
+ $this->setOptionDefaults($this->options, $this->defineOptions());
+ $this->view = $view;
+ $this->displayHandler = $display;
+ }
+
+ /**
+ * Provide a form to edit options for this plugin.
+ */
+ public function defineOptionsAlter(&$options) { }
+
+ /**
+ * Provide a form to edit options for this plugin.
+ */
+ public function buildOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Validate the options form.
+ */
+ public function validateOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Handle any special handling on the validate form.
+ */
+ public function submitOptionsForm(&$form, &$form_state) { }
+
+ /**
+ * Set up any variables on the view prior to execution.
+ */
+ public function pre_execute() { }
+
+ /**
+ * Inject anything into the query that the display_extender handler needs.
+ */
+ public function query() { }
+
+ /**
+ * Provide the default summary for options in the views UI.
+ *
+ * This output is returned as an array.
+ */
+ public function optionsSummary(&$categories, &$options) { }
+
+ /**
+ * Static member function to list which sections are defaultable
+ * and what items each section contains.
+ */
+ public function defaultableSections(&$sections, $section = NULL) { }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/Basic.php b/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/Basic.php
new file mode 100644
index 0000000..f850d89
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/Basic.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\exposed_form\Basic.
+ */
+
+namespace Drupal\views\Plugin\views\exposed_form;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Exposed form plugin that provides a basic exposed form.
+ *
+ * @ingroup views_exposed_form_plugins
+ *
+ * @Plugin(
+ * id = "basic",
+ * title = @Translation("Basic"),
+ * help = @Translation("Basic exposed form")
+ * )
+ */
+class Basic extends ExposedFormPluginBase {
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/ExposedFormPluginBase.php
new file mode 100644
index 0000000..4cad847
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/ExposedFormPluginBase.php
@@ -0,0 +1,322 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\exposed_form;
+
+use Drupal\views\ViewExecutable;
+use Drupal\views\Plugin\views\PluginBase;
+
+/**
+ * @defgroup views_exposed_form_plugins Views exposed form plugins
+ * @{
+ * Plugins that handle the validation/submission and rendering of exposed forms.
+ *
+ * If needed, it is possible to use them to add additional form elements.
+ */
+
+/**
+ * The base plugin to handle exposed filter forms.
+ */
+abstract class ExposedFormPluginBase extends PluginBase {
+
+ /**
+ * Overrides Drupal\views\Plugin\Plugin::$usesOptions.
+ */
+ protected $usesOptions = TRUE;
+
+ /**
+ * Initialize the plugin.
+ *
+ * @param $view
+ * The view object.
+ * @param $display
+ * The display handler.
+ */
+ public function init(ViewExecutable $view, &$display, $options = array()) {
+ $this->setOptionDefaults($this->options, $this->defineOptions());
+ $this->view = &$view;
+ $this->displayHandler = &$display;
+
+ $this->unpackOptions($this->options, $options);
+ }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['submit_button'] = array('default' => 'Apply', 'translatable' => TRUE);
+ $options['reset_button'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['reset_button_label'] = array('default' => 'Reset', 'translatable' => TRUE);
+ $options['exposed_sorts_label'] = array('default' => 'Sort by', 'translatable' => TRUE);
+ $options['expose_sort_order'] = array('default' => TRUE, 'bool' => TRUE);
+ $options['sort_asc_label'] = array('default' => 'Asc', 'translatable' => TRUE);
+ $options['sort_desc_label'] = array('default' => 'Desc', 'translatable' => TRUE);
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+ $form['submit_button'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Submit button text'),
+ '#description' => t('Text to display in the submit button of the exposed form.'),
+ '#default_value' => $this->options['submit_button'],
+ '#required' => TRUE,
+ );
+
+ $form['reset_button'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Include reset button'),
+ '#description' => t('If checked the exposed form will provide a button to reset all the applied exposed filters'),
+ '#default_value' => $this->options['reset_button'],
+ );
+
+ $form['reset_button_label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Reset button label'),
+ '#description' => t('Text to display in the reset button of the exposed form.'),
+ '#default_value' => $this->options['reset_button_label'],
+ '#required' => TRUE,
+ '#states' => array(
+ 'invisible' => array(
+ 'input[name="exposed_form_options[reset_button]"]' => array('checked' => FALSE),
+ ),
+ ),
+ );
+
+ $form['exposed_sorts_label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Exposed sorts label'),
+ '#description' => t('Text to display as the label of the exposed sort select box.'),
+ '#default_value' => $this->options['exposed_sorts_label'],
+ '#required' => TRUE,
+ );
+
+ $form['expose_sort_order'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Expose sort order'),
+ '#description' => t('Allow the user to choose the sort order. If sort order is not exposed, the sort criteria settings for each sort will determine its order.'),
+ '#default_value' => $this->options['expose_sort_order'],
+ );
+
+ $form['sort_asc_label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Ascending'),
+ '#description' => t('Text to use when exposed sort is ordered ascending.'),
+ '#default_value' => $this->options['sort_asc_label'],
+ '#required' => TRUE,
+ '#states' => array(
+ 'visible' => array(
+ 'input[name="exposed_form_options[expose_sort_order]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+
+ $form['sort_desc_label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Descending'),
+ '#description' => t('Text to use when exposed sort is ordered descending.'),
+ '#default_value' => $this->options['sort_desc_label'],
+ '#required' => TRUE,
+ '#states' => array(
+ 'visible' => array(
+ 'input[name="exposed_form_options[expose_sort_order]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * Render the exposed filter form.
+ *
+ * This actually does more than that; because it's using FAPI, the form will
+ * also assign data to the appropriate handlers for use in building the
+ * query.
+ */
+ function render_exposed_form($block = FALSE) {
+ // Deal with any exposed filters we may have, before building.
+ $form_state = array(
+ 'view' => &$this->view,
+ 'display' => &$this->display,
+ 'method' => 'get',
+ 'rerender' => TRUE,
+ 'no_redirect' => TRUE,
+ 'always_process' => TRUE,
+ );
+
+ // Some types of displays (eg. attachments) may wish to use the exposed
+ // filters of their parent displays instead of showing an additional
+ // exposed filter form for the attachment as well as that for the parent.
+ if (!$this->view->display_handler->displaysExposed() || (!$block && $this->view->display_handler->getOption('exposed_block'))) {
+ unset($form_state['rerender']);
+ }
+
+ if (!empty($this->ajax)) {
+ $form_state['ajax'] = TRUE;
+ }
+
+ $form_state['exposed_form_plugin'] = $this;
+ $form = drupal_build_form('views_exposed_form', $form_state);
+ $output = drupal_render($form);
+
+ if (!$this->view->display_handler->displaysExposed() || (!$block && $this->view->display_handler->getOption('exposed_block'))) {
+ return "";
+ }
+ else {
+ return $output;
+ }
+ }
+
+ public function query() {
+ $view = $this->view;
+ $exposed_data = isset($view->exposed_data) ? $view->exposed_data : array();
+ $sort_by = isset($exposed_data['sort_by']) ? $exposed_data['sort_by'] : NULL;
+ if (!empty($sort_by)) {
+ // Make sure the original order of sorts is preserved
+ // (e.g. a sticky sort is often first)
+ if (isset($view->sort[$sort_by])) {
+ $view->query->orderby = array();
+ foreach ($view->sort as $key => $sort) {
+ if (!$sort->isExposed()) {
+ $sort->query();
+ }
+ elseif ($key == $sort_by) {
+ if (isset($exposed_data['sort_order']) && in_array($exposed_data['sort_order'], array('ASC', 'DESC'))) {
+ $sort->options['order'] = $exposed_data['sort_order'];
+ }
+ $sort->setRelationship();
+ $sort->query();
+ }
+ }
+ }
+ }
+ }
+
+ function pre_render($values) { }
+
+ function post_render(&$output) { }
+
+ function pre_execute() { }
+
+ public function postExecute() { }
+
+ function exposed_form_alter(&$form, &$form_state) {
+ if (!empty($this->options['reset_button'])) {
+ $form['reset'] = array(
+ '#value' => $this->options['reset_button_label'],
+ '#type' => 'submit',
+ );
+ }
+
+ $form['submit']['#value'] = $this->options['submit_button'];
+ // Check if there is exposed sorts for this view
+ $exposed_sorts = array();
+ foreach ($this->view->sort as $id => $handler) {
+ if ($handler->canExpose() && $handler->isExposed()) {
+ $exposed_sorts[$id] = check_plain($handler->options['expose']['label']);
+ }
+ }
+
+ if (count($exposed_sorts)) {
+ $form['sort_by'] = array(
+ '#type' => 'select',
+ '#options' => $exposed_sorts,
+ '#title' => $this->options['exposed_sorts_label'],
+ );
+ $sort_order = array(
+ 'ASC' => $this->options['sort_asc_label'],
+ 'DESC' => $this->options['sort_desc_label'],
+ );
+ if (isset($form_state['input']['sort_by']) && isset($this->view->sort[$form_state['input']['sort_by']])) {
+ $default_sort_order = $this->view->sort[$form_state['input']['sort_by']]->options['order'];
+ }
+ else {
+ $first_sort = reset($this->view->sort);
+ $default_sort_order = $first_sort->options['order'];
+ }
+
+ if (!isset($form_state['input']['sort_by'])) {
+ $keys = array_keys($exposed_sorts);
+ $form_state['input']['sort_by'] = array_shift($keys);
+ }
+
+ if ($this->options['expose_sort_order']) {
+ $form['sort_order'] = array(
+ '#type' => 'select',
+ '#options' => $sort_order,
+ '#title' => t('Order'),
+ '#default_value' => $default_sort_order,
+ );
+ }
+ $form['submit']['#weight'] = 10;
+ if (isset($form['reset'])) {
+ $form['reset']['#weight'] = 10;
+ }
+ }
+
+ $pager = $this->view->display_handler->getPlugin('pager');
+ if ($pager) {
+ $pager->exposed_form_alter($form, $form_state);
+ $form_state['pager_plugin'] = $pager;
+ }
+ }
+
+ function exposed_form_validate(&$form, &$form_state) {
+ if (isset($form_state['pager_plugin'])) {
+ $form_state['pager_plugin']->exposed_form_validate($form, $form_state);
+ }
+ }
+
+ /**
+ * This function is executed when exposed form is submited.
+ *
+ * @param $form
+ * Nested array of form elements that comprise the form.
+ * @param $form_state
+ * A keyed array containing the current state of the form.
+ * @param $exclude
+ * Nested array of keys to exclude of insert into
+ * $view->exposed_raw_input
+ */
+ function exposed_form_submit(&$form, &$form_state, &$exclude) {
+ if (!empty($form_state['values']['op']) && $form_state['values']['op'] == $this->options['reset_button_label']) {
+ $this->reset_form($form, $form_state);
+ }
+ if (isset($form_state['pager_plugin'])) {
+ $form_state['pager_plugin']->exposed_form_submit($form, $form_state, $exclude);
+ $exclude[] = 'pager_plugin';
+ }
+ }
+
+ function reset_form(&$form, &$form_state) {
+ // _SESSION is not defined for users who are not logged in.
+
+ // If filters are not overridden, store the 'remember' settings on the
+ // default display. If they are, store them on this display. This way,
+ // multiple displays in the same view can share the same filters and
+ // remember settings.
+ $display_id = ($this->view->display_handler->isDefaulted('filters')) ? 'default' : $this->view->current_display;
+
+ if (isset($_SESSION['views'][$this->view->storage->name][$display_id])) {
+ unset($_SESSION['views'][$this->view->storage->name][$display_id]);
+ }
+
+ // Set the form to allow redirect.
+ if (empty($this->view->live_preview)) {
+ $form_state['no_redirect'] = FALSE;
+ }
+ else {
+ $form_state['rebuild'] = TRUE;
+ $this->view->exposed_data = array();
+ }
+
+ $form_state['values'] = array();
+ }
+
+}
+
+/**
+ * @}
+ */
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/InputRequired.php b/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/InputRequired.php
new file mode 100644
index 0000000..3baebe0
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/InputRequired.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\exposed_form\InputRequired.
+ */
+
+namespace Drupal\views\Plugin\views\exposed_form;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Exposed form plugin that provides an exposed form with required input.
+ *
+ * @ingroup views_exposed_form_plugins
+ *
+ * @Plugin(
+ * id = "input_required",
+ * title = @Translation("Input required"),
+ * help = @Translation("An exposed form that only renders a view if the form contains user input.")
+ * )
+ */
+class InputRequired extends ExposedFormPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['text_input_required'] = array('default' => 'Select any filter and click on Apply to see results', 'translatable' => TRUE);
+ $options['text_input_required_format'] = array('default' => NULL);
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $form['text_input_required'] = array(
+ '#type' => 'text_format',
+ '#title' => t('Text on demand'),
+ '#description' => t('Text to display instead of results until the user selects and applies an exposed filter.'),
+ '#default_value' => $this->options['text_input_required'],
+ '#format' => isset($this->options['text_input_required_format']) ? $this->options['text_input_required_format'] : filter_default_format(),
+ '#wysiwyg' => FALSE,
+ );
+ }
+
+ public function submitOptionsForm(&$form, &$form_state) {
+ $form_state['values']['exposed_form_options']['text_input_required_format'] = $form_state['values']['exposed_form_options']['text_input_required']['format'];
+ $form_state['values']['exposed_form_options']['text_input_required'] = $form_state['values']['exposed_form_options']['text_input_required']['value'];
+ parent::submitOptionsForm($form, $form_state);
+ }
+
+ function exposed_filter_applied() {
+ static $cache = NULL;
+ if (!isset($cache)) {
+ $view = $this->view;
+ if (is_array($view->filter) && count($view->filter)) {
+ foreach ($view->filter as $filter_id => $filter) {
+ if ($filter->isExposed()) {
+ $identifier = $filter->options['expose']['identifier'];
+ if (isset($view->exposed_input[$identifier])) {
+ $cache = TRUE;
+ return $cache;
+ }
+ }
+ }
+ }
+ $cache = FALSE;
+ }
+
+ return $cache;
+ }
+
+ function pre_render($values) {
+ if (!$this->exposed_filter_applied()) {
+ $options = array(
+ 'id' => 'area',
+ 'table' => 'views',
+ 'field' => 'area',
+ 'label' => '',
+ 'relationship' => 'none',
+ 'group_type' => 'group',
+ 'content' => $this->options['text_input_required'],
+ 'format' => $this->options['text_input_required_format'],
+ );
+ $handler = views_get_handler('views', 'area', 'area');
+ $handler->init($this->view, $options);
+ $this->displayHandler->handlers['empty'] = array(
+ 'area' => $handler,
+ );
+ $this->displayHandler->setOption('empty', array('text' => $options));
+ }
+ }
+
+ public function query() {
+ if (!$this->exposed_filter_applied()) {
+ // We return with no query; this will force the empty text.
+ $this->view->built = TRUE;
+ $this->view->executed = TRUE;
+ $this->view->result = array();
+ }
+ else {
+ parent::query();
+ }
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/Boolean.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/Boolean.php
new file mode 100644
index 0000000..5a9ae65
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/Boolean.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\field\Boolean.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\views\ViewExecutable;
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * A handler to provide proper displays for booleans.
+ *
+ * Allows for display of true/false, yes/no, on/off, enabled/disabled.
+ *
+ * Definition terms:
+ * - output formats: An array where the first entry is displayed on boolean true
+ * and the second is displayed on boolean false. An example for sticky is:
+ * @code
+ * 'output formats' => array(
+ * 'sticky' => array(t('Sticky'), ''),
+ * ),
+ * @endcode
+ *
+ * @ingroup views_field_handlers
+ *
+ * @Plugin(
+ * id = "boolean"
+ * )
+ */
+class Boolean extends FieldPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['type'] = array('default' => 'yes-no');
+ $options['not'] = array('definition bool' => 'reverse');
+
+ return $options;
+ }
+
+ public function init(ViewExecutable $view, &$options) {
+ parent::init($view, $options);
+
+ $default_formats = array(
+ 'yes-no' => array(t('Yes'), t('No')),
+ 'true-false' => array(t('True'), t('False')),
+ 'on-off' => array(t('On'), t('Off')),
+ 'enabled-disabled' => array(t('Enabled'), t('Disabled')),
+ 'boolean' => array(1, 0),
+ 'unicode-yes-no' => array('✔', '✖'),
+ );
+ $output_formats = isset($this->definition['output formats']) ? $this->definition['output formats'] : array();
+ $this->formats = array_merge($default_formats, $output_formats);
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ foreach ($this->formats as $key => $item) {
+ $options[$key] = implode('/', $item);
+ }
+
+ $form['type'] = array(
+ '#type' => 'select',
+ '#title' => t('Output format'),
+ '#options' => $options,
+ '#default_value' => $this->options['type'],
+ );
+ $form['not'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Reverse'),
+ '#description' => t('If checked, true will be displayed as false.'),
+ '#default_value' => $this->options['not'],
+ );
+ parent::buildOptionsForm($form, $form_state);
+ }
+
+ function render($values) {
+ $value = $this->get_value($values);
+ if (!empty($this->options['not'])) {
+ $value = !$value;
+ }
+
+ if (isset($this->formats[$this->options['type']])) {
+ return $value ? $this->formats[$this->options['type']][0] : $this->formats[$this->options['type']][1];
+ }
+ else {
+ return $value ? $this->formats['yes-no'][0] : $this->formats['yes-no'][1];
+ }
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/Broken.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/Broken.php
new file mode 100644
index 0000000..f131f25
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/Broken.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\field\Broken.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * A special handler to take the place of missing or broken handlers.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @Plugin(
+ * id = "broken"
+ * )
+ */
+class Broken extends FieldPluginBase {
+
+ public function adminLabel($short = FALSE) {
+ return t('Broken/missing handler');
+ }
+
+ public function defineOptions() { return array(); }
+ public function ensureMyTable() { /* No table to ensure! */ }
+ public function query($group_by = FALSE) { /* No query to run */ }
+ public function buildOptionsForm(&$form, &$form_state) {
+ $form['markup'] = array(
+ '#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
+ );
+ }
+
+ /**
+ * Determine if the handler is considered 'broken'
+ */
+ public function broken() { return TRUE; }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/ContextualLinks.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/ContextualLinks.php
new file mode 100644
index 0000000..ce7d957
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/ContextualLinks.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\field\ContextualLinks.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Provides a handler that adds contextual links.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @Plugin(
+ * id = "contextual_links"
+ * )
+ */
+class ContextualLinks extends FieldPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['fields'] = array('default' => array());
+ $options['destination'] = array('default' => 1);
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ $all_fields = $this->view->display_handler->getFieldLabels();
+ // Offer to include only those fields that follow this one.
+ $field_options = array_slice($all_fields, 0, array_search($this->options['id'], array_keys($all_fields)));
+ $form['fields'] = array(
+ '#type' => 'checkboxes',
+ '#title' => t('Fields'),
+ '#description' => t('Fields to be included as contextual links.'),
+ '#options' => $field_options,
+ '#default_value' => $this->options['fields'],
+ );
+ $form['destination'] = array(
+ '#type' => 'select',
+ '#title' => t('Include destination'),
+ '#description' => t('Include a "destination" parameter in the link to return the user to the original view upon completing the contextual action.'),
+ '#options' => array(
+ '0' => t('No'),
+ '1' => t('Yes'),
+ ),
+ '#default_value' => $this->options['destination'],
+ );
+ }
+
+ function pre_render(&$values) {
+ // Add a row plugin css class for the contextual link.
+ $class = 'contextual-region';
+ if (!empty($this->view->style_plugin->options['row_class'])) {
+ $this->view->style_plugin->options['row_class'] .= " $class";
+ }
+ else {
+ $this->view->style_plugin->options['row_class'] = $class;
+ }
+ }
+
+ /**
+ * Render the contextual fields.
+ */
+ function render($values) {
+ $links = array();
+ foreach ($this->options['fields'] as $field) {
+ if (empty($this->view->style_plugin->rendered_fields[$this->view->row_index][$field])) {
+ continue;
+ }
+ $title = $this->view->field[$field]->last_render_text;
+ $path = '';
+ if (!empty($this->view->field[$field]->options['alter']['path'])) {
+ $path = $this->view->field[$field]->options['alter']['path'];
+ }
+ if (!empty($title) && !empty($path)) {
+ // Make sure that tokens are replaced for this paths as well.
+ $tokens = $this->get_render_tokens(array());
+ $path = strip_tags(decode_entities(strtr($path, $tokens)));
+
+ $links[$field] = array(
+ 'href' => $path,
+ 'title' => $title,
+ );
+ if (!empty($this->options['destination'])) {
+ $links[$field]['query'] = drupal_get_destination();
+ }
+ }
+ }
+
+ if (!empty($links)) {
+ $build = array(
+ '#prefix' => '<div class="contextual">',
+ '#suffix' => '</div>',
+ '#theme' => 'links__contextual',
+ '#links' => $links,
+ '#attributes' => array('class' => array('contextual-links')),
+ '#attached' => array(
+ 'library' => array(array('contextual', 'contextual-links')),
+ ),
+ '#access' => user_access('access contextual links'),
+ );
+ return drupal_render($build);
+ }
+ else {
+ return '';
+ }
+ }
+
+ public function query() { }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/Counter.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/Counter.php
new file mode 100644
index 0000000..9d27f06
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/Counter.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\field\Counter.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * Field handler to show a counter of the current row.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @Plugin(
+ * id = "counter"
+ * )
+ */
+class Counter extends FieldPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+ $options['counter_start'] = array('default' => 1);
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ $form['counter_start'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Starting value'),
+ '#default_value' => $this->options['counter_start'],
+ '#description' => t('Specify the number the counter should start at.'),
+ '#size' => 2,
+ );
+
+ parent::buildOptionsForm($form, $form_state);
+ }
+
+ public function query() {
+ // do nothing -- to override the parent query.
+ }
+
+ /**
+ * Overrides Drupal\views\Plugin\views\field\FieldPluginBas::get_value()
+ */
+ public function get_value($values, $field = NULL) {
+ // Note: 1 is subtracted from the counter start value below because the
+ // counter value is incremented by 1 at the end of this function.
+ $count = is_numeric($this->options['counter_start']) ? $this->options['counter_start'] - 1 : 0;
+ $pager = $this->view->pager;
+ // Get the base count of the pager.
+ if ($pager->use_pager()) {
+ $count += ($pager->get_items_per_page() * $pager->get_current_page() + $pager->set_offset());
+ }
+ // Add the counter for the current site.
+ $count += $this->view->row_index + 1;
+
+ return $count;
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/Custom.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/Custom.php
new file mode 100644
index 0000000..6c10baa
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/Custom.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\field\Custom.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * A handler to provide a field that is completely custom by the administrator.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @Plugin(
+ * id = "custom"
+ * )
+ */
+class Custom extends FieldPluginBase {
+
+ public function query() {
+ // do nothing -- to override the parent query.
+ }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ // Override the alter text option to always alter the text.
+ $options['alter']['contains']['alter_text'] = array('default' => TRUE, 'bool' => TRUE);
+ $options['hide_alter_empty'] = array('default' => FALSE, 'bool' => TRUE);
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ // Remove the checkbox
+ unset($form['alter']['alter_text']);
+ unset($form['alter']['text']['#states']);
+ unset($form['alter']['help']['#states']);
+ $form['#pre_render'][] = 'views_handler_field_custom_pre_render_move_text';
+ }
+
+ function render($values) {
+ // Return the text, so the code never thinks the value is empty.
+ return $this->options['alter']['text'];
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/Date.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/Date.php
new file mode 100644
index 0000000..dd5f2fa
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/Date.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\field\Date.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * A handler to provide proper displays for dates.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @Plugin(
+ * id = "date"
+ * )
+ */
+class Date extends FieldPluginBase {
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['date_format'] = array('default' => 'small');
+ $options['custom_date_format'] = array('default' => '');
+ $options['timezone'] = array('default' => '');
+
+ return $options;
+ }
+
+ public function buildOptionsForm(&$form, &$form_state) {
+
+ $date_formats = array();
+ $date_types = system_get_date_types();
+ foreach ($date_types as $key => $value) {
+ $date_formats[$value['type']] = check_plain(t($value['title'] . ' format')) . ': ' . format_date(REQUEST_TIME, $value['type']);
+ }
+
+ $form['date_format'] = array(
+ '#type' => 'select',
+ '#title' => t('Date format'),
+ '#options' => $date_formats + array(
+ 'custom' => t('Custom'),
+ 'raw time ago' => t('Time ago'),
+ 'time ago' => t('Time ago (with "ago" appended)'),
+ 'raw time hence' => t('Time hence'),
+ 'time hence' => t('Time hence (with "hence" appended)'),
+ 'raw time span' => t('Time span (future dates have "-" prepended)'),
+ 'inverse time span' => t('Time span (past dates have "-" prepended)'),
+ 'time span' => t('Time span (with "ago/hence" appended)'),
+ ),
+ '#default_value' => isset($this->options['date_format']) ? $this->options['date_format'] : 'small',
+ );
+ $form['custom_date_format'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Custom date format'),
+ '#description' => t('If "Custom", see <a href="http://us.php.net/manual/en/function.date.php" target="_blank">the PHP docs</a> for date formats. Otherwise, enter the number of different time units to display, which defaults to 2.'),
+ '#default_value' => isset($this->options['custom_date_format']) ? $this->options['custom_date_format'] : '',
+ );
+ // Setup #states for all possible date_formats on the custom_date_format form element.
+ foreach (array('custom', 'raw time ago', 'time ago', 'raw time hence', 'time hence', 'raw time span', 'time span', 'raw time span', 'inverse time span', 'time span') as $custom_date_possible) {
+ $form['custom_date_format']['#states']['visible'][] = array(
+ ':input[name="options[date_format]"]' => array('value' => $custom_date_possible),
+ );
+ }
+ $form['timezone'] = array(
+ '#type' => 'select',
+ '#title' => t('Timezone'),
+ '#description' => t('Timezone to be used for date output.'),
+ '#options' => array('' => t('- Default site/user timezone -')) + system_time_zones(FALSE),
+ '#default_value' => $this->options['timezone'],
+ );
+ foreach (array_merge(array('custom'), array_keys($date_formats)) as $timezone_date_formats) {
+ $form['timezone']['#states']['visible'][] = array(
+ ':input[name="options[date_format]"]' => array('value' => $timezone_date_formats),
+ );
+ }
+
+ parent::buildOptionsForm($form, $form_state);
+ }
+
+ function render($values) {
+ $value = $this->get_value($values);
+ $format = $this->options['date_format'];
+ if (in_array($format, array('custom', 'raw time ago', 'time ago', 'raw time hence', 'time hence', 'raw time span', 'time span', 'raw time span', 'inverse time span', 'time span'))) {
+ $custom_format = $this->options['custom_date_format'];
+ }
+
+ if ($value) {
+ $timezone = !empty($this->options['timezone']) ? $this->options['timezone'] : NULL;
+ $time_diff = REQUEST_TIME - $value; // will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence)
+ switch ($format) {
+ case 'raw time ago':
+ return format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2);
+ case 'time ago':
+ return t('%time ago', array('%time' => format_interval($time_diff, is_numeric($custom_format) ? $custom_format : 2)));
+ case 'raw time hence':
+ return format_interval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2);
+ case 'time hence':
+ return t('%time hence', array('%time' => format_interval(-$time_diff, is_numeric($custom_format) ? $custom_format : 2)));
+ case 'raw time span':
+ return ($time_diff < 0 ? '-' : '') . format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2);
+ case 'inverse time span':
+ return ($time_diff > 0 ? '-' : '') . format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2);
+ case 'time span':
+ return t(($time_diff < 0 ? '%time hence' : '%time ago'), array('%time' => format_interval(abs($time_diff), is_numeric($custom_format) ? $custom_format : 2)));
+ case 'custom':
+ if ($custom_format == 'r') {
+ return format_date($value, $format, $custom_format, $timezone, 'en');
+ }
+ return format_date($value, $format, $custom_format, $timezone);
+ default:
+ return format_date($value, $format, '', $timezone);
+ }
+ }
+ }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
new file mode 100644
index 0000000..b63ed69
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php
@@ -0,0 +1,1627 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Plugin\views\field\FieldPluginBase.
+ */
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\views\Plugin\views\HandlerBase;
+use Drupal\views\ViewExecutable;
+use Drupal\Core\Annotation\Plugin;
+
+/**
+ * @defgroup views_field_handlers Views field handlers
+ * @{
+ * Handlers to tell Views how to build and display fields.
+ *
+ */
+
+/**
+ * Indicator of the render_text() method for rendering a single item.
+ * (If no render_item() is present).
+ */
+define('VIEWS_HANDLER_RENDER_TEXT_PHASE_SINGLE_ITEM', 0);
+
+/**
+ * Indicator of the render_text() method for rendering the whole element.
+ * (if no render_item() method is available).
+ */
+define('VIEWS_HANDLER_RENDER_TEXT_PHASE_COMPLETELY', 1);
+
+/**
+ * Indicator of the render_text() method for rendering the empty text.
+ */
+define('VIEWS_HANDLER_RENDER_TEXT_PHASE_EMPTY', 2);
+
+/**
+ * Base field handler that has no options and renders an unformatted field.
+ *
+ * Definition terms:
+ * - additional fields: An array of fields that should be added to the query
+ * for some purpose. The array is in the form of:
+ * array('identifier' => array('table' => tablename,
+ * 'field' => fieldname); as many fields as are necessary
+ * may be in this array.
+ * - click sortable: If TRUE, this field may be click sorted.
+ *
+ * @ingroup views_field_handlers
+ */
+abstract class FieldPluginBase extends HandlerBase {
+
+ var $field_alias = 'unknown';
+ var $aliases = array();
+
+ /**
+ * The field value prior to any rewriting.
+ *
+ * @var mixed
+ */
+ public $original_value = NULL;
+
+ /**
+ * @var array
+ * Stores additional fields which get's added to the query.
+ * The generated aliases are stored in $aliases.
+ */
+ var $additional_fields = array();
+
+ /**
+ * Overrides Drupal\views\Plugin\views\HandlerBase::init().
+ */
+ public function init(ViewExecutable $view, &$options) {
+ parent::init($view, $options);
+
+ $this->additional_fields = array();
+ if (!empty($this->definition['additional fields'])) {
+ $this->additional_fields = $this->definition['additional fields'];
+ }
+
+ if (!isset($this->options['exclude'])) {
+ $this->options['exclude'] = '';
+ }
+ }
+
+ /**
+ * Determine if this field can allow advanced rendering.
+ *
+ * Fields can set this to FALSE if they do not wish to allow
+ * token based rewriting or link-making.
+ */
+ function allow_advanced_render() {
+ return TRUE;
+ }
+
+ /**
+ * Called to add the field to a query.
+ */
+ public function query() {
+ $this->ensureMyTable();
+ // Add the field.
+ $params = $this->options['group_type'] != 'group' ? array('function' => $this->options['group_type']) : array();
+ $this->field_alias = $this->query->add_field($this->tableAlias, $this->realField, NULL, $params);
+
+ $this->add_additional_fields();
+ }
+
+ /**
+ * Add 'additional' fields to the query.
+ *
+ * @param $fields
+ * An array of fields. The key is an identifier used to later find the
+ * field alias used. The value is either a string in which case it's
+ * assumed to be a field on this handler's table; or it's an array in the
+ * form of
+ * @code array('table' => $tablename, 'field' => $fieldname) @endcode
+ */
+ function add_additional_fields($fields = NULL) {
+ if (!isset($fields)) {
+ // notice check
+ if (empty($this->additional_fields)) {
+ return;
+ }
+ $fields = $this->additional_fields;
+ }
+
+ $group_params = array();
+ if ($this->options['group_type'] != 'group') {
+ $group_params = array(
+ 'function' => $this->options['group_type'],
+ );
+ }
+
+ if (!empty($fields) && is_array($fields)) {
+ foreach ($fields as $identifier => $info) {
+ if (is_array($info)) {
+ if (isset($info['table'])) {
+ $table_alias = $this->query->ensure_table($info['table'], $this->relationship);
+ }
+ else {
+ $table_alias = $this->tableAlias;
+ }
+
+ if (empty($table_alias)) {
+ debug(t('Handler @handler tried to add additional_field @identifier but @table could not be added!', array('@handler' => $this->definition['handler'], '@identifier' => $identifier, '@table' => $info['table'])));
+ $this->aliases[$identifier] = 'broken';
+ continue;
+ }
+
+ $params = array();
+ if (!empty($info['params'])) {
+ $params = $info['params'];
+ }
+
+ $params += $group_params;
+ $this->aliases[$identifier] = $this->query->add_field($table_alias, $info['field'], NULL, $params);
+ }
+ else {
+ $this->aliases[$info] = $this->query->add_field($this->tableAlias, $info, NULL, $group_params);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called to determine what to tell the clicksorter.
+ */
+ function click_sort($order) {
+ if (isset($this->field_alias)) {
+ // Since fields should always have themselves already added, just
+ // add a sort on the field.
+ $params = $this->options['group_type'] != 'group' ? array('function' => $this->options['group_type']) : array();
+ $this->query->add_orderby(NULL, NULL, $order, $this->field_alias, $params);
+ }
+ }
+
+ /**
+ * Determine if this field is click sortable.
+ */
+ function click_sortable() {
+ return !empty($this->definition['click sortable']);
+ }
+
+ /**
+ * Get this field's label.
+ */
+ public function label() {
+ if (!isset($this->options['label'])) {
+ return '';
+ }
+ return $this->options['label'];
+ }
+
+ /**
+ * Return an HTML element based upon the field's element type.
+ */
+ function element_type($none_supported = FALSE, $default_empty = FALSE, $inline = FALSE) {
+ if ($none_supported) {
+ if ($this->options['element_type'] === '0') {
+ return '';
+ }
+ }
+ if ($this->options['element_type']) {
+ return check_plain($this->options['element_type']);
+ }
+
+ if ($default_empty) {
+ return '';
+ }
+
+ if ($inline) {
+ return 'span';
+ }
+
+ if (isset($this->definition['element type'])) {
+ return $this->definition['element type'];
+ }
+
+ return 'span';
+ }
+
+ /**
+ * Return an HTML element for the label based upon the field's element type.
+ */
+ function element_label_type($none_supported = FALSE, $default_empty = FALSE) {
+ if ($none_supported) {
+ if ($this->options['element_label_type'] === '0') {
+ return '';
+ }
+ }
+ if ($this->options['element_label_type']) {
+ return check_plain($this->options['element_label_type']);
+ }
+
+ if ($default_empty) {
+ return '';
+ }
+
+ return 'span';
+ }
+
+ /**
+ * Return an HTML element for the wrapper based upon the field's element type.
+ */
+ function element_wrapper_type($none_supported = FALSE, $default_empty = FALSE) {
+ if ($none_supported) {
+ if ($this->options['element_wrapper_type'] === '0') {
+ return 0;
+ }
+ }
+ if ($this->options['element_wrapper_type']) {
+ return check_plain($this->options['element_wrapper_type']);
+ }
+
+ if ($default_empty) {
+ return '';
+ }
+
+ return 'div';
+ }
+
+ /**
+ * Provide a list of elements valid for field HTML.
+ *
+ * This function can be overridden by fields that want more or fewer
+ * elements available, though this seems like it would be an incredibly
+ * rare occurence.
+ */
+ function get_elements() {
+ static $elements = NULL;
+ if (!isset($elements)) {
+ // @todo Add possible html5 elements.
+ $elements = array(
+ '' => t(' - Use default -'),
+ '0' => t('- None -')
+ );
+ $elements += config('views.settings')->get('field_rewrite_elements');
+ }
+
+ return $elements;
+ }
+
+ /**
+ * Return the class of the field.
+ */
+ function element_classes($row_index = NULL) {
+ $classes = explode(' ', $this->options['element_class']);
+ foreach ($classes as &$class) {
+ $class = $this->tokenize_value($class, $row_index);
+ $class = drupal_clean_css_identifier($class);
+ }
+ return implode(' ', $classes);
+ }
+
+ /**
+ * Replace a value with tokens from the last field.
+ *
+ * This function actually figures out which field was last and uses its
+ * tokens so they will all be available.
+ */
+ function tokenize_value($value, $row_index = NULL) {
+ if (strpos($value, '[') !== FALSE || strpos($value, '!') !== FALSE || strpos($value, '%') !== FALSE) {
+ $fake_item = array(
+ 'alter_text' => TRUE,
+ 'text' => $value,
+ );
+
+ // Use isset() because empty() will trigger on 0 and 0 is
+ // the first row.
+ if (isset($row_index) && isset($this->view->style_plugin->render_tokens[$row_index])) {
+ $tokens = $this->view->style_plugin->render_tokens[$row_index];
+ }
+ else {
+ // Get tokens from the last field.
+ $last_field = end($this->view->field);
+ if (isset($last_field->last_tokens)) {
+ $tokens = $last_field->last_tokens;
+ }
+ else {
+ $tokens = $last_field->get_render_tokens($fake_item);
+ }
+ }
+
+ $value = strip_tags($this->render_altered($fake_item, $tokens));
+ if (!empty($this->options['alter']['trim_whitespace'])) {
+ $value = trim($value);
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Return the class of the field's label.
+ */
+ function element_label_classes($row_index = NULL) {
+ $classes = explode(' ', $this->options['element_label_class']);
+ foreach ($classes as &$class) {
+ $class = $this->tokenize_value($class, $row_index);
+ $class = drupal_clean_css_identifier($class);
+ }
+ return implode(' ', $classes);
+ }
+
+ /**
+ * Return the class of the field's wrapper.
+ */
+ function element_wrapper_classes($row_index = NULL) {
+ $classes = explode(' ', $this->options['element_wrapper_class']);
+ foreach ($classes as &$class) {
+ $class = $this->tokenize_value($class, $row_index);
+ $class = drupal_clean_css_identifier($class);
+ }
+ return implode(' ', $classes);
+ }
+
+ /**
+ * Get the entity matching the current row and relationship.
+ *
+ * @param $values
+ * An object containing all retrieved values.
+ */
+ function get_entity($values) {
+ $relationship_id = $this->options['relationship'];
+ if ($relationship_id == 'none') {
+ return $values->_entity;
+ }
+ else {
+ return $values->_relationship_entities[$relationship_id];
+ }
+ }
+
+ /**
+ * Get the value that's supposed to be rendered.
+ *
+ * This api exists so that other modules can easy set the values of the field
+ * without having the need to change the render method as well.
+ *
+ * @param $values
+ * An object containing all retrieved values.
+ * @param $field
+ * Optional name of the field where the value is stored.
+ */
+ function get_value($values, $field = NULL) {
+ $alias = isset($field) ? $this->aliases[$field] : $this->field_alias;
+ if (isset($values->{$alias})) {
+ return $values->{$alias};
+ }
+ }
+
+ /**
+ * Determines if this field will be available as an option to group the result
+ * by in the style settings.
+ *
+ * @return bool
+ * TRUE if this field handler is groupable, otherwise FALSE.
+ */
+ function use_string_group_by() {
+ return TRUE;
+ }
+
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['label'] = array('default' => $this->definition['title'], 'translatable' => TRUE);
+ $options['exclude'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['alter'] = array(
+ 'contains' => array(
+ 'alter_text' => array('default' => FALSE, 'bool' => TRUE),
+ 'text' => array('default' => '', 'translatable' => TRUE),
+ 'make_link' => array('default' => FALSE, 'bool' => TRUE),
+ 'path' => array('default' => ''),
+ 'absolute' => array('default' => FALSE, 'bool' => TRUE),
+ 'external' => array('default' => FALSE, 'bool' => TRUE),
+ 'replace_spaces' => array('default' => FALSE, 'bool' => TRUE),
+ 'path_case' => array('default' => 'none', 'translatable' => FALSE),
+ 'trim_whitespace' => array('default' => FALSE, 'bool' => TRUE),
+ 'alt' => array('default' => '', 'translatable' => TRUE),
+ 'rel' => array('default' => ''),
+ 'link_class' => array('default' => ''),
+ 'prefix' => array('default' => '', 'translatable' => TRUE),
+ 'suffix' => array('default' => '', 'translatable' => TRUE),
+ 'target' => array('default' => '', 'translatable' => TRUE),
+ 'nl2br' => array('default' => FALSE, 'bool' => TRUE),
+ 'max_length' => array('default' => ''),
+ 'word_boundary' => array('default' => TRUE, 'bool' => TRUE),
+ 'ellipsis' => array('default' => TRUE, 'bool' => TRUE),
+ 'more_link' => array('default' => FALSE, 'bool' => TRUE),
+ 'more_link_text' => array('default' => '', 'translatable' => TRUE),
+ 'more_link_path' => array('default' => ''),
+ 'strip_tags' => array('default' => FALSE, 'bool' => TRUE),
+ 'trim' => array('default' => FALSE, 'bool' => TRUE),
+ 'preserve_tags' => array('default' => ''),
+ 'html' => array('default' => FALSE, 'bool' => TRUE),
+ ),
+ );
+ $options['element_type'] = array('default' => '');
+ $options['element_class'] = array('default' => '');
+
+ $options['element_label_type'] = array('default' => '');
+ $options['element_label_class'] = array('default' => '');
+ $options['element_label_colon'] = array('default' => TRUE, 'bool' => TRUE);
+
+ $options['element_wrapper_type'] = array('default' => '');
+ $options['element_wrapper_class'] = array('default' => '');
+
+ $options['element_default_classes'] = array('default' => TRUE, 'bool' => TRUE);
+
+ $options['empty'] = array('default' => '', 'translatable' => TRUE);
+ $options['hide_empty'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['empty_zero'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['hide_alter_empty'] = array('default' => TRUE, 'bool' => TRUE);
+
+ return $options;
+ }
+
+ /**
+ * Performs some cleanup tasks on the options array before saving it.
+ */
+ public function submitOptionsForm(&$form, &$form_state) {
+ $options = &$form_state['values']['options'];
+ $types = array('element_type', 'element_label_type', 'element_wrapper_type');
+ $classes = array_combine(array('element_class', 'element_label_class', 'element_wrapper_class'), $types);
+
+ foreach ($types as $type) {
+ if (!$options[$type . '_enable']) {
+ $options[$type] = '';
+ }
+ }
+
+ foreach ($classes as $class => $type) {
+ if (!$options[$class . '_enable'] || !$options[$type . '_enable']) {
+ $options[$class] = '';
+ }
+ }
+
+ if (empty($options['custom_label'])) {
+ $options['label'] = '';
+ $options['element_label_colon'] = FALSE;
+ }
+ }
+
+ /**
+ * Default options form that provides the label widget that all fields
+ * should have.
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $label = $this->label();
+ $form['custom_label'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Create a label'),
+ '#description' => t('Enable to create a label for this field.'),
+ '#default_value' => $label !== '',
+ '#weight' => -103,
+ );
+ $form['label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Label'),
+ '#default_value' => $label,
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[custom_label]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#weight' => -102,
+ );
+ $form['element_label_colon'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Place a colon after the label'),
+ '#default_value' => $this->options['element_label_colon'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[custom_label]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#weight' => -101,
+ );
+
+ $form['exclude'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Exclude from display'),
+ '#default_value' => $this->options['exclude'],
+ '#description' => t('Enable to load this field as hidden. Often used to group fields, or to use as token in another field.'),
+ '#weight' => -100,
+ );
+
+ $form['style_settings'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Style settings'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#weight' => 99,
+ );
+
+ $form['element_type_enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Customize field HTML'),
+ '#default_value' => !empty($this->options['element_type']) || (string) $this->options['element_type'] == '0' || !empty($this->options['element_class']) || (string) $this->options['element_class'] == '0',
+ '#fieldset' => 'style_settings',
+ );
+ $form['element_type'] = array(
+ '#title' => t('HTML element'),
+ '#options' => $this->get_elements(),
+ '#type' => 'select',
+ '#default_value' => $this->options['element_type'],
+ '#description' => t('Choose the HTML element to wrap around this field, e.g. H1, H2, etc.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[element_type_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#fieldset' => 'style_settings',
+ );
+
+ $form['element_class_enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Create a CSS class'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[element_type_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#default_value' => !empty($this->options['element_class']) || (string) $this->options['element_class'] == '0',
+ '#fieldset' => 'style_settings',
+ );
+ $form['element_class'] = array(
+ '#title' => t('CSS class'),
+ '#description' => t('You may use token substitutions from the rewriting section in this class.'),
+ '#type' => 'textfield',
+ '#default_value' => $this->options['element_class'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[element_type_enable]"]' => array('checked' => TRUE),
+ ':input[name="options[element_class_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#fieldset' => 'style_settings',
+ );
+
+ $form['element_label_type_enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Customize label HTML'),
+ '#default_value' => !empty($this->options['element_label_type']) || (string) $this->options['element_label_type'] == '0' || !empty($this->options['element_label_class']) || (string) $this->options['element_label_class'] == '0',
+ '#fieldset' => 'style_settings',
+ );
+ $form['element_label_type'] = array(
+ '#title' => t('Label HTML element'),
+ '#options' => $this->get_elements(FALSE),
+ '#type' => 'select',
+ '#default_value' => $this->options['element_label_type'],
+ '#description' => t('Choose the HTML element to wrap around this label, e.g. H1, H2, etc.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[element_label_type_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#fieldset' => 'style_settings',
+ );
+ $form['element_label_class_enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Create a CSS class'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[element_label_type_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#default_value' => !empty($this->options['element_label_class']) || (string) $this->options['element_label_class'] == '0',
+ '#fieldset' => 'style_settings',
+ );
+ $form['element_label_class'] = array(
+ '#title' => t('CSS class'),
+ '#description' => t('You may use token substitutions from the rewriting section in this class.'),
+ '#type' => 'textfield',
+ '#default_value' => $this->options['element_label_class'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[element_label_type_enable]"]' => array('checked' => TRUE),
+ ':input[name="options[element_label_class_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#fieldset' => 'style_settings',
+ );
+
+ $form['element_wrapper_type_enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Customize field and label wrapper HTML'),
+ '#default_value' => !empty($this->options['element_wrapper_type']) || (string) $this->options['element_wrapper_type'] == '0' || !empty($this->options['element_wrapper_class']) || (string) $this->options['element_wrapper_class'] == '0',
+ '#fieldset' => 'style_settings',
+ );
+ $form['element_wrapper_type'] = array(
+ '#title' => t('Wrapper HTML element'),
+ '#options' => $this->get_elements(FALSE),
+ '#type' => 'select',
+ '#default_value' => $this->options['element_wrapper_type'],
+ '#description' => t('Choose the HTML element to wrap around this field and label, e.g. H1, H2, etc. This may not be used if the field and label are not rendered together, such as with a table.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[element_wrapper_type_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#fieldset' => 'style_settings',
+ );
+
+ $form['element_wrapper_class_enable'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Create a CSS class'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[element_wrapper_type_enable]"]' => array('checked' => TRUE),
+ ),
+ ),
+ '#default_value' => !empty($this->options['element_wrapper_class']) || (string) $this->options['element_wrapper_class'] == '0',
+ '#fieldset' => 'style_settings',
+ );
+ $form['element_wrapper_class'] = array(
+ '#title' => t('CSS class'),
+ '#description' => t('You may use token substitutions from the rewriting section in this class.'),
+ '#type' => 'textfield',
+ '#default_value' => $this->options['element_wrapper_class'],
+ '#states' => array(
+ 'visible' => array(